ncurses 5.9 - patch 20110604
[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.54 2011/05/21 18:32:13 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                 return (EXIT_FAILURE);
185             } else if (verbosity)
186                 (void) printf("#\n#%s:\n#\n", eargv[i]);
187
188             while ((subdir = readdir(termdir)) != 0) {
189                 size_t len = NAMLEN(subdir);
190                 size_t cwd_len = len + strlen(eargv[i]) + 3;
191                 char name_1[PATH_MAX];
192                 DIR *entrydir;
193                 DIRENT *entry;
194
195                 cwd_buf = typeRealloc(char, cwd_len, cwd_buf);
196                 if (cwd_buf == 0)
197                     failed("realloc cwd_buf");
198
199                 assert(cwd_buf != 0);
200
201                 strncpy(name_1, subdir->d_name, len)[len] = '\0';
202                 if (isDotname(name_1))
203                     continue;
204
205                 (void) sprintf(cwd_buf, "%s/%.*s/", eargv[i], (int) len, name_1);
206                 if (chdir(cwd_buf) != 0)
207                     continue;
208
209                 entrydir = opendir(".");
210                 if (entrydir == 0) {
211                     perror(cwd_buf);
212                     continue;
213                 }
214                 while ((entry = readdir(entrydir)) != 0) {
215                     char name_2[PATH_MAX];
216                     TERMTYPE lterm;
217                     char *cn;
218                     int status;
219
220                     len = NAMLEN(entry);
221                     strncpy(name_2, entry->d_name, len)[len] = '\0';
222                     if (isDotname(name_2) || !_nc_is_file_path(name_2))
223                         continue;
224
225                     status = _nc_read_file_entry(name_2, &lterm);
226                     if (status <= 0) {
227                         (void) fflush(stdout);
228                         (void) fprintf(stderr,
229                                        "%s: couldn't open terminfo file %s.\n",
230                                        _nc_progname, name_2);
231                         return (EXIT_FAILURE);
232                     }
233
234                     /* only visit things once, by primary name */
235                     cn = _nc_first_name(lterm.term_names);
236                     if (!strcmp(cn, name_2)) {
237                         /* apply the selected hook function */
238                         (*hook) (cn, &lterm);
239                     }
240                     _nc_free_termtype(&lterm);
241                 }
242                 closedir(entrydir);
243             }
244             closedir(termdir);
245             if (cwd_buf != 0)
246                 free(cwd_buf);
247         }
248 #if USE_HASHED_DB
249         else {
250             DB *capdbp;
251             char filename[PATH_MAX];
252
253             if (make_db_name(filename, eargv[i], sizeof(filename))) {
254                 if ((capdbp = _nc_db_open(filename, FALSE)) != 0) {
255                     DBT key, data;
256                     int code;
257
258                     code = _nc_db_first(capdbp, &key, &data);
259                     while (code == 0) {
260                         TERMTYPE lterm;
261                         int used;
262                         char *have;
263                         char *cn;
264
265                         if (_nc_db_have_data(&key, &data, &have, &used)) {
266                             if (_nc_read_termtype(&lterm, have, used) > 0) {
267                                 /* only visit things once, by primary name */
268                                 cn = _nc_first_name(lterm.term_names);
269                                 /* apply the selected hook function */
270                                 (*hook) (cn, &lterm);
271                                 _nc_free_termtype(&lterm);
272                             }
273                         }
274                         code = _nc_db_next(capdbp, &key, &data);
275                     }
276
277                     _nc_db_close(capdbp);
278                 }
279             }
280         }
281 #endif
282 #endif
283 #if USE_TERMCAP
284 #if HAVE_BSD_CGETENT
285         char *db_array[2];
286         char *buffer = 0;
287
288         if (verbosity)
289             (void) printf("#\n#%s:\n#\n", eargv[i]);
290
291         db_array[0] = eargv[i];
292         db_array[1] = 0;
293
294         if (cgetfirst(&buffer, db_array)) {
295             show_termcap(buffer, hook);
296             free(buffer);
297             while (cgetnext(&buffer, db_array)) {
298                 show_termcap(buffer, hook);
299                 free(buffer);
300             }
301         }
302         cgetclose();
303 #else
304         /* scan termcap text-file only */
305         if (_nc_is_file_path(eargv[i])) {
306             char buffer[2048];
307             FILE *fp;
308
309             if ((fp = fopen(eargv[i], "r")) != 0) {
310                 while (fgets(buffer, sizeof(buffer), fp) != 0) {
311                     if (*buffer == '#')
312                         continue;
313                     if (isspace(*buffer))
314                         continue;
315                     show_termcap(buffer, hook);
316                 }
317                 fclose(fp);
318             }
319         }
320 #endif
321 #endif
322     }
323
324     return (EXIT_SUCCESS);
325 }
326
327 static void
328 usage(void)
329 {
330     (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname);
331     ExitProgram(EXIT_FAILURE);
332 }
333
334 int
335 main(int argc, char *argv[])
336 {
337     bool all_dirs = FALSE;
338     bool direct_dependencies = FALSE;
339     bool invert_dependencies = FALSE;
340     bool header = FALSE;
341     char *report_file = 0;
342     unsigned i;
343     int code;
344     int this_opt, last_opt = '?';
345     unsigned v_opt = 0;
346
347     _nc_progname = _nc_rootname(argv[0]);
348
349     while ((this_opt = getopt(argc, argv, "0123456789ahu:vU:V")) != -1) {
350         /* handle optional parameter */
351         if (isdigit(this_opt)) {
352             switch (last_opt) {
353             case 'v':
354                 v_opt = (unsigned) (this_opt - '0');
355                 break;
356             default:
357                 if (isdigit(last_opt))
358                     v_opt *= 10;
359                 else
360                     v_opt = 0;
361                 v_opt += (unsigned) (this_opt - '0');
362                 last_opt = this_opt;
363             }
364             continue;
365         }
366         switch (this_opt) {
367         case 'a':
368             all_dirs = TRUE;
369             break;
370         case 'h':
371             header = TRUE;
372             break;
373         case 'u':
374             direct_dependencies = TRUE;
375             report_file = optarg;
376             break;
377         case 'v':
378             v_opt = 1;
379             break;
380         case 'U':
381             invert_dependencies = TRUE;
382             report_file = optarg;
383             break;
384         case 'V':
385             puts(curses_version());
386             ExitProgram(EXIT_SUCCESS);
387         default:
388             usage();
389         }
390     }
391     set_trace_level(v_opt);
392
393     if (report_file != 0) {
394         if (freopen(report_file, "r", stdin) == 0) {
395             (void) fflush(stdout);
396             fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file);
397             ExitProgram(EXIT_FAILURE);
398         }
399
400         /* parse entries out of the source file */
401         _nc_set_source(report_file);
402         _nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK);
403     }
404
405     /* maybe we want a direct-dependency listing? */
406     if (direct_dependencies) {
407         ENTRY *qp;
408
409         for_entry_list(qp) {
410             if (qp->nuses) {
411                 unsigned j;
412
413                 (void) printf("%s:", _nc_first_name(qp->tterm.term_names));
414                 for (j = 0; j < qp->nuses; j++)
415                     (void) printf(" %s", qp->uses[j].name);
416                 putchar('\n');
417             }
418         }
419
420         ExitProgram(EXIT_SUCCESS);
421     }
422
423     /* maybe we want a reverse-dependency listing? */
424     if (invert_dependencies) {
425         ENTRY *qp, *rp;
426         int matchcount;
427
428         for_entry_list(qp) {
429             matchcount = 0;
430             for_entry_list(rp) {
431                 if (rp->nuses == 0)
432                     continue;
433
434                 for (i = 0; i < rp->nuses; i++)
435                     if (_nc_name_match(qp->tterm.term_names,
436                                        rp->uses[i].name, "|")) {
437                         if (matchcount++ == 0)
438                             (void) printf("%s:",
439                                           _nc_first_name(qp->tterm.term_names));
440                         (void) printf(" %s",
441                                       _nc_first_name(rp->tterm.term_names));
442                     }
443             }
444             if (matchcount)
445                 putchar('\n');
446         }
447
448         ExitProgram(EXIT_SUCCESS);
449     }
450
451     /*
452      * If we get this far, user wants a simple terminal type listing.
453      */
454     if (optind < argc) {
455         code = typelist(argc - optind, argv + optind, header, deschook);
456     } else if (all_dirs) {
457         DBDIRS state;
458         int offset;
459         int pass;
460         const char *path;
461         char **eargv = 0;
462
463         code = EXIT_FAILURE;
464         for (pass = 0; pass < 2; ++pass) {
465             unsigned count = 0;
466
467             _nc_first_db(&state, &offset);
468             while ((path = _nc_next_db(&state, &offset)) != 0) {
469                 if (!is_database(path)) {
470                     ;
471                 } else if (eargv != 0) {
472                     unsigned n;
473                     int found = FALSE;
474
475                     /* eliminate duplicates */
476                     for (n = 0; n < count; ++n) {
477                         if (!strcmp(path, eargv[n])) {
478                             found = TRUE;
479                             break;
480                         }
481                     }
482                     if (!found) {
483                         eargv[count] = strdup(path);
484                         ++count;
485                     }
486                 } else {
487                     ++count;
488                 }
489             }
490             if (!pass) {
491                 eargv = typeCalloc(char *, count + 1);
492                 if (eargv == 0)
493                     failed("realloc eargv");
494
495                 assert(eargv != 0);
496             } else {
497                 code = typelist((int) count, eargv, header, deschook);
498                 while (count-- > 0)
499                     free(eargv[count]);
500                 free(eargv);
501             }
502         }
503     } else {
504         DBDIRS state;
505         int offset;
506         const char *path;
507         char *eargv[3];
508         int count = 0;
509
510         _nc_first_db(&state, &offset);
511         while ((path = _nc_next_db(&state, &offset)) != 0) {
512             if (is_database(path)) {
513                 eargv[count++] = strdup(path);
514                 break;
515             }
516         }
517         eargv[count] = 0;
518
519         code = typelist(count, eargv, header, deschook);
520
521         while (count-- > 0)
522             free(eargv[count]);
523     }
524     _nc_last_db();
525
526     ExitProgram(code);
527 }