ncurses 5.9 - patch 20111231
[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.64 2012/01/01 02:56:17 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             (void) strcpy(dst, src);
241         else
242             (void) sprintf(dst, "%s%s", src, suffix);
243         result = TRUE;
244     }
245     return result;
246 }
247 #endif
248
249 typedef void (DescHook) (int /* db_index */ ,
250                          int /* db_limit */ ,
251                          const char * /* term_name */ ,
252                          TERMTYPE * /* term */ );
253
254 static const char *
255 term_description(TERMTYPE *tp)
256 {
257     const char *desc;
258
259     if ((desc = strrchr(tp->term_names, '|')) == 0 || *++desc == '\0')
260         desc = "(No description)";
261
262     return desc;
263 }
264
265 /* display a description for the type */
266 static void
267 deschook(int db_index, int db_limit, const char *term_name, TERMTYPE *tp)
268 {
269     (void) db_index;
270     (void) db_limit;
271     (void) printf("%-10s\t%s\n", term_name, term_description(tp));
272 }
273
274 static unsigned long
275 string_sum(const char *value)
276 {
277     unsigned long result = 0;
278
279     if ((intptr_t) value == (intptr_t) (-1)) {
280         result = ~result;
281     } else if (value) {
282         while (*value) {
283             result += UChar(*value);
284             ++value;
285         }
286     }
287     return result;
288 }
289
290 static unsigned long
291 checksum_of(TERMTYPE *tp)
292 {
293     unsigned long result = string_sum(tp->term_names);
294     unsigned i;
295
296     for (i = 0; i < NUM_BOOLEANS(tp); i++) {
297         result += (tp->Booleans[i]);
298     }
299     for (i = 0; i < NUM_NUMBERS(tp); i++) {
300         result += (tp->Numbers[i]);
301     }
302     for (i = 0; i < NUM_STRINGS(tp); i++) {
303         result += string_sum(tp->Strings[i]);
304     }
305     return result;
306 }
307
308 /* collect data, to sort before display */
309 static void
310 sorthook(int db_index, int db_limit, const char *term_name, TERMTYPE *tp)
311 {
312     TERMDATA *data = new_termdata();
313
314     data->db_index = db_index;
315     data->checksum = ((db_limit > 1) ? checksum_of(tp) : 0);
316     data->term_name = strmalloc(term_name);
317     data->description = strmalloc(term_description(tp));
318 }
319
320 #if USE_TERMCAP
321 static void
322 show_termcap(int db_index, int db_limit, char *buffer, DescHook hook)
323 {
324     TERMTYPE data;
325     char *next = strchr(buffer, ':');
326     char *last;
327     char *list = buffer;
328
329     if (next)
330         *next = '\0';
331
332     last = strrchr(buffer, '|');
333     if (last)
334         ++last;
335
336     memset(&data, 0, sizeof(data));
337     data.term_names = strmalloc(buffer);
338     while ((next = strtok(list, "|")) != 0) {
339         if (next != last)
340             hook(db_index, db_limit, next, &data);
341         list = 0;
342     }
343     free(data.term_names);
344 }
345 #endif
346
347 static int
348 typelist(int eargc, char *eargv[],
349          bool verbosity,
350          DescHook hook)
351 /* apply a function to each entry in given terminfo directories */
352 {
353     int i;
354
355     for (i = 0; i < eargc; i++) {
356 #if USE_DATABASE
357         if (_nc_is_dir_path(eargv[i])) {
358             char *cwd_buf = 0;
359             DIR *termdir;
360             DIRENT *subdir;
361
362             if ((termdir = opendir(eargv[i])) == 0) {
363                 (void) fflush(stdout);
364                 (void) fprintf(stderr,
365                                "%s: can't open terminfo directory %s\n",
366                                _nc_progname, eargv[i]);
367                 continue;
368             }
369
370             if (verbosity)
371                 (void) printf("#\n#%s:\n#\n", eargv[i]);
372
373             while ((subdir = readdir(termdir)) != 0) {
374                 size_t len = NAMLEN(subdir);
375                 size_t cwd_len = len + strlen(eargv[i]) + 3;
376                 char name_1[PATH_MAX];
377                 DIR *entrydir;
378                 DIRENT *entry;
379
380                 cwd_buf = typeRealloc(char, cwd_len, cwd_buf);
381                 if (cwd_buf == 0)
382                     failed("realloc cwd_buf");
383
384                 assert(cwd_buf != 0);
385
386                 strncpy(name_1, subdir->d_name, len)[len] = '\0';
387                 if (isDotname(name_1))
388                     continue;
389
390                 (void) sprintf(cwd_buf, "%s/%.*s/", eargv[i], (int) len, name_1);
391                 if (chdir(cwd_buf) != 0)
392                     continue;
393
394                 entrydir = opendir(".");
395                 if (entrydir == 0) {
396                     perror(cwd_buf);
397                     continue;
398                 }
399                 while ((entry = readdir(entrydir)) != 0) {
400                     char name_2[PATH_MAX];
401                     TERMTYPE lterm;
402                     char *cn;
403                     int status;
404
405                     len = NAMLEN(entry);
406                     strncpy(name_2, entry->d_name, len)[len] = '\0';
407                     if (isDotname(name_2) || !_nc_is_file_path(name_2))
408                         continue;
409
410                     status = _nc_read_file_entry(name_2, &lterm);
411                     if (status <= 0) {
412                         (void) fflush(stdout);
413                         (void) fprintf(stderr,
414                                        "%s: couldn't open terminfo file %s.\n",
415                                        _nc_progname, name_2);
416                         return (EXIT_FAILURE);
417                     }
418
419                     /* only visit things once, by primary name */
420                     cn = _nc_first_name(lterm.term_names);
421                     if (!strcmp(cn, name_2)) {
422                         /* apply the selected hook function */
423                         hook(i, eargc, cn, &lterm);
424                     }
425                     _nc_free_termtype(&lterm);
426                 }
427                 closedir(entrydir);
428             }
429             closedir(termdir);
430             if (cwd_buf != 0)
431                 free(cwd_buf);
432             continue;
433         }
434 #if USE_HASHED_DB
435         else {
436             DB *capdbp;
437             char filename[PATH_MAX];
438
439             if (verbosity)
440                 (void) printf("#\n#%s:\n#\n", eargv[i]);
441
442             if (make_db_name(filename, eargv[i], sizeof(filename))) {
443                 if ((capdbp = _nc_db_open(filename, FALSE)) != 0) {
444                     DBT key, data;
445                     int code;
446
447                     code = _nc_db_first(capdbp, &key, &data);
448                     while (code == 0) {
449                         TERMTYPE lterm;
450                         int used;
451                         char *have;
452                         char *cn;
453
454                         if (_nc_db_have_data(&key, &data, &have, &used)) {
455                             if (_nc_read_termtype(&lterm, have, used) > 0) {
456                                 /* only visit things once, by primary name */
457                                 cn = _nc_first_name(lterm.term_names);
458                                 /* apply the selected hook function */
459                                 hook(i, eargc, cn, &lterm);
460                                 _nc_free_termtype(&lterm);
461                             }
462                         }
463                         code = _nc_db_next(capdbp, &key, &data);
464                     }
465
466                     _nc_db_close(capdbp);
467                     continue;
468                 }
469             }
470         }
471 #endif
472 #endif
473 #if USE_TERMCAP
474 #if HAVE_BSD_CGETENT
475         {
476             CGETENT_CONST char *db_array[2];
477             char *buffer = 0;
478
479             if (verbosity)
480                 (void) printf("#\n#%s:\n#\n", eargv[i]);
481
482             db_array[0] = eargv[i];
483             db_array[1] = 0;
484
485             if (cgetfirst(&buffer, db_array) > 0) {
486                 show_termcap(i, eargc, buffer, hook);
487                 free(buffer);
488                 while (cgetnext(&buffer, db_array) > 0) {
489                     show_termcap(i, eargc, buffer, hook);
490                     free(buffer);
491                 }
492                 cgetclose();
493                 continue;
494             }
495         }
496 #else
497         /* scan termcap text-file only */
498         if (_nc_is_file_path(eargv[i])) {
499             char buffer[2048];
500             FILE *fp;
501
502             if (verbosity)
503                 (void) printf("#\n#%s:\n#\n", eargv[i]);
504
505             if ((fp = fopen(eargv[i], "r")) != 0) {
506                 while (fgets(buffer, sizeof(buffer), fp) != 0) {
507                     if (*buffer == '#')
508                         continue;
509                     if (isspace(*buffer))
510                         continue;
511                     show_termcap(i, eargc, buffer, hook);
512                 }
513                 fclose(fp);
514             }
515         }
516 #endif
517 #endif
518     }
519
520     if (hook == sorthook) {
521         show_termdata(eargc, eargv);
522         free_termdata();
523     }
524
525     return (EXIT_SUCCESS);
526 }
527
528 static void
529 usage(void)
530 {
531     (void) fprintf(stderr, "usage: %s [-ahsuUV] [-v n] [file...]\n", _nc_progname);
532     ExitProgram(EXIT_FAILURE);
533 }
534
535 int
536 main(int argc, char *argv[])
537 {
538     bool all_dirs = FALSE;
539     bool direct_dependencies = FALSE;
540     bool invert_dependencies = FALSE;
541     bool header = FALSE;
542     char *report_file = 0;
543     unsigned i;
544     int code;
545     int this_opt, last_opt = '?';
546     unsigned v_opt = 0;
547     DescHook *hook = deschook;
548
549     _nc_progname = _nc_rootname(argv[0]);
550
551     while ((this_opt = getopt(argc, argv, "0123456789ahsu:vU:V")) != -1) {
552         /* handle optional parameter */
553         if (isdigit(this_opt)) {
554             switch (last_opt) {
555             case 'v':
556                 v_opt = (unsigned) (this_opt - '0');
557                 break;
558             default:
559                 if (isdigit(last_opt))
560                     v_opt *= 10;
561                 else
562                     v_opt = 0;
563                 v_opt += (unsigned) (this_opt - '0');
564                 last_opt = this_opt;
565             }
566             continue;
567         }
568         switch (this_opt) {
569         case 'a':
570             all_dirs = TRUE;
571             break;
572         case 'h':
573             header = TRUE;
574             break;
575         case 's':
576             hook = sorthook;
577             break;
578         case 'u':
579             direct_dependencies = TRUE;
580             report_file = optarg;
581             break;
582         case 'v':
583             v_opt = 1;
584             break;
585         case 'U':
586             invert_dependencies = TRUE;
587             report_file = optarg;
588             break;
589         case 'V':
590             puts(curses_version());
591             ExitProgram(EXIT_SUCCESS);
592         default:
593             usage();
594         }
595     }
596     set_trace_level(v_opt);
597
598     if (report_file != 0) {
599         if (freopen(report_file, "r", stdin) == 0) {
600             (void) fflush(stdout);
601             fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file);
602             ExitProgram(EXIT_FAILURE);
603         }
604
605         /* parse entries out of the source file */
606         _nc_set_source(report_file);
607         _nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK);
608     }
609
610     /* maybe we want a direct-dependency listing? */
611     if (direct_dependencies) {
612         ENTRY *qp;
613
614         for_entry_list(qp) {
615             if (qp->nuses) {
616                 unsigned j;
617
618                 (void) printf("%s:", _nc_first_name(qp->tterm.term_names));
619                 for (j = 0; j < qp->nuses; j++)
620                     (void) printf(" %s", qp->uses[j].name);
621                 putchar('\n');
622             }
623         }
624
625         ExitProgram(EXIT_SUCCESS);
626     }
627
628     /* maybe we want a reverse-dependency listing? */
629     if (invert_dependencies) {
630         ENTRY *qp, *rp;
631         int matchcount;
632
633         for_entry_list(qp) {
634             matchcount = 0;
635             for_entry_list(rp) {
636                 if (rp->nuses == 0)
637                     continue;
638
639                 for (i = 0; i < rp->nuses; i++)
640                     if (_nc_name_match(qp->tterm.term_names,
641                                        rp->uses[i].name, "|")) {
642                         if (matchcount++ == 0)
643                             (void) printf("%s:",
644                                           _nc_first_name(qp->tterm.term_names));
645                         (void) printf(" %s",
646                                       _nc_first_name(rp->tterm.term_names));
647                     }
648             }
649             if (matchcount)
650                 putchar('\n');
651         }
652
653         ExitProgram(EXIT_SUCCESS);
654     }
655
656     /*
657      * If we get this far, user wants a simple terminal type listing.
658      */
659     if (optind < argc) {
660         code = typelist(argc - optind, argv + optind, header, hook);
661     } else if (all_dirs) {
662         DBDIRS state;
663         int offset;
664         int pass;
665         const char *path;
666         char **eargv = 0;
667
668         code = EXIT_FAILURE;
669         for (pass = 0; pass < 2; ++pass) {
670             size_t count = 0;
671
672             _nc_first_db(&state, &offset);
673             while ((path = _nc_next_db(&state, &offset)) != 0) {
674                 if (pass) {
675                     eargv[count] = strmalloc(path);
676                 }
677                 ++count;
678             }
679             if (!pass) {
680                 eargv = allocArgv(count);
681             } else {
682                 code = typelist((int) count, eargv, header, hook);
683                 freeArgv(eargv);
684             }
685         }
686     } else {
687         DBDIRS state;
688         int offset;
689         const char *path;
690         char **eargv = allocArgv(2);
691         size_t count = 0;
692
693         _nc_first_db(&state, &offset);
694         if ((path = _nc_next_db(&state, &offset)) != 0) {
695             eargv[count++] = strmalloc(path);
696         }
697
698         code = typelist((int) count, eargv, header, hook);
699
700         freeArgv(eargv);
701     }
702     _nc_last_db();
703
704     ExitProgram(code);
705 }