xref: /openbsd-src/lib/libcurses/tinfo/db_iterator.c (revision c7ef0cfc17afcba97172c25e1e3a943e893bc632)
1*c7ef0cfcSnicm /* $OpenBSD: db_iterator.c,v 1.2 2023/10/17 09:52:09 nicm Exp $ */
281d8c4e1Snicm 
381d8c4e1Snicm /****************************************************************************
4*c7ef0cfcSnicm  * Copyright 2018-2022,2023 Thomas E. Dickey                                *
5*c7ef0cfcSnicm  * Copyright 2006-2016,2017 Free Software Foundation, Inc.                  *
681d8c4e1Snicm  *                                                                          *
781d8c4e1Snicm  * Permission is hereby granted, free of charge, to any person obtaining a  *
881d8c4e1Snicm  * copy of this software and associated documentation files (the            *
981d8c4e1Snicm  * "Software"), to deal in the Software without restriction, including      *
1081d8c4e1Snicm  * without limitation the rights to use, copy, modify, merge, publish,      *
1181d8c4e1Snicm  * distribute, distribute with modifications, sublicense, and/or sell       *
1281d8c4e1Snicm  * copies of the Software, and to permit persons to whom the Software is    *
1381d8c4e1Snicm  * furnished to do so, subject to the following conditions:                 *
1481d8c4e1Snicm  *                                                                          *
1581d8c4e1Snicm  * The above copyright notice and this permission notice shall be included  *
1681d8c4e1Snicm  * in all copies or substantial portions of the Software.                   *
1781d8c4e1Snicm  *                                                                          *
1881d8c4e1Snicm  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1981d8c4e1Snicm  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
2081d8c4e1Snicm  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
2181d8c4e1Snicm  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
2281d8c4e1Snicm  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2381d8c4e1Snicm  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2481d8c4e1Snicm  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2581d8c4e1Snicm  *                                                                          *
2681d8c4e1Snicm  * Except as contained in this notice, the name(s) of the above copyright   *
2781d8c4e1Snicm  * holders shall not be used in advertising or otherwise to promote the     *
2881d8c4e1Snicm  * sale, use or other dealings in this Software without prior written       *
2981d8c4e1Snicm  * authorization.                                                           *
3081d8c4e1Snicm  ****************************************************************************/
3181d8c4e1Snicm 
3281d8c4e1Snicm /****************************************************************************
3381d8c4e1Snicm  *  Author: Thomas E. Dickey                                                *
3481d8c4e1Snicm  ****************************************************************************/
3581d8c4e1Snicm 
3681d8c4e1Snicm /*
3781d8c4e1Snicm  * Iterators for terminal databases.
3881d8c4e1Snicm  */
3981d8c4e1Snicm 
4081d8c4e1Snicm #include <curses.priv.h>
4181d8c4e1Snicm 
42*c7ef0cfcSnicm #include <time.h>
4381d8c4e1Snicm #include <tic.h>
4481d8c4e1Snicm 
45*c7ef0cfcSnicm #if USE_HASHED_DB
46*c7ef0cfcSnicm #include <hashed_db.h>
47*c7ef0cfcSnicm #endif
48*c7ef0cfcSnicm 
49*c7ef0cfcSnicm MODULE_ID("$Id: db_iterator.c,v 1.2 2023/10/17 09:52:09 nicm Exp $")
5081d8c4e1Snicm 
5181d8c4e1Snicm #define HaveTicDirectory _nc_globals.have_tic_directory
5281d8c4e1Snicm #define KeepTicDirectory _nc_globals.keep_tic_directory
5381d8c4e1Snicm #define TicDirectory     _nc_globals.tic_directory
54*c7ef0cfcSnicm #define my_blob          _nc_globals.dbd_blob
55*c7ef0cfcSnicm #define my_list          _nc_globals.dbd_list
56*c7ef0cfcSnicm #define my_size          _nc_globals.dbd_size
57*c7ef0cfcSnicm #define my_time          _nc_globals.dbd_time
58*c7ef0cfcSnicm #define my_vars          _nc_globals.dbd_vars
59*c7ef0cfcSnicm 
60*c7ef0cfcSnicm static void
add_to_blob(const char * text,size_t limit)61*c7ef0cfcSnicm add_to_blob(const char *text, size_t limit)
62*c7ef0cfcSnicm {
63*c7ef0cfcSnicm     (void) limit;
64*c7ef0cfcSnicm 
65*c7ef0cfcSnicm     if (*text != '\0') {
66*c7ef0cfcSnicm 	char *last = my_blob + strlen(my_blob);
67*c7ef0cfcSnicm 	if (last != my_blob)
68*c7ef0cfcSnicm 	    *last++ = NCURSES_PATHSEP;
69*c7ef0cfcSnicm 	_nc_STRCPY(last, text, limit);
70*c7ef0cfcSnicm     }
71*c7ef0cfcSnicm }
72*c7ef0cfcSnicm 
73*c7ef0cfcSnicm static bool
check_existence(const char * name,struct stat * sb)74*c7ef0cfcSnicm check_existence(const char *name, struct stat *sb)
75*c7ef0cfcSnicm {
76*c7ef0cfcSnicm     bool result = FALSE;
77*c7ef0cfcSnicm 
78*c7ef0cfcSnicm     if (quick_prefix(name)) {
79*c7ef0cfcSnicm 	result = TRUE;
80*c7ef0cfcSnicm     } else if (stat(name, sb) == 0
81*c7ef0cfcSnicm 	       && (S_ISDIR(sb->st_mode)
82*c7ef0cfcSnicm 		   || (S_ISREG(sb->st_mode) && sb->st_size))) {
83*c7ef0cfcSnicm 	result = TRUE;
84*c7ef0cfcSnicm     }
85*c7ef0cfcSnicm #if USE_HASHED_DB
86*c7ef0cfcSnicm     else if (strlen(name) < PATH_MAX - sizeof(DBM_SUFFIX)) {
87*c7ef0cfcSnicm 	char temp[PATH_MAX];
88*c7ef0cfcSnicm 	_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) "%s%s", name, DBM_SUFFIX);
89*c7ef0cfcSnicm 	if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode) && sb->st_size) {
90*c7ef0cfcSnicm 	    result = TRUE;
91*c7ef0cfcSnicm 	}
92*c7ef0cfcSnicm     }
93*c7ef0cfcSnicm #endif
94*c7ef0cfcSnicm     return result;
95*c7ef0cfcSnicm }
96*c7ef0cfcSnicm 
97*c7ef0cfcSnicm /*
98*c7ef0cfcSnicm  * Trim newlines (and backslashes preceding those) and tab characters to
99*c7ef0cfcSnicm  * help simplify scripting of the quick-dump feature.  Leave spaces and
100*c7ef0cfcSnicm  * other backslashes alone.
101*c7ef0cfcSnicm  */
102*c7ef0cfcSnicm static void
trim_formatting(char * source)103*c7ef0cfcSnicm trim_formatting(char *source)
104*c7ef0cfcSnicm {
105*c7ef0cfcSnicm     char *target = source;
106*c7ef0cfcSnicm     char ch;
107*c7ef0cfcSnicm 
108*c7ef0cfcSnicm     while ((ch = *source++) != '\0') {
109*c7ef0cfcSnicm 	if (ch == '\\' && *source == '\n')
110*c7ef0cfcSnicm 	    continue;
111*c7ef0cfcSnicm 	if (ch == '\n' || ch == '\t')
112*c7ef0cfcSnicm 	    continue;
113*c7ef0cfcSnicm 	*target++ = ch;
114*c7ef0cfcSnicm     }
115*c7ef0cfcSnicm     *target = '\0';
116*c7ef0cfcSnicm }
117*c7ef0cfcSnicm 
118*c7ef0cfcSnicm /*
119*c7ef0cfcSnicm  * Store the latest value of an environment variable in my_vars[] so we can
120*c7ef0cfcSnicm  * detect if one changes, invalidating the cached search-list.
121*c7ef0cfcSnicm  */
122*c7ef0cfcSnicm static bool
update_getenv(const char * name,DBDIRS which)123*c7ef0cfcSnicm update_getenv(const char *name, DBDIRS which)
124*c7ef0cfcSnicm {
125*c7ef0cfcSnicm     bool result = FALSE;
126*c7ef0cfcSnicm 
127*c7ef0cfcSnicm     if (which < dbdLAST) {
128*c7ef0cfcSnicm 	char *value;
129*c7ef0cfcSnicm 	char *cached_value = my_vars[which].value;
130*c7ef0cfcSnicm 	bool same_value;
131*c7ef0cfcSnicm 
132*c7ef0cfcSnicm 	if ((value = getenv(name)) != 0) {
133*c7ef0cfcSnicm 	    value = strdup(value);
134*c7ef0cfcSnicm 	}
135*c7ef0cfcSnicm 	same_value = ((value == 0 && cached_value == 0) ||
136*c7ef0cfcSnicm 		      (value != 0 &&
137*c7ef0cfcSnicm 		       cached_value != 0 &&
138*c7ef0cfcSnicm 		       strcmp(value, cached_value) == 0));
139*c7ef0cfcSnicm 
140*c7ef0cfcSnicm 	/* Set variable name to enable checks in cache_expired(). */
141*c7ef0cfcSnicm 	my_vars[which].name = name;
142*c7ef0cfcSnicm 
143*c7ef0cfcSnicm 	if (!same_value) {
144*c7ef0cfcSnicm 	    FreeIfNeeded(my_vars[which].value);
145*c7ef0cfcSnicm 	    my_vars[which].value = value;
146*c7ef0cfcSnicm 	    result = TRUE;
147*c7ef0cfcSnicm 	} else {
148*c7ef0cfcSnicm 	    free(value);
149*c7ef0cfcSnicm 	}
150*c7ef0cfcSnicm     }
151*c7ef0cfcSnicm     return result;
152*c7ef0cfcSnicm }
153*c7ef0cfcSnicm 
154*c7ef0cfcSnicm #if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
155*c7ef0cfcSnicm static char *
cache_getenv(const char * name,DBDIRS which)156*c7ef0cfcSnicm cache_getenv(const char *name, DBDIRS which)
157*c7ef0cfcSnicm {
158*c7ef0cfcSnicm     char *result = 0;
159*c7ef0cfcSnicm 
160*c7ef0cfcSnicm     (void) update_getenv(name, which);
161*c7ef0cfcSnicm     if (which < dbdLAST) {
162*c7ef0cfcSnicm 	result = my_vars[which].value;
163*c7ef0cfcSnicm     }
164*c7ef0cfcSnicm     return result;
165*c7ef0cfcSnicm }
166*c7ef0cfcSnicm #endif
167*c7ef0cfcSnicm 
168*c7ef0cfcSnicm /*
169*c7ef0cfcSnicm  * The cache expires if at least a second has passed since the initial lookup,
170*c7ef0cfcSnicm  * or if one of the environment variables changed.
171*c7ef0cfcSnicm  *
172*c7ef0cfcSnicm  * Only a few applications use multiple lookups of terminal entries, seems that
173*c7ef0cfcSnicm  * aside from bulk I/O such as tic and toe, that leaves interactive programs
174*c7ef0cfcSnicm  * which should not be modifying the terminal databases in a way that would
175*c7ef0cfcSnicm  * invalidate the search-list.
176*c7ef0cfcSnicm  *
177*c7ef0cfcSnicm  * The "1-second" is to allow for user-directed changes outside the program.
178*c7ef0cfcSnicm  */
179*c7ef0cfcSnicm static bool
cache_expired(void)180*c7ef0cfcSnicm cache_expired(void)
181*c7ef0cfcSnicm {
182*c7ef0cfcSnicm     bool result = FALSE;
183*c7ef0cfcSnicm     time_t now = time((time_t *) 0);
184*c7ef0cfcSnicm 
185*c7ef0cfcSnicm     if (now > my_time) {
186*c7ef0cfcSnicm 	result = TRUE;
187*c7ef0cfcSnicm     } else {
188*c7ef0cfcSnicm 	DBDIRS n;
189*c7ef0cfcSnicm 	for (n = (DBDIRS) 0; n < dbdLAST; ++n) {
190*c7ef0cfcSnicm 	    if (my_vars[n].name != 0
191*c7ef0cfcSnicm 		&& update_getenv(my_vars[n].name, n)) {
192*c7ef0cfcSnicm 		result = TRUE;
193*c7ef0cfcSnicm 		break;
194*c7ef0cfcSnicm 	    }
195*c7ef0cfcSnicm 	}
196*c7ef0cfcSnicm     }
197*c7ef0cfcSnicm     return result;
198*c7ef0cfcSnicm }
199*c7ef0cfcSnicm 
200*c7ef0cfcSnicm static void
free_cache(void)201*c7ef0cfcSnicm free_cache(void)
202*c7ef0cfcSnicm {
203*c7ef0cfcSnicm     FreeAndNull(my_blob);
204*c7ef0cfcSnicm     FreeAndNull(my_list);
205*c7ef0cfcSnicm }
206*c7ef0cfcSnicm 
207*c7ef0cfcSnicm static void
update_tic_dir(const char * update)208*c7ef0cfcSnicm update_tic_dir(const char *update)
209*c7ef0cfcSnicm {
210*c7ef0cfcSnicm     free((char *) TicDirectory);
211*c7ef0cfcSnicm     TicDirectory = update;
212*c7ef0cfcSnicm }
21381d8c4e1Snicm 
21481d8c4e1Snicm /*
21581d8c4e1Snicm  * Record the "official" location of the terminfo directory, according to
21681d8c4e1Snicm  * the place where we're writing to, or the normal default, if not.
21781d8c4e1Snicm  */
21881d8c4e1Snicm NCURSES_EXPORT(const char *)
_nc_tic_dir(const char * path)21981d8c4e1Snicm _nc_tic_dir(const char *path)
22081d8c4e1Snicm {
221*c7ef0cfcSnicm     T(("_nc_tic_dir %s", NonNull(path)));
22281d8c4e1Snicm     if (!KeepTicDirectory) {
223*c7ef0cfcSnicm 	if (path != NULL) {
224*c7ef0cfcSnicm 	    if (path != TicDirectory)
225*c7ef0cfcSnicm 		update_tic_dir(strdup(path));
22681d8c4e1Snicm 	    HaveTicDirectory = TRUE;
227*c7ef0cfcSnicm 	} else if (HaveTicDirectory == 0) {
228*c7ef0cfcSnicm 	    if (use_terminfo_vars()) {
229*c7ef0cfcSnicm 		const char *envp;
23081d8c4e1Snicm 		if ((envp = getenv("TERMINFO")) != 0)
23181d8c4e1Snicm 		    return _nc_tic_dir(envp);
23281d8c4e1Snicm 	    }
23381d8c4e1Snicm 	}
234*c7ef0cfcSnicm     }
235*c7ef0cfcSnicm     return TicDirectory ? TicDirectory : TERMINFO;
23681d8c4e1Snicm }
23781d8c4e1Snicm 
23881d8c4e1Snicm /*
23981d8c4e1Snicm  * Special fix to prevent the terminfo directory from being moved after tic
24081d8c4e1Snicm  * has chdir'd to it.  If we let it be changed, then if $TERMINFO has a
24181d8c4e1Snicm  * relative path, we'll lose track of the actual directory.
24281d8c4e1Snicm  */
24381d8c4e1Snicm NCURSES_EXPORT(void)
_nc_keep_tic_dir(const char * path)24481d8c4e1Snicm _nc_keep_tic_dir(const char *path)
24581d8c4e1Snicm {
24681d8c4e1Snicm     _nc_tic_dir(path);
24781d8c4e1Snicm     KeepTicDirectory = TRUE;
24881d8c4e1Snicm }
24981d8c4e1Snicm 
25081d8c4e1Snicm /*
25181d8c4e1Snicm  * Cleanup.
25281d8c4e1Snicm  */
25381d8c4e1Snicm NCURSES_EXPORT(void)
_nc_last_db(void)25481d8c4e1Snicm _nc_last_db(void)
25581d8c4e1Snicm {
256*c7ef0cfcSnicm     if (my_blob != 0 && cache_expired()) {
257*c7ef0cfcSnicm 	free_cache();
25881d8c4e1Snicm     }
25981d8c4e1Snicm }
26081d8c4e1Snicm 
26181d8c4e1Snicm /*
26281d8c4e1Snicm  * This is a simple iterator which allows the caller to step through the
26381d8c4e1Snicm  * possible locations for a terminfo directory.  ncurses uses this to find
26481d8c4e1Snicm  * terminfo files to read.
26581d8c4e1Snicm  */
26681d8c4e1Snicm NCURSES_EXPORT(const char *)
_nc_next_db(DBDIRS * state,int * offset)26781d8c4e1Snicm _nc_next_db(DBDIRS * state, int *offset)
26881d8c4e1Snicm {
26981d8c4e1Snicm     const char *result;
27081d8c4e1Snicm 
271*c7ef0cfcSnicm     (void) offset;
272*c7ef0cfcSnicm     if ((int) *state < my_size
273*c7ef0cfcSnicm 	&& my_list != 0
274*c7ef0cfcSnicm 	&& my_list[*state] != 0) {
275*c7ef0cfcSnicm 	result = my_list[*state];
276*c7ef0cfcSnicm 	(*state)++;
277*c7ef0cfcSnicm     } else {
27881d8c4e1Snicm 	result = 0;
27981d8c4e1Snicm     }
28081d8c4e1Snicm     if (result != 0) {
281*c7ef0cfcSnicm 	T(("_nc_next_db %d %s", *state, result));
282*c7ef0cfcSnicm     }
28381d8c4e1Snicm     return result;
28481d8c4e1Snicm }
28581d8c4e1Snicm 
28681d8c4e1Snicm NCURSES_EXPORT(void)
_nc_first_db(DBDIRS * state,int * offset)28781d8c4e1Snicm _nc_first_db(DBDIRS * state, int *offset)
28881d8c4e1Snicm {
289*c7ef0cfcSnicm     bool cache_has_expired = FALSE;
29081d8c4e1Snicm     *state = dbdTIC;
29181d8c4e1Snicm     *offset = 0;
292*c7ef0cfcSnicm 
293*c7ef0cfcSnicm     T((T_CALLED("_nc_first_db")));
294*c7ef0cfcSnicm 
295*c7ef0cfcSnicm     /* build a blob containing all of the strings we will use for a lookup
296*c7ef0cfcSnicm      * table.
297*c7ef0cfcSnicm      */
298*c7ef0cfcSnicm     if (my_blob == 0 || (cache_has_expired = cache_expired())) {
299*c7ef0cfcSnicm 	size_t blobsize = 0;
300*c7ef0cfcSnicm 	const char *values[dbdLAST];
301*c7ef0cfcSnicm 	struct stat *my_stat;
302*c7ef0cfcSnicm 	int j;
303*c7ef0cfcSnicm 
304*c7ef0cfcSnicm 	if (cache_has_expired)
305*c7ef0cfcSnicm 	    free_cache();
306*c7ef0cfcSnicm 
307*c7ef0cfcSnicm 	for (j = 0; j < dbdLAST; ++j)
308*c7ef0cfcSnicm 	    values[j] = 0;
309*c7ef0cfcSnicm 
310*c7ef0cfcSnicm 	/*
311*c7ef0cfcSnicm 	 * This is the first item in the list, and is used only when tic is
312*c7ef0cfcSnicm 	 * writing to the database, as a performance improvement.
313*c7ef0cfcSnicm 	 */
314*c7ef0cfcSnicm 	values[dbdTIC] = TicDirectory;
315*c7ef0cfcSnicm 
316*c7ef0cfcSnicm #if NCURSES_USE_DATABASE
317*c7ef0cfcSnicm #ifdef TERMINFO_DIRS
318*c7ef0cfcSnicm 	values[dbdCfgList] = TERMINFO_DIRS;
319*c7ef0cfcSnicm #endif
320*c7ef0cfcSnicm #ifdef TERMINFO
321*c7ef0cfcSnicm 	values[dbdCfgOnce] = TERMINFO;
322*c7ef0cfcSnicm #endif
323*c7ef0cfcSnicm #endif
324*c7ef0cfcSnicm 
325*c7ef0cfcSnicm #if NCURSES_USE_TERMCAP
326*c7ef0cfcSnicm 	values[dbdCfgList2] = TERMPATH;
327*c7ef0cfcSnicm #endif
328*c7ef0cfcSnicm 
329*c7ef0cfcSnicm 	if (use_terminfo_vars()) {
330*c7ef0cfcSnicm #if NCURSES_USE_DATABASE
331*c7ef0cfcSnicm 	    values[dbdEnvOnce] = cache_getenv("TERMINFO", dbdEnvOnce);
332*c7ef0cfcSnicm 	    values[dbdHome] = _nc_home_terminfo();
333*c7ef0cfcSnicm 	    (void) cache_getenv("HOME", dbdHome);
334*c7ef0cfcSnicm 	    values[dbdEnvList] = cache_getenv("TERMINFO_DIRS", dbdEnvList);
335*c7ef0cfcSnicm 
336*c7ef0cfcSnicm #endif
337*c7ef0cfcSnicm #if NCURSES_USE_TERMCAP
338*c7ef0cfcSnicm 	    values[dbdEnvOnce2] = cache_getenv("TERMCAP", dbdEnvOnce2);
339*c7ef0cfcSnicm 	    /* only use $TERMCAP if it is an absolute path */
340*c7ef0cfcSnicm 	    if (values[dbdEnvOnce2] != 0
341*c7ef0cfcSnicm 		&& *values[dbdEnvOnce2] != '/') {
342*c7ef0cfcSnicm 		values[dbdEnvOnce2] = 0;
34381d8c4e1Snicm 	    }
344*c7ef0cfcSnicm 	    values[dbdEnvList2] = cache_getenv("TERMPATH", dbdEnvList2);
345*c7ef0cfcSnicm #endif /* NCURSES_USE_TERMCAP */
346*c7ef0cfcSnicm 	}
347*c7ef0cfcSnicm 
348*c7ef0cfcSnicm 	for (j = 0; j < dbdLAST; ++j) {
349*c7ef0cfcSnicm 	    if (values[j] == 0)
350*c7ef0cfcSnicm 		values[j] = "";
351*c7ef0cfcSnicm 	    blobsize += 2 + strlen(values[j]);
352*c7ef0cfcSnicm 	}
353*c7ef0cfcSnicm 
354*c7ef0cfcSnicm 	my_blob = malloc(blobsize);
355*c7ef0cfcSnicm 	if (my_blob != 0) {
356*c7ef0cfcSnicm 	    *my_blob = '\0';
357*c7ef0cfcSnicm 	    for (j = 0; j < dbdLAST; ++j) {
358*c7ef0cfcSnicm 		add_to_blob(values[j], blobsize);
359*c7ef0cfcSnicm 	    }
360*c7ef0cfcSnicm 
361*c7ef0cfcSnicm 	    /* Now, build an array which will be pointers to the distinct
362*c7ef0cfcSnicm 	     * strings in the blob.
363*c7ef0cfcSnicm 	     */
364*c7ef0cfcSnicm 	    blobsize = 2;
365*c7ef0cfcSnicm 	    for (j = 0; my_blob[j] != '\0'; ++j) {
366*c7ef0cfcSnicm 		if (my_blob[j] == NCURSES_PATHSEP)
367*c7ef0cfcSnicm 		    ++blobsize;
368*c7ef0cfcSnicm 	    }
369*c7ef0cfcSnicm 	    my_list = typeCalloc(char *, blobsize);
370*c7ef0cfcSnicm 	    my_stat = typeCalloc(struct stat, blobsize);
371*c7ef0cfcSnicm 	    if (my_list != 0 && my_stat != 0) {
372*c7ef0cfcSnicm 		int k = 0;
373*c7ef0cfcSnicm 		my_list[k++] = my_blob;
374*c7ef0cfcSnicm 		for (j = 0; my_blob[j] != '\0'; ++j) {
375*c7ef0cfcSnicm 		    if (my_blob[j] == NCURSES_PATHSEP
376*c7ef0cfcSnicm 			&& ((&my_blob[j] - my_list[k - 1]) != 3
377*c7ef0cfcSnicm 			    || !quick_prefix(my_list[k - 1]))) {
378*c7ef0cfcSnicm 			my_blob[j] = '\0';
379*c7ef0cfcSnicm 			my_list[k++] = &my_blob[j + 1];
380*c7ef0cfcSnicm 		    }
381*c7ef0cfcSnicm 		}
382*c7ef0cfcSnicm 
383*c7ef0cfcSnicm 		/*
384*c7ef0cfcSnicm 		 * Eliminate duplicates from the list.
385*c7ef0cfcSnicm 		 */
386*c7ef0cfcSnicm 		for (j = 0; my_list[j] != 0; ++j) {
387*c7ef0cfcSnicm #ifdef TERMINFO
388*c7ef0cfcSnicm 		    if (*my_list[j] == '\0') {
389*c7ef0cfcSnicm 			char *my_copy = strdup(TERMINFO);
390*c7ef0cfcSnicm 			if (my_copy != 0)
391*c7ef0cfcSnicm 			    my_list[j] = my_copy;
392*c7ef0cfcSnicm 		    }
393*c7ef0cfcSnicm #endif
394*c7ef0cfcSnicm 		    trim_formatting(my_list[j]);
395*c7ef0cfcSnicm 		    for (k = 0; k < j; ++k) {
396*c7ef0cfcSnicm 			if (!strcmp(my_list[j], my_list[k])) {
397*c7ef0cfcSnicm 			    T(("duplicate %s", my_list[j]));
398*c7ef0cfcSnicm 			    k = j - 1;
399*c7ef0cfcSnicm 			    while ((my_list[j] = my_list[j + 1]) != 0) {
400*c7ef0cfcSnicm 				++j;
401*c7ef0cfcSnicm 			    }
402*c7ef0cfcSnicm 			    j = k;
403*c7ef0cfcSnicm 			    break;
404*c7ef0cfcSnicm 			}
405*c7ef0cfcSnicm 		    }
406*c7ef0cfcSnicm 		}
407*c7ef0cfcSnicm 
408*c7ef0cfcSnicm 		/*
409*c7ef0cfcSnicm 		 * Eliminate non-existent databases, and those that happen to
410*c7ef0cfcSnicm 		 * be symlinked to another location.
411*c7ef0cfcSnicm 		 */
412*c7ef0cfcSnicm 		for (j = 0; my_list[j] != 0; ++j) {
413*c7ef0cfcSnicm 		    bool found = check_existence(my_list[j], &my_stat[j]);
414*c7ef0cfcSnicm #if HAVE_LINK
415*c7ef0cfcSnicm 		    if (found) {
416*c7ef0cfcSnicm 			for (k = 0; k < j; ++k) {
417*c7ef0cfcSnicm 			    if (my_stat[j].st_dev == my_stat[k].st_dev
418*c7ef0cfcSnicm 				&& my_stat[j].st_ino == my_stat[k].st_ino) {
419*c7ef0cfcSnicm 				found = FALSE;
420*c7ef0cfcSnicm 				break;
421*c7ef0cfcSnicm 			    }
422*c7ef0cfcSnicm 			}
423*c7ef0cfcSnicm 		    }
424*c7ef0cfcSnicm #endif
425*c7ef0cfcSnicm 		    if (!found) {
426*c7ef0cfcSnicm 			T(("not found %s", my_list[j]));
427*c7ef0cfcSnicm 			k = j;
428*c7ef0cfcSnicm 			while ((my_list[k] = my_list[k + 1]) != 0) {
429*c7ef0cfcSnicm 			    ++k;
430*c7ef0cfcSnicm 			}
431*c7ef0cfcSnicm 			--j;
432*c7ef0cfcSnicm 		    }
433*c7ef0cfcSnicm 		}
434*c7ef0cfcSnicm 		my_size = j;
435*c7ef0cfcSnicm 		my_time = time((time_t *) 0);
436*c7ef0cfcSnicm 	    } else {
437*c7ef0cfcSnicm 		FreeAndNull(my_blob);
438*c7ef0cfcSnicm 	    }
439*c7ef0cfcSnicm 	    free(my_stat);
440*c7ef0cfcSnicm 	}
441*c7ef0cfcSnicm     }
442*c7ef0cfcSnicm     returnVoid;
443*c7ef0cfcSnicm }
444*c7ef0cfcSnicm 
445*c7ef0cfcSnicm #if NO_LEAKS
446*c7ef0cfcSnicm void
_nc_db_iterator_leaks(void)447*c7ef0cfcSnicm _nc_db_iterator_leaks(void)
448*c7ef0cfcSnicm {
449*c7ef0cfcSnicm     DBDIRS which;
450*c7ef0cfcSnicm 
451*c7ef0cfcSnicm     if (my_blob != 0)
452*c7ef0cfcSnicm 	FreeAndNull(my_blob);
453*c7ef0cfcSnicm     if (my_list != 0)
454*c7ef0cfcSnicm 	FreeAndNull(my_list);
455*c7ef0cfcSnicm     for (which = 0; (int) which < dbdLAST; ++which) {
456*c7ef0cfcSnicm 	my_vars[which].name = 0;
457*c7ef0cfcSnicm 	FreeIfNeeded(my_vars[which].value);
458*c7ef0cfcSnicm 	my_vars[which].value = 0;
459*c7ef0cfcSnicm     }
460*c7ef0cfcSnicm     update_tic_dir(NULL);
461*c7ef0cfcSnicm }
462*c7ef0cfcSnicm #endif
463