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
47MODULE_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
51const char *_nc_progname;
52
53#if NO_LEAKS
54#undef ExitProgram
55static void ExitProgram(int code) GCC_NORETURN;
56static void
57ExitProgram(int code)
58{
59    _nc_free_entries(_nc_head);
60    _nc_free_tic(code);
61}
62#endif
63
64#if USE_HASHED_DB
65static bool
66make_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
87static bool
88is_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
116static void
117deschook(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
129static void
130show_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
155static int
156typelist(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
318static void
319usage(void)
320{
321    (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname);
322    ExitProgram(EXIT_FAILURE);
323}
324
325int
326main(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