ncurses 5.9 - patch 20120225
[ncurses.git] / progs / toe.c
1 /****************************************************************************
2  * Copyright (c) 1998-2011,2012 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.66 2012/02/22 23:57:44 tom Exp $")
48
49 #define isDotname(name) (!strcmp(name, ".") || !strcmp(name, ".."))
50
51 typedef struct {
52     int db_index;
53     unsigned long checksum;
54     char *term_name;
55     char *description;
56 } TERMDATA;
57
58 const char *_nc_progname;
59
60 static TERMDATA *ptr_termdata;  /* array of terminal data */
61 static size_t use_termdata;     /* actual usage in ptr_termdata[] */
62 static size_t len_termdata;     /* allocated size of ptr_termdata[] */
63
64 #if NO_LEAKS
65 #undef ExitProgram
66 static void ExitProgram(int code) GCC_NORETURN;
67 static void
68 ExitProgram(int code)
69 {
70     _nc_free_entries(_nc_head);
71     _nc_free_tic(code);
72 }
73 #endif
74
75 static void failed(const char *) GCC_NORETURN;
76
77 static void
78 failed(const char *msg)
79 {
80     perror(msg);
81     ExitProgram(EXIT_FAILURE);
82 }
83
84 static char *
85 strmalloc(const char *value)
86 {
87     char *result = strdup(value);
88     if (result == 0) {
89         failed("strmalloc");
90     }
91     return result;
92 }
93
94 static TERMDATA *
95 new_termdata(void)
96 {
97     size_t want = use_termdata + 1;
98
99     if (want >= len_termdata) {
100         len_termdata = (2 * want) + 10;
101         ptr_termdata = typeRealloc(TERMDATA, len_termdata, ptr_termdata);
102     }
103
104     return ptr_termdata + use_termdata++;
105 }
106
107 static int
108 compare_termdata(const void *a, const void *b)
109 {
110     const TERMDATA *p = (const TERMDATA *) a;
111     const TERMDATA *q = (const TERMDATA *) b;
112     int result = strcmp(p->term_name, q->term_name);
113
114     if (result == 0) {
115         result = (p->db_index - q->db_index);
116     }
117     return result;
118 }
119
120 /*
121  * Sort the array of TERMDATA and print it.  If more than one database is being
122  * reported, add a column to show which database has a given entry.
123  */
124 static void
125 show_termdata(int eargc, char **eargv)
126 {
127     int j, k;
128     size_t n;
129
130     if (use_termdata) {
131         if (eargc > 1) {
132             for (j = 0; j < eargc; ++j) {
133                 for (k = 0; k <= j; ++k) {
134                     printf("--");
135                 }
136                 printf("> ");
137                 printf("%s\n", eargv[j]);
138             }
139         }
140         if (use_termdata > 1)
141             qsort(ptr_termdata, use_termdata, sizeof(TERMDATA), compare_termdata);
142         for (n = 0; n < use_termdata; ++n) {
143
144             /*
145              * If there is more than one database, show how they differ.
146              */
147             if (eargc > 1) {
148                 unsigned long check = 0;
149                 k = 0;
150                 for (;;) {
151                     for (; k < ptr_termdata[n].db_index; ++k) {
152                         printf("--");
153                     }
154
155                     /*
156                      * If this is the first entry, or its checksum differs
157                      * from the first entry's checksum, print "*". Otherwise
158                      * it looks enough like a duplicate to print "+".
159                      */
160                     printf("%c-", ((check == 0
161                                     || (check != ptr_termdata[n].checksum))
162                                    ? '*'
163                                    : '+'));
164                     check = ptr_termdata[n].checksum;
165
166                     ++k;
167                     if ((n + 1) >= use_termdata
168                         || strcmp(ptr_termdata[n].term_name,
169                                   ptr_termdata[n + 1].term_name)) {
170                         break;
171                     }
172                     ++n;
173                 }
174                 for (; k < eargc; ++k) {
175                     printf("--");
176                 }
177                 printf(":\t");
178             }
179
180             (void) printf("%-10s\t%s\n",
181                           ptr_termdata[n].term_name,
182                           ptr_termdata[n].description);
183         }
184     }
185 }
186
187 static void
188 free_termdata(void)
189 {
190     if (ptr_termdata != 0) {
191         while (use_termdata != 0) {
192             --use_termdata;
193             free(ptr_termdata[use_termdata].term_name);
194             free(ptr_termdata[use_termdata].description);
195         }
196         free(ptr_termdata);
197         ptr_termdata = 0;
198     }
199     use_termdata = 0;
200     len_termdata = 0;
201 }
202
203 static char **
204 allocArgv(size_t count)
205 {
206     char **result = typeCalloc(char *, count + 1);
207     if (result == 0)
208         failed("realloc eargv");
209
210     assert(result != 0);
211     return result;
212 }
213
214 static void
215 freeArgv(char **argv)
216 {
217     if (argv) {
218         int count = 0;
219         while (argv[count]) {
220             free(argv[count++]);
221         }
222         free(argv);
223     }
224 }
225
226 #if USE_HASHED_DB
227 static bool
228 make_db_name(char *dst, const char *src, unsigned limit)
229 {
230     static const char suffix[] = DBM_SUFFIX;
231
232     bool result = FALSE;
233     unsigned lens = sizeof(suffix) - 1;
234     unsigned size = strlen(src);
235     unsigned need = lens + size;
236
237     if (need <= limit) {
238         if (size >= lens
239             && !strcmp(src + size - lens, suffix)) {
240             _nc_STRCPY(dst, src, PATH_MAX);
241         } else {
242             _nc_SPRINTF(dst, _nc_SLIMIT(PATH_MAX) "%s%s", src, suffix);
243         }
244         result = TRUE;
245     }
246     return result;
247 }
248 #endif
249
250 typedef void (DescHook) (int /* db_index */ ,
251                          int /* db_limit */ ,
252                          const char * /* term_name */ ,
253                          TERMTYPE * /* term */ );
254
255 static const char *
256 term_description(TERMTYPE *tp)
257 {
258     const char *desc;
259
260     if ((desc = strrchr(tp->term_names, '|')) == 0 || *++desc == '\0')
261         desc = "(No description)";
262
263     return desc;
264 }
265
266 /* display a description for the type */
267 static void
268 deschook(int db_index, int db_limit, const char *term_name, TERMTYPE *tp)
269 {
270     (void) db_index;
271     (void) db_limit;
272     (void) printf("%-10s\t%s\n", term_name, term_description(tp));
273 }
274
275 static unsigned long
276 string_sum(const char *value)
277 {
278     unsigned long result = 0;
279
280     if ((intptr_t) value == (intptr_t) (-1)) {
281         result = ~result;
282     } else if (value) {
283         while (*value) {
284             result += UChar(*value);
285             ++value;
286         }
287     }
288     return result;
289 }
290
291 static unsigned long
292 checksum_of(TERMTYPE *tp)
293 {
294     unsigned long result = string_sum(tp->term_names);
295     unsigned i;
296
297     for (i = 0; i < NUM_BOOLEANS(tp); i++) {
298         result += (tp->Booleans[i]);
299     }
300     for (i = 0; i < NUM_NUMBERS(tp); i++) {
301         result += (tp->Numbers[i]);
302     }
303     for (i = 0; i < NUM_STRINGS(tp); i++) {
304         result += string_sum(tp->Strings[i]);
305     }
306     return result;
307 }
308
309 /* collect data, to sort before display */
310 static void
311 sorthook(int db_index, int db_limit, const char *term_name, TERMTYPE *tp)
312 {
313     TERMDATA *data = new_termdata();
314
315     data->db_index = db_index;
316     data->checksum = ((db_limit > 1) ? checksum_of(tp) : 0);
317     data->term_name = strmalloc(term_name);
318     data->description = strmalloc(term_description(tp));
319 }
320
321 #if USE_TERMCAP
322 static void
323 show_termcap(int db_index, int db_limit, char *buffer, DescHook hook)
324 {
325     TERMTYPE data;
326     char *next = strchr(buffer, ':');
327     char *last;
328     char *list = buffer;
329
330     if (next)
331         *next = '\0';
332
333     last = strrchr(buffer, '|');
334     if (last)
335         ++last;
336
337     memset(&data, 0, sizeof(data));
338     data.term_names = strmalloc(buffer);
339     while ((next = strtok(list, "|")) != 0) {
340         if (next != last)
341             hook(db_index, db_limit, next, &data);
342         list = 0;
343     }
344     free(data.term_names);
345 }
346 #endif
347
348 static int
349 typelist(int eargc, char *eargv[],
350          bool verbosity,
351          DescHook hook)
352 /* apply a function to each entry in given terminfo directories */
353 {
354     int i;
355
356     for (i = 0; i < eargc; i++) {
357 #if USE_DATABASE
358         if (_nc_is_dir_path(eargv[i])) {
359             char *cwd_buf = 0;
360             DIR *termdir;
361             DIRENT *subdir;
362
363             if ((termdir = opendir(eargv[i])) == 0) {
364                 (void) fflush(stdout);
365                 (void) fprintf(stderr,
366                                "%s: can't open terminfo directory %s\n",
367                                _nc_progname, eargv[i]);
368                 continue;
369             }
370
371             if (verbosity)
372                 (void) printf("#\n#%s:\n#\n", eargv[i]);
373
374             while ((subdir = readdir(termdir)) != 0) {
375                 size_t len = NAMLEN(subdir);
376                 size_t cwd_len = len + strlen(eargv[i]) + 3;
377                 char name_1[PATH_MAX];
378                 DIR *entrydir;
379                 DIRENT *entry;
380
381                 cwd_buf = typeRealloc(char, cwd_len, cwd_buf);
382                 if (cwd_buf == 0)
383                     failed("realloc cwd_buf");
384
385                 assert(cwd_buf != 0);
386
387                 strncpy(name_1, subdir->d_name, len)[len] = '\0';
388                 if (isDotname(name_1))
389                     continue;
390
391                 _nc_SPRINTF(cwd_buf, _nc_SLIMIT(cwd_len)
392                             "%s/%.*s/", eargv[i], (int) len, name_1);
393                 if (chdir(cwd_buf) != 0)
394                     continue;
395
396                 entrydir = opendir(".");
397                 if (entrydir == 0) {
398                     perror(cwd_buf);
399                     continue;
400                 }
401                 while ((entry = readdir(entrydir)) != 0) {
402                     char name_2[PATH_MAX];
403                     TERMTYPE lterm;
404                     char *cn;
405                     int status;
406
407                     len = NAMLEN(entry);
408                     strncpy(name_2, entry->d_name, len)[len] = '\0';
409                     if (isDotname(name_2) || !_nc_is_file_path(name_2))
410                         continue;
411
412                     status = _nc_read_file_entry(name_2, &lterm);
413                     if (status <= 0) {
414                         (void) fflush(stdout);
415                         (void) fprintf(stderr,
416                                        "%s: couldn't open terminfo file %s.\n",
417                                        _nc_progname, name_2);
418                         return (EXIT_FAILURE);
419                     }
420
421                     /* only visit things once, by primary name */
422                     cn = _nc_first_name(lterm.term_names);
423                     if (!strcmp(cn, name_2)) {
424                         /* apply the selected hook function */
425                         hook(i, eargc, cn, &lterm);
426                     }
427                     _nc_free_termtype(&lterm);
428                 }
429                 closedir(entrydir);
430             }
431             closedir(termdir);
432             if (cwd_buf != 0)
433                 free(cwd_buf);
434             continue;
435         }
436 #if USE_HASHED_DB
437         else {
438             DB *capdbp;
439             char filename[PATH_MAX];
440
441             if (verbosity)
442                 (void) printf("#\n#%s:\n#\n", eargv[i]);
443
444             if (make_db_name(filename, eargv[i], sizeof(filename))) {
445                 if ((capdbp = _nc_db_open(filename, FALSE)) != 0) {
446                     DBT key, data;
447                     int code;
448
449                     code = _nc_db_first(capdbp, &key, &data);
450                     while (code == 0) {
451                         TERMTYPE lterm;
452                         int used;
453                         char *have;
454                         char *cn;
455
456                         if (_nc_db_have_data(&key, &data, &have, &used)) {
457                             if (_nc_read_termtype(&lterm, have, used) > 0) {
458                                 /* only visit things once, by primary name */
459                                 cn = _nc_first_name(lterm.term_names);
460                                 /* apply the selected hook function */
461                                 hook(i, eargc, cn, &lterm);
462                                 _nc_free_termtype(&lterm);
463                             }
464                         }
465                         code = _nc_db_next(capdbp, &key, &data);
466                     }
467
468                     _nc_db_close(capdbp);
469                     continue;
470                 }
471             }
472         }
473 #endif
474 #endif
475 #if USE_TERMCAP
476 #if HAVE_BSD_CGETENT
477         {
478             CGETENT_CONST char *db_array[2];
479             char *buffer = 0;
480
481             if (verbosity)
482                 (void) printf("#\n#%s:\n#\n", eargv[i]);
483
484             db_array[0] = eargv[i];
485             db_array[1] = 0;
486
487             if (cgetfirst(&buffer, db_array) > 0) {
488                 show_termcap(i, eargc, buffer, hook);
489                 free(buffer);
490                 while (cgetnext(&buffer, db_array) > 0) {
491                     show_termcap(i, eargc, buffer, hook);
492                     free(buffer);
493                 }
494                 cgetclose();
495                 continue;
496             }
497         }
498 #else
499         /* scan termcap text-file only */
500         if (_nc_is_file_path(eargv[i])) {
501             char buffer[2048];
502             FILE *fp;
503
504             if (verbosity)
505                 (void) printf("#\n#%s:\n#\n", eargv[i]);
506
507             if ((fp = fopen(eargv[i], "r")) != 0) {
508                 while (fgets(buffer, sizeof(buffer), fp) != 0) {
509                     if (*buffer == '#')
510                         continue;
511                     if (isspace(*buffer))
512                         continue;
513                     show_termcap(i, eargc, buffer, hook);
514                 }
515                 fclose(fp);
516             }
517         }
518 #endif
519 #endif
520     }
521
522     if (hook == sorthook) {
523         show_termdata(eargc, eargv);
524         free_termdata();
525     }
526
527     return (EXIT_SUCCESS);
528 }
529
530 static void
531 usage(void)
532 {
533     (void) fprintf(stderr, "usage: %s [-ahsuUV] [-v n] [file...]\n", _nc_progname);
534     ExitProgram(EXIT_FAILURE);
535 }
536
537 int
538 main(int argc, char *argv[])
539 {
540     bool all_dirs = FALSE;
541     bool direct_dependencies = FALSE;
542     bool invert_dependencies = FALSE;
543     bool header = FALSE;
544     char *report_file = 0;
545     unsigned i;
546     int code;
547     int this_opt, last_opt = '?';
548     unsigned v_opt = 0;
549     DescHook *hook = deschook;
550
551     _nc_progname = _nc_rootname(argv[0]);
552
553     while ((this_opt = getopt(argc, argv, "0123456789ahsu:vU:V")) != -1) {
554         /* handle optional parameter */
555         if (isdigit(this_opt)) {
556             switch (last_opt) {
557             case 'v':
558                 v_opt = (unsigned) (this_opt - '0');
559                 break;
560             default:
561                 if (isdigit(last_opt))
562                     v_opt *= 10;
563                 else
564                     v_opt = 0;
565                 v_opt += (unsigned) (this_opt - '0');
566                 last_opt = this_opt;
567             }
568             continue;
569         }
570         switch (this_opt) {
571         case 'a':
572             all_dirs = TRUE;
573             break;
574         case 'h':
575             header = TRUE;
576             break;
577         case 's':
578             hook = sorthook;
579             break;
580         case 'u':
581             direct_dependencies = TRUE;
582             report_file = optarg;
583             break;
584         case 'v':
585             v_opt = 1;
586             break;
587         case 'U':
588             invert_dependencies = TRUE;
589             report_file = optarg;
590             break;
591         case 'V':
592             puts(curses_version());
593             ExitProgram(EXIT_SUCCESS);
594         default:
595             usage();
596         }
597     }
598     set_trace_level(v_opt);
599
600     if (report_file != 0) {
601         if (freopen(report_file, "r", stdin) == 0) {
602             (void) fflush(stdout);
603             fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file);
604             ExitProgram(EXIT_FAILURE);
605         }
606
607         /* parse entries out of the source file */
608         _nc_set_source(report_file);
609         _nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK);
610     }
611
612     /* maybe we want a direct-dependency listing? */
613     if (direct_dependencies) {
614         ENTRY *qp;
615
616         for_entry_list(qp) {
617             if (qp->nuses) {
618                 unsigned j;
619
620                 (void) printf("%s:", _nc_first_name(qp->tterm.term_names));
621                 for (j = 0; j < qp->nuses; j++)
622                     (void) printf(" %s", qp->uses[j].name);
623                 putchar('\n');
624             }
625         }
626
627         ExitProgram(EXIT_SUCCESS);
628     }
629
630     /* maybe we want a reverse-dependency listing? */
631     if (invert_dependencies) {
632         ENTRY *qp, *rp;
633         int matchcount;
634
635         for_entry_list(qp) {
636             matchcount = 0;
637             for_entry_list(rp) {
638                 if (rp->nuses == 0)
639                     continue;
640
641                 for (i = 0; i < rp->nuses; i++)
642                     if (_nc_name_match(qp->tterm.term_names,
643                                        rp->uses[i].name, "|")) {
644                         if (matchcount++ == 0)
645                             (void) printf("%s:",
646                                           _nc_first_name(qp->tterm.term_names));
647                         (void) printf(" %s",
648                                       _nc_first_name(rp->tterm.term_names));
649                     }
650             }
651             if (matchcount)
652                 putchar('\n');
653         }
654
655         ExitProgram(EXIT_SUCCESS);
656     }
657
658     /*
659      * If we get this far, user wants a simple terminal type listing.
660      */
661     if (optind < argc) {
662         code = typelist(argc - optind, argv + optind, header, hook);
663     } else if (all_dirs) {
664         DBDIRS state;
665         int offset;
666         int pass;
667         const char *path;
668         char **eargv = 0;
669
670         code = EXIT_FAILURE;
671         for (pass = 0; pass < 2; ++pass) {
672             size_t count = 0;
673
674             _nc_first_db(&state, &offset);
675             while ((path = _nc_next_db(&state, &offset)) != 0) {
676                 if (pass) {
677                     eargv[count] = strmalloc(path);
678                 }
679                 ++count;
680             }
681             if (!pass) {
682                 eargv = allocArgv(count);
683             } else {
684                 code = typelist((int) count, eargv, header, hook);
685                 freeArgv(eargv);
686             }
687         }
688     } else {
689         DBDIRS state;
690         int offset;
691         const char *path;
692         char **eargv = allocArgv(2);
693         size_t count = 0;
694
695         _nc_first_db(&state, &offset);
696         if ((path = _nc_next_db(&state, &offset)) != 0) {
697             eargv[count++] = strmalloc(path);
698         }
699
700         code = typelist((int) count, eargv, header, hook);
701
702         freeArgv(eargv);
703     }
704     _nc_last_db();
705
706     ExitProgram(code);
707 }