ncurses 5.7 - patch 20090314
[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.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 }