xref: /freebsd/contrib/ncurses/progs/toe.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /****************************************************************************
2  * Copyright (c) 1998-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: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey                        1996-on                 *
33  ****************************************************************************/
34 
35 /*
36  *	toe.c --- table of entries report generator
37  */
38 
39 #include <progs.priv.h>
40 
41 #include <sys/stat.h>
42 
43 #if USE_HASHED_DB
44 #include <hashed_db.h>
45 #endif
46 
47 MODULE_ID("$Id: toe.c,v 1.46 2007/10/13 22:26:38 tom Exp $")
48 
49 #define isDotname(name) (!strcmp(name, ".") || !strcmp(name, ".."))
50 
51 const char *_nc_progname;
52 
53 #if NO_LEAKS
54 #undef ExitProgram
55 static void ExitProgram(int code) GCC_NORETURN;
56 static void
57 ExitProgram(int code)
58 {
59     _nc_free_entries(_nc_head);
60     _nc_free_tic(code);
61 }
62 #endif
63 
64 #if USE_HASHED_DB
65 static bool
66 make_db_name(char *dst, const char *src, unsigned limit)
67 {
68     static const char suffix[] = DBM_SUFFIX;
69 
70     bool result = FALSE;
71     unsigned lens = sizeof(suffix) - 1;
72     unsigned size = strlen(src);
73     unsigned need = lens + size;
74 
75     if (need <= limit) {
76 	if (size >= lens
77 	    && !strcmp(src + size - lens, suffix))
78 	    (void) strcpy(dst, src);
79 	else
80 	    (void) sprintf(dst, "%s%s", src, suffix);
81 	result = TRUE;
82     }
83     return result;
84 }
85 #endif
86 
87 static bool
88 is_database(const char *path)
89 {
90     bool result = FALSE;
91 #if USE_DATABASE
92     if (_nc_is_dir_path(path) && access(path, R_OK | X_OK) == 0) {
93 	result = TRUE;
94     }
95 #endif
96 #if USE_TERMCAP
97     if (_nc_is_file_path(path) && access(path, R_OK) == 0) {
98 	result = TRUE;
99     }
100 #endif
101 #if USE_HASHED_DB
102     if (!result) {
103 	char filename[PATH_MAX];
104 	if (_nc_is_file_path(path) && access(path, R_OK) == 0) {
105 	    result = TRUE;
106 	} else if (make_db_name(filename, path, sizeof(filename))) {
107 	    if (_nc_is_file_path(filename) && access(filename, R_OK) == 0) {
108 		result = TRUE;
109 	    }
110 	}
111     }
112 #endif
113     return result;
114 }
115 
116 static void
117 deschook(const char *cn, TERMTYPE *tp)
118 /* display a description for the type */
119 {
120     const char *desc;
121 
122     if ((desc = strrchr(tp->term_names, '|')) == 0 || *++desc == '\0')
123 	desc = "(No description)";
124 
125     (void) printf("%-10s\t%s\n", cn, desc);
126 }
127 
128 #if USE_TERMCAP
129 static void
130 show_termcap(char *buffer,
131 	     void (*hook) (const char *, TERMTYPE *tp))
132 {
133     TERMTYPE data;
134     char *next = strchr(buffer, ':');
135     char *last;
136     char *list = buffer;
137 
138     if (next)
139 	*next = '\0';
140 
141     last = strrchr(buffer, '|');
142     if (last)
143 	++last;
144 
145     data.term_names = strdup(buffer);
146     while ((next = strtok(list, "|")) != 0) {
147 	if (next != last)
148 	    hook(next, &data);
149 	list = 0;
150     }
151     free(data.term_names);
152 }
153 #endif
154 
155 static int
156 typelist(int eargc, char *eargv[],
157 	 bool verbosity,
158 	 void (*hook) (const char *, TERMTYPE *tp))
159 /* apply a function to each entry in given terminfo directories */
160 {
161     int i;
162 
163     for (i = 0; i < eargc; i++) {
164 #if USE_DATABASE
165 	if (_nc_is_dir_path(eargv[i])) {
166 	    DIR *termdir;
167 	    DIRENT *subdir;
168 
169 	    if ((termdir = opendir(eargv[i])) == 0) {
170 		(void) fflush(stdout);
171 		(void) fprintf(stderr,
172 			       "%s: can't open terminfo directory %s\n",
173 			       _nc_progname, eargv[i]);
174 		return (EXIT_FAILURE);
175 	    } else if (verbosity)
176 		(void) printf("#\n#%s:\n#\n", eargv[i]);
177 
178 	    while ((subdir = readdir(termdir)) != 0) {
179 		size_t len = NAMLEN(subdir);
180 		char buf[PATH_MAX];
181 		char name_1[PATH_MAX];
182 		DIR *entrydir;
183 		DIRENT *entry;
184 
185 		strncpy(name_1, subdir->d_name, len)[len] = '\0';
186 		if (isDotname(name_1))
187 		    continue;
188 
189 		(void) sprintf(buf, "%s/%s/", eargv[i], name_1);
190 		if (chdir(buf) != 0)
191 		    continue;
192 
193 		entrydir = opendir(".");
194 		while ((entry = readdir(entrydir)) != 0) {
195 		    char name_2[PATH_MAX];
196 		    TERMTYPE lterm;
197 		    char *cn;
198 		    int status;
199 
200 		    len = NAMLEN(entry);
201 		    strncpy(name_2, entry->d_name, len)[len] = '\0';
202 		    if (isDotname(name_2) || !_nc_is_file_path(name_2))
203 			continue;
204 
205 		    status = _nc_read_file_entry(name_2, &lterm);
206 		    if (status <= 0) {
207 			(void) fflush(stdout);
208 			(void) fprintf(stderr,
209 				       "%s: couldn't open terminfo file %s.\n",
210 				       _nc_progname, name_2);
211 			return (EXIT_FAILURE);
212 		    }
213 
214 		    /* only visit things once, by primary name */
215 		    cn = _nc_first_name(lterm.term_names);
216 		    if (!strcmp(cn, name_2)) {
217 			/* apply the selected hook function */
218 			(*hook) (cn, &lterm);
219 		    }
220 		    _nc_free_termtype(&lterm);
221 		}
222 		closedir(entrydir);
223 	    }
224 	    closedir(termdir);
225 	}
226 #if USE_HASHED_DB
227 	else {
228 	    DB *capdbp;
229 	    char filename[PATH_MAX];
230 
231 	    if (make_db_name(filename, eargv[i], sizeof(filename))) {
232 		if ((capdbp = _nc_db_open(filename, FALSE)) != 0) {
233 		    DBT key, data;
234 		    int code;
235 
236 		    code = _nc_db_first(capdbp, &key, &data);
237 		    while (code == 0) {
238 			TERMTYPE lterm;
239 			int used;
240 			char *have;
241 			char *cn;
242 
243 			if (_nc_db_have_data(&key, &data, &have, &used)) {
244 			    if (_nc_read_termtype(&lterm, have, used) > 0) {
245 				/* only visit things once, by primary name */
246 				cn = _nc_first_name(lterm.term_names);
247 				/* apply the selected hook function */
248 				(*hook) (cn, &lterm);
249 				_nc_free_termtype(&lterm);
250 			    }
251 			}
252 			code = _nc_db_next(capdbp, &key, &data);
253 		    }
254 
255 		    _nc_db_close(capdbp);
256 		}
257 	    }
258 	}
259 #endif
260 #endif
261 #if USE_TERMCAP
262 #if HAVE_BSD_CGETENT
263 	char *db_array[2];
264 	char *buffer = 0;
265 
266 	if (verbosity)
267 	    (void) printf("#\n#%s:\n#\n", eargv[i]);
268 
269 	db_array[0] = eargv[i];
270 	db_array[1] = 0;
271 
272 	if (cgetfirst(&buffer, db_array)) {
273 	    show_termcap(buffer, hook);
274 	    free(buffer);
275 	    while (cgetnext(&buffer, db_array)) {
276 		show_termcap(buffer, hook);
277 		free(buffer);
278 	    }
279 	}
280 	cgetclose();
281 #else
282 	/* scan termcap text-file only */
283 	if (_nc_is_file_path(eargv[i])) {
284 	    char buffer[2048];
285 	    FILE *fp;
286 
287 	    if ((fp = fopen(eargv[i], "r")) != 0) {
288 		while (fgets(buffer, sizeof(buffer), fp) != 0) {
289 		    if (*buffer == '#')
290 			continue;
291 		    if (isspace(*buffer))
292 			continue;
293 		    show_termcap(buffer, hook);
294 		}
295 		fclose(fp);
296 	    }
297 	}
298 #endif
299 #endif
300     }
301 
302     return (EXIT_SUCCESS);
303 }
304 
305 static void
306 usage(void)
307 {
308     (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname);
309     ExitProgram(EXIT_FAILURE);
310 }
311 
312 int
313 main(int argc, char *argv[])
314 {
315     bool all_dirs = FALSE;
316     bool direct_dependencies = FALSE;
317     bool invert_dependencies = FALSE;
318     bool header = FALSE;
319     int i;
320     int code;
321     int this_opt, last_opt = '?';
322     int v_opt = 0;
323 
324     _nc_progname = _nc_rootname(argv[0]);
325 
326     while ((this_opt = getopt(argc, argv, "0123456789ahuvUV")) != -1) {
327 	/* handle optional parameter */
328 	if (isdigit(this_opt)) {
329 	    switch (last_opt) {
330 	    case 'v':
331 		v_opt = (this_opt - '0');
332 		break;
333 	    default:
334 		if (isdigit(last_opt))
335 		    v_opt *= 10;
336 		else
337 		    v_opt = 0;
338 		v_opt += (this_opt - '0');
339 		last_opt = this_opt;
340 	    }
341 	    continue;
342 	}
343 	switch (this_opt) {
344 	case 'a':
345 	    all_dirs = TRUE;
346 	    break;
347 	case 'h':
348 	    header = TRUE;
349 	    break;
350 	case 'u':
351 	    direct_dependencies = TRUE;
352 	    break;
353 	case 'v':
354 	    v_opt = 1;
355 	    break;
356 	case 'U':
357 	    invert_dependencies = TRUE;
358 	    break;
359 	case 'V':
360 	    puts(curses_version());
361 	    ExitProgram(EXIT_SUCCESS);
362 	default:
363 	    usage();
364 	}
365     }
366     set_trace_level(v_opt);
367 
368     if (direct_dependencies || invert_dependencies) {
369 	if (freopen(argv[optind], "r", stdin) == 0) {
370 	    (void) fflush(stdout);
371 	    fprintf(stderr, "%s: can't open %s\n", _nc_progname, argv[optind]);
372 	    ExitProgram(EXIT_FAILURE);
373 	}
374 
375 	/* parse entries out of the source file */
376 	_nc_set_source(argv[optind]);
377 	_nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK);
378     }
379 
380     /* maybe we want a direct-dependency listing? */
381     if (direct_dependencies) {
382 	ENTRY *qp;
383 
384 	for_entry_list(qp) {
385 	    if (qp->nuses) {
386 		int j;
387 
388 		(void) printf("%s:", _nc_first_name(qp->tterm.term_names));
389 		for (j = 0; j < qp->nuses; j++)
390 		    (void) printf(" %s", qp->uses[j].name);
391 		putchar('\n');
392 	    }
393 	}
394 
395 	ExitProgram(EXIT_SUCCESS);
396     }
397 
398     /* maybe we want a reverse-dependency listing? */
399     if (invert_dependencies) {
400 	ENTRY *qp, *rp;
401 	int matchcount;
402 
403 	for_entry_list(qp) {
404 	    matchcount = 0;
405 	    for_entry_list(rp) {
406 		if (rp->nuses == 0)
407 		    continue;
408 
409 		for (i = 0; i < rp->nuses; i++)
410 		    if (_nc_name_match(qp->tterm.term_names,
411 				       rp->uses[i].name, "|")) {
412 			if (matchcount++ == 0)
413 			    (void) printf("%s:",
414 					  _nc_first_name(qp->tterm.term_names));
415 			(void) printf(" %s",
416 				      _nc_first_name(rp->tterm.term_names));
417 		    }
418 	    }
419 	    if (matchcount)
420 		putchar('\n');
421 	}
422 
423 	ExitProgram(EXIT_SUCCESS);
424     }
425 
426     /*
427      * If we get this far, user wants a simple terminal type listing.
428      */
429     if (optind < argc) {
430 	code = typelist(argc - optind, argv + optind, header, deschook);
431     } else if (all_dirs) {
432 	DBDIRS state;
433 	int offset;
434 	int pass;
435 	const char *path;
436 	char **eargv = 0;
437 
438 	code = EXIT_FAILURE;
439 	for (pass = 0; pass < 2; ++pass) {
440 	    unsigned count = 0;
441 
442 	    _nc_first_db(&state, &offset);
443 	    while ((path = _nc_next_db(&state, &offset)) != 0) {
444 		if (!is_database(path)) {
445 		    ;
446 		} else if (eargv != 0) {
447 		    unsigned n;
448 		    int found = FALSE;
449 
450 		    /* eliminate duplicates */
451 		    for (n = 0; n < count; ++n) {
452 			if (!strcmp(path, eargv[n])) {
453 			    found = TRUE;
454 			    break;
455 			}
456 		    }
457 		    if (!found) {
458 			eargv[count] = strdup(path);
459 			++count;
460 		    }
461 		} else {
462 		    ++count;
463 		}
464 	    }
465 	    if (!pass) {
466 		eargv = typeCalloc(char *, count + 1);
467 	    } else {
468 		code = typelist((int) count, eargv, header, deschook);
469 		while (count-- > 0)
470 		    free(eargv[count]);
471 		free(eargv);
472 	    }
473 	}
474     } else {
475 	DBDIRS state;
476 	int offset;
477 	const char *path;
478 	char *eargv[3];
479 	int count = 0;
480 
481 	_nc_first_db(&state, &offset);
482 	while ((path = _nc_next_db(&state, &offset)) != 0) {
483 	    if (is_database(path)) {
484 		eargv[count++] = strdup(path);
485 		break;
486 	    }
487 	}
488 	eargv[count] = 0;
489 
490 	code = typelist(count, eargv, header, deschook);
491 
492 	while (count-- > 0)
493 	    free(eargv[count]);
494     }
495     _nc_last_db();
496 
497     ExitProgram(code);
498 }
499