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