1 /****************************************************************************
2  * Copyright (c) 2006,2007 Free Software Foundation, Inc.                   *
3  *                                                                          *
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:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
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.                               *
22  *                                                                          *
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       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  *  Author: Thomas E. Dickey                                                *
31  ****************************************************************************/
32 
33 /*
34  * Iterators for terminal databases.
35  */
36 
37 #include <curses.priv.h>
38 
39 #include <tic.h>
40 
41 MODULE_ID("$Id: db_iterator.c,v 1.6 2007/04/22 00:00:26 tom Exp $")
42 
43 #define HaveTicDirectory _nc_globals.have_tic_directory
44 #define KeepTicDirectory _nc_globals.keep_tic_directory
45 #define TicDirectory     _nc_globals.tic_directory
46 
47 /*
48  * Record the "official" location of the terminfo directory, according to
49  * the place where we're writing to, or the normal default, if not.
50  */
51 NCURSES_EXPORT(const char *)
52 _nc_tic_dir(const char *path)
53 {
54     if (!KeepTicDirectory) {
55 	if (path != 0) {
56 	    TicDirectory = path;
57 	    HaveTicDirectory = TRUE;
58 	} else if (!HaveTicDirectory && use_terminfo_vars()) {
59 	    char *envp;
60 	    if ((envp = getenv("TERMINFO")) != 0)
61 		return _nc_tic_dir(envp);
62 	}
63     }
64     return TicDirectory;
65 }
66 
67 /*
68  * Special fix to prevent the terminfo directory from being moved after tic
69  * has chdir'd to it.  If we let it be changed, then if $TERMINFO has a
70  * relative path, we'll lose track of the actual directory.
71  */
72 NCURSES_EXPORT(void)
73 _nc_keep_tic_dir(const char *path)
74 {
75     _nc_tic_dir(path);
76     KeepTicDirectory = TRUE;
77 }
78 
79 /*
80  * Process the list of :-separated directories, looking for the terminal type.
81  * We don't use strtok because it does not show us empty tokens.
82  */
83 #define ThisDbList	_nc_globals.dbi_list
84 #define ThisDbSize	_nc_globals.dbi_size
85 
86 /*
87  * Cleanup.
88  */
89 NCURSES_EXPORT(void)
90 _nc_last_db(void)
91 {
92     if (ThisDbList != 0) {
93 	FreeAndNull(ThisDbList);
94     }
95     ThisDbSize = 0;
96 }
97 
98 /* The TERMINFO_DIRS value, if defined by the configure script, begins with a
99  * ":", which will be interpreted as TERMINFO.
100  */
101 static const char *
102 next_list_item(const char *source, int *offset)
103 {
104     if (source != 0) {
105 	FreeIfNeeded(ThisDbList);
106 	ThisDbList = strdup(source);
107 	ThisDbSize = strlen(source);
108     }
109 
110     if (ThisDbList != 0 && ThisDbSize && *offset < ThisDbSize) {
111 	static char system_db[] = TERMINFO;
112 	char *result = ThisDbList + *offset;
113 	char *marker = strchr(result, NCURSES_PATHSEP);
114 
115 	/*
116 	 * Put a null on the marker if a separator was found.  Set the offset
117 	 * to the next position after the marker so we can call this function
118 	 * again, using the data at the offset.
119 	 */
120 	if (marker == 0) {
121 	    *offset += strlen(result) + 1;
122 	    marker = result + *offset;
123 	} else {
124 	    *marker++ = 0;
125 	    *offset = marker - ThisDbList;
126 	}
127 	if (*result == 0 && result != (ThisDbList + ThisDbSize))
128 	    result = system_db;
129 	return result;
130     }
131     return 0;
132 }
133 
134 #define NEXT_DBD(var, offset) next_list_item((*offset == 0) ? var : 0, offset)
135 
136 /*
137  * This is a simple iterator which allows the caller to step through the
138  * possible locations for a terminfo directory.  ncurses uses this to find
139  * terminfo files to read.
140  */
141 NCURSES_EXPORT(const char *)
142 _nc_next_db(DBDIRS * state, int *offset)
143 {
144     const char *result;
145     char *envp;
146 
147     while (*state < dbdLAST) {
148 	DBDIRS next = (DBDIRS) ((int) (*state) + 1);
149 
150 	result = 0;
151 
152 	switch (*state) {
153 	case dbdTIC:
154 	    if (HaveTicDirectory)
155 		result = _nc_tic_dir(0);
156 	    break;
157 #if USE_DATABASE
158 	case dbdEnvOnce:
159 	    if (use_terminfo_vars()) {
160 		if ((envp = getenv("TERMINFO")) != 0)
161 		    result = _nc_tic_dir(envp);
162 	    }
163 	    break;
164 	case dbdHome:
165 	    if (use_terminfo_vars()) {
166 		result = _nc_home_terminfo();
167 	    }
168 	    break;
169 	case dbdEnvList:
170 	    if (use_terminfo_vars()) {
171 		if ((result = NEXT_DBD(getenv("TERMINFO_DIRS"), offset)) != 0)
172 		    next = *state;
173 	    }
174 	    break;
175 	case dbdCfgList:
176 #ifdef TERMINFO_DIRS
177 	    if ((result = NEXT_DBD(TERMINFO_DIRS, offset)) != 0)
178 		next = *state;
179 #endif
180 	    break;
181 	case dbdCfgOnce:
182 #ifndef TERMINFO_DIRS
183 	    result = TERMINFO;
184 #endif
185 	    break;
186 #endif /* USE_DATABASE */
187 #if USE_TERMCAP
188 	case dbdEnvOnce2:
189 	    if (use_terminfo_vars()) {
190 		if ((envp = getenv("TERMCAP")) != 0)
191 		    result = _nc_tic_dir(envp);
192 	    }
193 	    break;
194 	case dbdEnvList2:
195 	    if (use_terminfo_vars()) {
196 		if ((result = NEXT_DBD(getenv("TERMPATH"), offset)) != 0)
197 		    next = *state;
198 	    }
199 	    break;
200 	case dbdCfgList2:
201 	    if ((result = NEXT_DBD(TERMPATH, offset)) != 0)
202 		next = *state;
203 	    break;
204 #endif /* USE_TERMCAP */
205 	case dbdLAST:
206 	    break;
207 	}
208 	if (*state != next) {
209 	    *state = next;
210 	    *offset = 0;
211 	    _nc_last_db();
212 	}
213 	if (result != 0) {
214 	    return result;
215 	}
216     }
217     return 0;
218 }
219 
220 NCURSES_EXPORT(void)
221 _nc_first_db(DBDIRS * state, int *offset)
222 {
223     *state = dbdTIC;
224     *offset = 0;
225 }
226