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