38b85cb362ed0918fd1975b29fabdce782a21c11
[ncurses.git] / progs / toe.c
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.48 2008/01/05 20:41:26 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     char *report_file = 0;
320     int i;
321     int code;
322     int this_opt, last_opt = '?';
323     int v_opt = 0;
324
325     _nc_progname = _nc_rootname(argv[0]);
326
327     while ((this_opt = getopt(argc, argv, "0123456789ahu:vU:V")) != -1) {
328         /* handle optional parameter */
329         if (isdigit(this_opt)) {
330             switch (last_opt) {
331             case 'v':
332                 v_opt = (this_opt - '0');
333                 break;
334             default:
335                 if (isdigit(last_opt))
336                     v_opt *= 10;
337                 else
338                     v_opt = 0;
339                 v_opt += (this_opt - '0');
340                 last_opt = this_opt;
341             }
342             continue;
343         }
344         switch (this_opt) {
345         case 'a':
346             all_dirs = TRUE;
347             break;
348         case 'h':
349             header = TRUE;
350             break;
351         case 'u':
352             direct_dependencies = TRUE;
353             report_file = optarg;
354             break;
355         case 'v':
356             v_opt = 1;
357             break;
358         case 'U':
359             invert_dependencies = TRUE;
360             report_file = optarg;
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 (report_file != 0) {
372         if (freopen(report_file, "r", stdin) == 0) {
373             (void) fflush(stdout);
374             fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file);
375             ExitProgram(EXIT_FAILURE);
376         }
377
378         /* parse entries out of the source file */
379         _nc_set_source(report_file);
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 }