xref: /freebsd/contrib/ncurses/progs/toe.c (revision 9a14aa017b21c292740c00ee098195cd46642730)
1 /****************************************************************************
2  * Copyright (c) 1998-2007,2008 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.51 2008/08/16 21:53:25 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 	    char *cwd_buf = 0;
167 	    DIR *termdir;
168 	    DIRENT *subdir;
169 
170 	    if ((termdir = opendir(eargv[i])) == 0) {
171 		(void) fflush(stdout);
172 		(void) fprintf(stderr,
173 			       "%s: can't open terminfo directory %s\n",
174 			       _nc_progname, eargv[i]);
175 		return (EXIT_FAILURE);
176 	    } else if (verbosity)
177 		(void) printf("#\n#%s:\n#\n", eargv[i]);
178 
179 	    while ((subdir = readdir(termdir)) != 0) {
180 		size_t len = NAMLEN(subdir);
181 		size_t cwd_len = len + strlen(eargv[i]) + 3;
182 		char name_1[PATH_MAX];
183 		DIR *entrydir;
184 		DIRENT *entry;
185 
186 		cwd_buf = typeRealloc(char, cwd_len, cwd_buf);
187 		if (cwd_buf == 0) {
188 		    perror("realloc cwd_buf");
189 		    continue;
190 		}
191 
192 		strncpy(name_1, subdir->d_name, len)[len] = '\0';
193 		if (isDotname(name_1))
194 		    continue;
195 
196 		(void) sprintf(cwd_buf, "%s/%.*s/", eargv[i], (int) len, name_1);
197 		if (chdir(cwd_buf) != 0)
198 		    continue;
199 
200 		entrydir = opendir(".");
201 		if (entrydir == 0) {
202 		    perror(cwd_buf);
203 		    continue;
204 		}
205 		while ((entry = readdir(entrydir)) != 0) {
206 		    char name_2[PATH_MAX];
207 		    TERMTYPE lterm;
208 		    char *cn;
209 		    int status;
210 
211 		    len = NAMLEN(entry);
212 		    strncpy(name_2, entry->d_name, len)[len] = '\0';
213 		    if (isDotname(name_2) || !_nc_is_file_path(name_2))
214 			continue;
215 
216 		    status = _nc_read_file_entry(name_2, &lterm);
217 		    if (status <= 0) {
218 			(void) fflush(stdout);
219 			(void) fprintf(stderr,
220 				       "%s: couldn't open terminfo file %s.\n",
221 				       _nc_progname, name_2);
222 			return (EXIT_FAILURE);
223 		    }
224 
225 		    /* only visit things once, by primary name */
226 		    cn = _nc_first_name(lterm.term_names);
227 		    if (!strcmp(cn, name_2)) {
228 			/* apply the selected hook function */
229 			(*hook) (cn, &lterm);
230 		    }
231 		    _nc_free_termtype(&lterm);
232 		}
233 		closedir(entrydir);
234 	    }
235 	    closedir(termdir);
236 	    if (cwd_buf != 0)
237 		free(cwd_buf);
238 	}
239 #if USE_HASHED_DB
240 	else {
241 	    DB *capdbp;
242 	    char filename[PATH_MAX];
243 
244 	    if (make_db_name(filename, eargv[i], sizeof(filename))) {
245 		if ((capdbp = _nc_db_open(filename, FALSE)) != 0) {
246 		    DBT key, data;
247 		    int code;
248 
249 		    code = _nc_db_first(capdbp, &key, &data);
250 		    while (code == 0) {
251 			TERMTYPE lterm;
252 			int used;
253 			char *have;
254 			char *cn;
255 
256 			if (_nc_db_have_data(&key, &data, &have, &used)) {
257 			    if (_nc_read_termtype(&lterm, have, used) > 0) {
258 				/* only visit things once, by primary name */
259 				cn = _nc_first_name(lterm.term_names);
260 				/* apply the selected hook function */
261 				(*hook) (cn, &lterm);
262 				_nc_free_termtype(&lterm);
263 			    }
264 			}
265 			code = _nc_db_next(capdbp, &key, &data);
266 		    }
267 
268 		    _nc_db_close(capdbp);
269 		}
270 	    }
271 	}
272 #endif
273 #endif
274 #if USE_TERMCAP
275 #if HAVE_BSD_CGETENT
276 	char *db_array[2];
277 	char *buffer = 0;
278 
279 	if (verbosity)
280 	    (void) printf("#\n#%s:\n#\n", eargv[i]);
281 
282 	db_array[0] = eargv[i];
283 	db_array[1] = 0;
284 
285 	if (cgetfirst(&buffer, db_array)) {
286 	    show_termcap(buffer, hook);
287 	    free(buffer);
288 	    while (cgetnext(&buffer, db_array)) {
289 		show_termcap(buffer, hook);
290 		free(buffer);
291 	    }
292 	}
293 	cgetclose();
294 #else
295 	/* scan termcap text-file only */
296 	if (_nc_is_file_path(eargv[i])) {
297 	    char buffer[2048];
298 	    FILE *fp;
299 
300 	    if ((fp = fopen(eargv[i], "r")) != 0) {
301 		while (fgets(buffer, sizeof(buffer), fp) != 0) {
302 		    if (*buffer == '#')
303 			continue;
304 		    if (isspace(*buffer))
305 			continue;
306 		    show_termcap(buffer, hook);
307 		}
308 		fclose(fp);
309 	    }
310 	}
311 #endif
312 #endif
313     }
314 
315     return (EXIT_SUCCESS);
316 }
317 
318 static void
319 usage(void)
320 {
321     (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname);
322     ExitProgram(EXIT_FAILURE);
323 }
324 
325 int
326 main(int argc, char *argv[])
327 {
328     bool all_dirs = FALSE;
329     bool direct_dependencies = FALSE;
330     bool invert_dependencies = FALSE;
331     bool header = FALSE;
332     char *report_file = 0;
333     unsigned i;
334     int code;
335     int this_opt, last_opt = '?';
336     int v_opt = 0;
337 
338     _nc_progname = _nc_rootname(argv[0]);
339 
340     while ((this_opt = getopt(argc, argv, "0123456789ahu:vU:V")) != -1) {
341 	/* handle optional parameter */
342 	if (isdigit(this_opt)) {
343 	    switch (last_opt) {
344 	    case 'v':
345 		v_opt = (this_opt - '0');
346 		break;
347 	    default:
348 		if (isdigit(last_opt))
349 		    v_opt *= 10;
350 		else
351 		    v_opt = 0;
352 		v_opt += (this_opt - '0');
353 		last_opt = this_opt;
354 	    }
355 	    continue;
356 	}
357 	switch (this_opt) {
358 	case 'a':
359 	    all_dirs = TRUE;
360 	    break;
361 	case 'h':
362 	    header = TRUE;
363 	    break;
364 	case 'u':
365 	    direct_dependencies = TRUE;
366 	    report_file = optarg;
367 	    break;
368 	case 'v':
369 	    v_opt = 1;
370 	    break;
371 	case 'U':
372 	    invert_dependencies = TRUE;
373 	    report_file = optarg;
374 	    break;
375 	case 'V':
376 	    puts(curses_version());
377 	    ExitProgram(EXIT_SUCCESS);
378 	default:
379 	    usage();
380 	}
381     }
382     set_trace_level(v_opt);
383 
384     if (report_file != 0) {
385 	if (freopen(report_file, "r", stdin) == 0) {
386 	    (void) fflush(stdout);
387 	    fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file);
388 	    ExitProgram(EXIT_FAILURE);
389 	}
390 
391 	/* parse entries out of the source file */
392 	_nc_set_source(report_file);
393 	_nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK);
394     }
395 
396     /* maybe we want a direct-dependency listing? */
397     if (direct_dependencies) {
398 	ENTRY *qp;
399 
400 	for_entry_list(qp) {
401 	    if (qp->nuses) {
402 		unsigned j;
403 
404 		(void) printf("%s:", _nc_first_name(qp->tterm.term_names));
405 		for (j = 0; j < qp->nuses; j++)
406 		    (void) printf(" %s", qp->uses[j].name);
407 		putchar('\n');
408 	    }
409 	}
410 
411 	ExitProgram(EXIT_SUCCESS);
412     }
413 
414     /* maybe we want a reverse-dependency listing? */
415     if (invert_dependencies) {
416 	ENTRY *qp, *rp;
417 	int matchcount;
418 
419 	for_entry_list(qp) {
420 	    matchcount = 0;
421 	    for_entry_list(rp) {
422 		if (rp->nuses == 0)
423 		    continue;
424 
425 		for (i = 0; i < rp->nuses; i++)
426 		    if (_nc_name_match(qp->tterm.term_names,
427 				       rp->uses[i].name, "|")) {
428 			if (matchcount++ == 0)
429 			    (void) printf("%s:",
430 					  _nc_first_name(qp->tterm.term_names));
431 			(void) printf(" %s",
432 				      _nc_first_name(rp->tterm.term_names));
433 		    }
434 	    }
435 	    if (matchcount)
436 		putchar('\n');
437 	}
438 
439 	ExitProgram(EXIT_SUCCESS);
440     }
441 
442     /*
443      * If we get this far, user wants a simple terminal type listing.
444      */
445     if (optind < argc) {
446 	code = typelist(argc - optind, argv + optind, header, deschook);
447     } else if (all_dirs) {
448 	DBDIRS state;
449 	int offset;
450 	int pass;
451 	const char *path;
452 	char **eargv = 0;
453 
454 	code = EXIT_FAILURE;
455 	for (pass = 0; pass < 2; ++pass) {
456 	    unsigned count = 0;
457 
458 	    _nc_first_db(&state, &offset);
459 	    while ((path = _nc_next_db(&state, &offset)) != 0) {
460 		if (!is_database(path)) {
461 		    ;
462 		} else if (eargv != 0) {
463 		    unsigned n;
464 		    int found = FALSE;
465 
466 		    /* eliminate duplicates */
467 		    for (n = 0; n < count; ++n) {
468 			if (!strcmp(path, eargv[n])) {
469 			    found = TRUE;
470 			    break;
471 			}
472 		    }
473 		    if (!found) {
474 			eargv[count] = strdup(path);
475 			++count;
476 		    }
477 		} else {
478 		    ++count;
479 		}
480 	    }
481 	    if (!pass) {
482 		eargv = typeCalloc(char *, count + 1);
483 	    } else {
484 		code = typelist((int) count, eargv, header, deschook);
485 		while (count-- > 0)
486 		    free(eargv[count]);
487 		free(eargv);
488 	    }
489 	}
490     } else {
491 	DBDIRS state;
492 	int offset;
493 	const char *path;
494 	char *eargv[3];
495 	int count = 0;
496 
497 	_nc_first_db(&state, &offset);
498 	while ((path = _nc_next_db(&state, &offset)) != 0) {
499 	    if (is_database(path)) {
500 		eargv[count++] = strdup(path);
501 		break;
502 	    }
503 	}
504 	eargv[count] = 0;
505 
506 	code = typelist(count, eargv, header, deschook);
507 
508 	while (count-- > 0)
509 	    free(eargv[count]);
510     }
511     _nc_last_db();
512 
513     ExitProgram(code);
514 }
515