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