1166124Srafan/**************************************************************************** 2262685Sdelphij * Copyright (c) 2006-2012,2013 Free Software Foundation, Inc. * 3166124Srafan * * 4166124Srafan * Permission is hereby granted, free of charge, to any person obtaining a * 5166124Srafan * copy of this software and associated documentation files (the * 6166124Srafan * "Software"), to deal in the Software without restriction, including * 7166124Srafan * without limitation the rights to use, copy, modify, merge, publish, * 8166124Srafan * distribute, distribute with modifications, sublicense, and/or sell * 9166124Srafan * copies of the Software, and to permit persons to whom the Software is * 10166124Srafan * furnished to do so, subject to the following conditions: * 11166124Srafan * * 12166124Srafan * The above copyright notice and this permission notice shall be included * 13166124Srafan * in all copies or substantial portions of the Software. * 14166124Srafan * * 15166124Srafan * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16166124Srafan * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17166124Srafan * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18166124Srafan * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19166124Srafan * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20166124Srafan * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21166124Srafan * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22166124Srafan * * 23166124Srafan * Except as contained in this notice, the name(s) of the above copyright * 24166124Srafan * holders shall not be used in advertising or otherwise to promote the * 25166124Srafan * sale, use or other dealings in this Software without prior written * 26166124Srafan * authorization. * 27166124Srafan ****************************************************************************/ 28166124Srafan 29166124Srafan/**************************************************************************** 30174993Srafan * Author: Thomas E. Dickey * 31166124Srafan ****************************************************************************/ 32166124Srafan 33166124Srafan/* 34166124Srafan * Iterators for terminal databases. 35166124Srafan */ 36166124Srafan 37166124Srafan#include <curses.priv.h> 38166124Srafan 39262685Sdelphij#include <time.h> 40166124Srafan#include <tic.h> 41166124Srafan 42262685Sdelphij#if USE_HASHED_DB 43262685Sdelphij#include <hashed_db.h> 44262685Sdelphij#endif 45166124Srafan 46262685SdelphijMODULE_ID("$Id: db_iterator.c,v 1.38 2013/12/14 21:23:20 tom Exp $") 47262685Sdelphij 48174993Srafan#define HaveTicDirectory _nc_globals.have_tic_directory 49174993Srafan#define KeepTicDirectory _nc_globals.keep_tic_directory 50174993Srafan#define TicDirectory _nc_globals.tic_directory 51262685Sdelphij#define my_blob _nc_globals.dbd_blob 52262685Sdelphij#define my_list _nc_globals.dbd_list 53262685Sdelphij#define my_size _nc_globals.dbd_size 54262685Sdelphij#define my_time _nc_globals.dbd_time 55262685Sdelphij#define my_vars _nc_globals.dbd_vars 56166124Srafan 57262685Sdelphijstatic void 58262685Sdelphijadd_to_blob(const char *text, size_t limit) 59262685Sdelphij{ 60262685Sdelphij (void) limit; 61262685Sdelphij 62262685Sdelphij if (*text != '\0') { 63262685Sdelphij char *last = my_blob + strlen(my_blob); 64262685Sdelphij if (last != my_blob) 65262685Sdelphij *last++ = NCURSES_PATHSEP; 66262685Sdelphij _nc_STRCPY(last, text, limit); 67262685Sdelphij } 68262685Sdelphij} 69262685Sdelphij 70262685Sdelphijstatic bool 71262685Sdelphijcheck_existence(const char *name, struct stat *sb) 72262685Sdelphij{ 73262685Sdelphij bool result = FALSE; 74262685Sdelphij 75262685Sdelphij if (stat(name, sb) == 0 76262685Sdelphij && (S_ISDIR(sb->st_mode) || S_ISREG(sb->st_mode))) { 77262685Sdelphij result = TRUE; 78262685Sdelphij } 79262685Sdelphij#if USE_HASHED_DB 80262685Sdelphij else if (strlen(name) < PATH_MAX - sizeof(DBM_SUFFIX)) { 81262685Sdelphij char temp[PATH_MAX]; 82262685Sdelphij _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) "%s%s", name, DBM_SUFFIX); 83262685Sdelphij if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode)) { 84262685Sdelphij result = TRUE; 85262685Sdelphij } 86262685Sdelphij } 87262685Sdelphij#endif 88262685Sdelphij return result; 89262685Sdelphij} 90262685Sdelphij 91166124Srafan/* 92262685Sdelphij * Store the latest value of an environment variable in my_vars[] so we can 93262685Sdelphij * detect if one changes, invalidating the cached search-list. 94262685Sdelphij */ 95262685Sdelphijstatic bool 96262685Sdelphijupdate_getenv(const char *name, DBDIRS which) 97262685Sdelphij{ 98262685Sdelphij bool result = FALSE; 99262685Sdelphij 100262685Sdelphij if (which < dbdLAST) { 101262685Sdelphij char *value; 102262685Sdelphij 103262685Sdelphij if ((value = getenv(name)) == 0 || (value = strdup(value)) == 0) { 104262685Sdelphij ; 105262685Sdelphij } else if (my_vars[which].name == 0 || strcmp(my_vars[which].name, name)) { 106262685Sdelphij FreeIfNeeded(my_vars[which].value); 107262685Sdelphij my_vars[which].name = name; 108262685Sdelphij my_vars[which].value = value; 109262685Sdelphij result = TRUE; 110262685Sdelphij } else if ((my_vars[which].value != 0) ^ (value != 0)) { 111262685Sdelphij FreeIfNeeded(my_vars[which].value); 112262685Sdelphij my_vars[which].value = value; 113262685Sdelphij result = TRUE; 114262685Sdelphij } else if (value != 0 && strcmp(value, my_vars[which].value)) { 115262685Sdelphij FreeIfNeeded(my_vars[which].value); 116262685Sdelphij my_vars[which].value = value; 117262685Sdelphij result = TRUE; 118262685Sdelphij } else { 119262685Sdelphij free(value); 120262685Sdelphij } 121262685Sdelphij } 122262685Sdelphij return result; 123262685Sdelphij} 124262685Sdelphij 125262685Sdelphijstatic char * 126262685Sdelphijcache_getenv(const char *name, DBDIRS which) 127262685Sdelphij{ 128262685Sdelphij char *result = 0; 129262685Sdelphij 130262685Sdelphij (void) update_getenv(name, which); 131262685Sdelphij if (which < dbdLAST) { 132262685Sdelphij result = my_vars[which].value; 133262685Sdelphij } 134262685Sdelphij return result; 135262685Sdelphij} 136262685Sdelphij 137262685Sdelphij/* 138262685Sdelphij * The cache expires if at least a second has passed since the initial lookup, 139262685Sdelphij * or if one of the environment variables changed. 140262685Sdelphij * 141262685Sdelphij * Only a few applications use multiple lookups of terminal entries, seems that 142262685Sdelphij * aside from bulk I/O such as tic and toe, that leaves interactive programs 143262685Sdelphij * which should not be modifying the terminal databases in a way that would 144262685Sdelphij * invalidate the search-list. 145262685Sdelphij * 146262685Sdelphij * The "1-second" is to allow for user-directed changes outside the program. 147262685Sdelphij */ 148262685Sdelphijstatic bool 149262685Sdelphijcache_expired(void) 150262685Sdelphij{ 151262685Sdelphij bool result = FALSE; 152262685Sdelphij time_t now = time((time_t *) 0); 153262685Sdelphij 154262685Sdelphij if (now > my_time) { 155262685Sdelphij result = TRUE; 156262685Sdelphij } else { 157262685Sdelphij DBDIRS n; 158262685Sdelphij for (n = (DBDIRS) 0; n < dbdLAST; ++n) { 159262685Sdelphij if (my_vars[n].name != 0 160262685Sdelphij && update_getenv(my_vars[n].name, n)) { 161262685Sdelphij result = TRUE; 162262685Sdelphij break; 163262685Sdelphij } 164262685Sdelphij } 165262685Sdelphij } 166262685Sdelphij return result; 167262685Sdelphij} 168262685Sdelphij 169262685Sdelphijstatic void 170262685Sdelphijfree_cache(void) 171262685Sdelphij{ 172262685Sdelphij FreeAndNull(my_blob); 173262685Sdelphij FreeAndNull(my_list); 174262685Sdelphij} 175262685Sdelphij 176262685Sdelphij/* 177166124Srafan * Record the "official" location of the terminfo directory, according to 178166124Srafan * the place where we're writing to, or the normal default, if not. 179166124Srafan */ 180166124SrafanNCURSES_EXPORT(const char *) 181166124Srafan_nc_tic_dir(const char *path) 182166124Srafan{ 183262685Sdelphij T(("_nc_tic_dir %s", NonNull(path))); 184174993Srafan if (!KeepTicDirectory) { 185166124Srafan if (path != 0) { 186174993Srafan TicDirectory = path; 187174993Srafan HaveTicDirectory = TRUE; 188262685Sdelphij } else if (HaveTicDirectory == 0) { 189262685Sdelphij if (use_terminfo_vars()) { 190262685Sdelphij char *envp; 191262685Sdelphij if ((envp = getenv("TERMINFO")) != 0) 192262685Sdelphij return _nc_tic_dir(envp); 193262685Sdelphij } 194166124Srafan } 195166124Srafan } 196262685Sdelphij return TicDirectory ? TicDirectory : TERMINFO; 197166124Srafan} 198166124Srafan 199166124Srafan/* 200166124Srafan * Special fix to prevent the terminfo directory from being moved after tic 201166124Srafan * has chdir'd to it. If we let it be changed, then if $TERMINFO has a 202166124Srafan * relative path, we'll lose track of the actual directory. 203166124Srafan */ 204166124SrafanNCURSES_EXPORT(void) 205166124Srafan_nc_keep_tic_dir(const char *path) 206166124Srafan{ 207166124Srafan _nc_tic_dir(path); 208174993Srafan KeepTicDirectory = TRUE; 209166124Srafan} 210166124Srafan 211166124Srafan/* 212166124Srafan * Cleanup. 213166124Srafan */ 214166124SrafanNCURSES_EXPORT(void) 215166124Srafan_nc_last_db(void) 216166124Srafan{ 217262685Sdelphij if (my_blob != 0 && cache_expired()) { 218262685Sdelphij free_cache(); 219166124Srafan } 220166124Srafan} 221166124Srafan 222166124Srafan/* 223166124Srafan * This is a simple iterator which allows the caller to step through the 224166124Srafan * possible locations for a terminfo directory. ncurses uses this to find 225166124Srafan * terminfo files to read. 226166124Srafan */ 227166124SrafanNCURSES_EXPORT(const char *) 228166124Srafan_nc_next_db(DBDIRS * state, int *offset) 229166124Srafan{ 230166124Srafan const char *result; 231166124Srafan 232262685Sdelphij (void) offset; 233262685Sdelphij if ((int) *state < my_size 234262685Sdelphij && my_list != 0 235262685Sdelphij && my_list[*state] != 0) { 236262685Sdelphij result = my_list[*state]; 237262685Sdelphij (*state)++; 238262685Sdelphij } else { 239166124Srafan result = 0; 240262685Sdelphij } 241262685Sdelphij if (result != 0) { 242262685Sdelphij T(("_nc_next_db %d %s", *state, result)); 243262685Sdelphij } 244262685Sdelphij return result; 245262685Sdelphij} 246166124Srafan 247262685SdelphijNCURSES_EXPORT(void) 248262685Sdelphij_nc_first_db(DBDIRS * state, int *offset) 249262685Sdelphij{ 250262685Sdelphij bool cache_has_expired = FALSE; 251262685Sdelphij *state = dbdTIC; 252262685Sdelphij *offset = 0; 253262685Sdelphij 254262685Sdelphij T(("_nc_first_db")); 255262685Sdelphij 256262685Sdelphij /* build a blob containing all of the strings we will use for a lookup 257262685Sdelphij * table. 258262685Sdelphij */ 259262685Sdelphij if (my_blob == 0 || (cache_has_expired = cache_expired())) { 260262685Sdelphij size_t blobsize = 0; 261262685Sdelphij const char *values[dbdLAST]; 262262685Sdelphij struct stat *my_stat; 263262685Sdelphij int j, k; 264262685Sdelphij 265262685Sdelphij if (cache_has_expired) 266262685Sdelphij free_cache(); 267262685Sdelphij 268262685Sdelphij for (j = 0; j < dbdLAST; ++j) 269262685Sdelphij values[j] = 0; 270262685Sdelphij 271262685Sdelphij /* 272262685Sdelphij * This is the first item in the list, and is used only when tic is 273262685Sdelphij * writing to the database, as a performance improvement. 274262685Sdelphij */ 275262685Sdelphij values[dbdTIC] = TicDirectory; 276262685Sdelphij 277262685Sdelphij#if NCURSES_USE_DATABASE 278262685Sdelphij#ifdef TERMINFO_DIRS 279262685Sdelphij values[dbdCfgList] = TERMINFO_DIRS; 280262685Sdelphij#endif 281262685Sdelphij#ifdef TERMINFO 282262685Sdelphij values[dbdCfgOnce] = TERMINFO; 283262685Sdelphij#endif 284262685Sdelphij#endif 285262685Sdelphij 286262685Sdelphij#if NCURSES_USE_TERMCAP 287262685Sdelphij values[dbdCfgList2] = TERMPATH; 288262685Sdelphij#endif 289262685Sdelphij 290262685Sdelphij if (use_terminfo_vars()) { 291262685Sdelphij#if NCURSES_USE_DATABASE 292262685Sdelphij values[dbdEnvOnce] = cache_getenv("TERMINFO", dbdEnvOnce); 293262685Sdelphij values[dbdHome] = _nc_home_terminfo(); 294262685Sdelphij (void) cache_getenv("HOME", dbdHome); 295262685Sdelphij values[dbdEnvList] = cache_getenv("TERMINFO_DIRS", dbdEnvList); 296262685Sdelphij 297262685Sdelphij#endif 298262685Sdelphij#if NCURSES_USE_TERMCAP 299262685Sdelphij values[dbdEnvOnce2] = cache_getenv("TERMCAP", dbdEnvOnce2); 300262685Sdelphij /* only use $TERMCAP if it is an absolute path */ 301262685Sdelphij if (values[dbdEnvOnce2] != 0 302262685Sdelphij && *values[dbdEnvOnce2] != '/') { 303262685Sdelphij values[dbdEnvOnce2] = 0; 304166124Srafan } 305262685Sdelphij values[dbdEnvList2] = cache_getenv("TERMPATH", dbdEnvList2); 306262685Sdelphij#endif /* NCURSES_USE_TERMCAP */ 307262685Sdelphij } 308262685Sdelphij 309262685Sdelphij for (j = 0; j < dbdLAST; ++j) { 310262685Sdelphij if (values[j] == 0) 311262685Sdelphij values[j] = ""; 312262685Sdelphij blobsize += 2 + strlen(values[j]); 313262685Sdelphij } 314262685Sdelphij 315262685Sdelphij my_blob = malloc(blobsize); 316262685Sdelphij if (my_blob != 0) { 317262685Sdelphij *my_blob = '\0'; 318262685Sdelphij for (j = 0; j < dbdLAST; ++j) { 319262685Sdelphij add_to_blob(values[j], blobsize); 320166124Srafan } 321262685Sdelphij 322262685Sdelphij /* Now, build an array which will be pointers to the distinct 323262685Sdelphij * strings in the blob. 324262685Sdelphij */ 325262685Sdelphij blobsize = 2; 326262685Sdelphij for (j = 0; my_blob[j] != '\0'; ++j) { 327262685Sdelphij if (my_blob[j] == NCURSES_PATHSEP) 328262685Sdelphij ++blobsize; 329166124Srafan } 330262685Sdelphij my_list = typeCalloc(char *, blobsize); 331262685Sdelphij my_stat = typeCalloc(struct stat, blobsize); 332262685Sdelphij if (my_list != 0 && my_stat != 0) { 333262685Sdelphij k = 0; 334262685Sdelphij my_list[k++] = my_blob; 335262685Sdelphij for (j = 0; my_blob[j] != '\0'; ++j) { 336262685Sdelphij if (my_blob[j] == NCURSES_PATHSEP) { 337262685Sdelphij my_blob[j] = '\0'; 338262685Sdelphij my_list[k++] = &my_blob[j + 1]; 339262685Sdelphij } 340262685Sdelphij } 341262685Sdelphij 342262685Sdelphij /* 343262685Sdelphij * Eliminate duplicates from the list. 344262685Sdelphij */ 345262685Sdelphij for (j = 0; my_list[j] != 0; ++j) { 346262685Sdelphij#ifdef TERMINFO 347262685Sdelphij if (*my_list[j] == '\0') 348262685Sdelphij my_list[j] = strdup(TERMINFO); 349166124Srafan#endif 350262685Sdelphij for (k = 0; k < j; ++k) { 351262685Sdelphij if (!strcmp(my_list[j], my_list[k])) { 352262685Sdelphij k = j - 1; 353262685Sdelphij while ((my_list[j] = my_list[j + 1]) != 0) { 354262685Sdelphij ++j; 355262685Sdelphij } 356262685Sdelphij j = k; 357262685Sdelphij break; 358262685Sdelphij } 359262685Sdelphij } 360262685Sdelphij } 361262685Sdelphij 362262685Sdelphij /* 363262685Sdelphij * Eliminate non-existent databases, and those that happen to 364262685Sdelphij * be symlinked to another location. 365262685Sdelphij */ 366262685Sdelphij for (j = 0; my_list[j] != 0; ++j) { 367262685Sdelphij bool found = check_existence(my_list[j], &my_stat[j]); 368262685Sdelphij#if HAVE_LINK 369262685Sdelphij if (found) { 370262685Sdelphij for (k = 0; k < j; ++k) { 371262685Sdelphij if (my_stat[j].st_dev == my_stat[k].st_dev 372262685Sdelphij && my_stat[j].st_ino == my_stat[k].st_ino) { 373262685Sdelphij found = FALSE; 374262685Sdelphij break; 375262685Sdelphij } 376262685Sdelphij } 377262685Sdelphij } 378166124Srafan#endif 379262685Sdelphij if (!found) { 380262685Sdelphij k = j; 381262685Sdelphij while ((my_list[k] = my_list[k + 1]) != 0) { 382262685Sdelphij ++k; 383262685Sdelphij } 384262685Sdelphij --j; 385262685Sdelphij } 386262685Sdelphij } 387262685Sdelphij my_size = j; 388262685Sdelphij my_time = time((time_t *) 0); 389262685Sdelphij } else { 390262685Sdelphij FreeAndNull(my_blob); 391166124Srafan } 392262685Sdelphij free(my_stat); 393166124Srafan } 394166124Srafan } 395166124Srafan} 396166124Srafan 397262685Sdelphij#if NO_LEAKS 398262685Sdelphijvoid 399262685Sdelphij_nc_db_iterator_leaks(void) 400166124Srafan{ 401262685Sdelphij DBDIRS which; 402262685Sdelphij 403262685Sdelphij if (my_blob != 0) 404262685Sdelphij FreeAndNull(my_blob); 405262685Sdelphij if (my_list != 0) 406262685Sdelphij FreeAndNull(my_list); 407262685Sdelphij for (which = 0; (int) which < dbdLAST; ++which) { 408262685Sdelphij my_vars[which].name = 0; 409262685Sdelphij FreeIfNeeded(my_vars[which].value); 410262685Sdelphij my_vars[which].value = 0; 411262685Sdelphij } 412166124Srafan} 413262685Sdelphij#endif 414