X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=progs%2Ftoe.c;h=8fd98d006c2706a70c4633e0c3f6d5b6bcac4acb;hp=2e67bd507ba4ab6f293e572b07d3f6d7e00adae0;hb=7f4b9f390624835ceb0849965a7f6ff2dcb39d00;hpb=c6abbc3dd08d8a749098dc1674ce6ec65d844e86 diff --git a/progs/toe.c b/progs/toe.c index 2e67bd50..8fd98d00 100644 --- a/progs/toe.c +++ b/progs/toe.c @@ -1,5 +1,6 @@ /**************************************************************************** - * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * + * Copyright 2018-2020,2021 Thomas E. Dickey * + * Copyright 1998-2013,2017 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * @@ -40,31 +41,194 @@ #include -#include - #if USE_HASHED_DB #include #endif -MODULE_ID("$Id: toe.c,v 1.42 2007/01/21 01:09:07 tom Exp $") +MODULE_ID("$Id: toe.c,v 1.81 2021/04/03 22:54:52 tom Exp $") #define isDotname(name) (!strcmp(name, ".") || !strcmp(name, "..")) +typedef struct { + int db_index; + unsigned long checksum; + char *term_name; + char *description; +} TERMDATA; + const char *_nc_progname; +static TERMDATA *ptr_termdata; /* array of terminal data */ +static size_t use_termdata; /* actual usage in ptr_termdata[] */ +static size_t len_termdata; /* allocated size of ptr_termdata[] */ + #if NO_LEAKS #undef ExitProgram -static void ExitProgram(int code) GCC_NORETURN; +static GCC_NORETURN void ExitProgram(int code); static void ExitProgram(int code) { _nc_free_entries(_nc_head); - _nc_leaks_dump_entry(); - _nc_leaks_tic(); - _nc_free_and_exit(code); + _nc_free_tic(code); } #endif +static GCC_NORETURN void failed(const char *); + +static void +failed(const char *msg) +{ + perror(msg); + ExitProgram(EXIT_FAILURE); +} + +static char * +strmalloc(const char *value) +{ + char *result = strdup(value); + if (result == 0) { + failed("strmalloc"); + } + return result; +} + +static TERMDATA * +new_termdata(void) +{ + size_t want = use_termdata + 1; + + if (want >= len_termdata) { + len_termdata = (2 * want) + 10; + ptr_termdata = typeRealloc(TERMDATA, len_termdata, ptr_termdata); + if (ptr_termdata == 0) + failed("ptr_termdata"); + } + + return ptr_termdata + use_termdata++; +} + +static int +compare_termdata(const void *a, const void *b) +{ + const TERMDATA *p = (const TERMDATA *) a; + const TERMDATA *q = (const TERMDATA *) b; + int result = strcmp(p->term_name, q->term_name); + + if (result == 0) { + result = (p->db_index - q->db_index); + } + return result; +} + +/* + * Sort the array of TERMDATA and print it. If more than one database is being + * reported, add a column to show which database has a given entry. + */ +static void +show_termdata(int eargc, char **eargv) +{ + if (use_termdata) { + size_t n; + + if (eargc > 1) { + int j; + + for (j = 0; j < eargc; ++j) { + int k; + + for (k = 0; k <= j; ++k) { + printf("--"); + } + printf("> "); + printf("%s\n", eargv[j]); + } + } + if (use_termdata > 1) + qsort(ptr_termdata, use_termdata, sizeof(TERMDATA), compare_termdata); + for (n = 0; n < use_termdata; ++n) { + + /* + * If there is more than one database, show how they differ. + */ + if (eargc > 1) { + unsigned long check = 0; + int k = 0; + for (;;) { + for (; k < ptr_termdata[n].db_index; ++k) { + printf("--"); + } + + /* + * If this is the first entry, or its checksum differs + * from the first entry's checksum, print "*". Otherwise + * it looks enough like a duplicate to print "+". + */ + printf("%c-", ((check == 0 + || (check != ptr_termdata[n].checksum)) + ? '*' + : '+')); + check = ptr_termdata[n].checksum; + + ++k; + if ((n + 1) >= use_termdata + || strcmp(ptr_termdata[n].term_name, + ptr_termdata[n + 1].term_name)) { + break; + } + ++n; + } + for (; k < eargc; ++k) { + printf("--"); + } + printf(":\t"); + } + + (void) printf("%-10s\t%s\n", + ptr_termdata[n].term_name, + ptr_termdata[n].description); + } + } +} + +static void +free_termdata(void) +{ + if (ptr_termdata != 0) { + while (use_termdata != 0) { + --use_termdata; + free(ptr_termdata[use_termdata].term_name); + free(ptr_termdata[use_termdata].description); + } + free(ptr_termdata); + ptr_termdata = 0; + } + use_termdata = 0; + len_termdata = 0; +} + +static char ** +allocArgv(size_t count) +{ + char **result = typeCalloc(char *, count + 1); + if (result == 0) + failed("realloc eargv"); + + assert(result != 0); + return result; +} + +static void +freeArgv(char **argv) +{ + if (argv) { + int count = 0; + while (argv[count]) { + free(argv[count++]); + } + free(argv); + } +} + #if USE_HASHED_DB static bool make_db_name(char *dst, const char *src, unsigned limit) @@ -72,69 +236,102 @@ make_db_name(char *dst, const char *src, unsigned limit) static const char suffix[] = DBM_SUFFIX; bool result = FALSE; - unsigned lens = sizeof(suffix) - 1; - unsigned size = strlen(src); - unsigned need = lens + size; + size_t lens = sizeof(suffix) - 1; + size_t size = strlen(src); + size_t need = lens + size; if (need <= limit) { if (size >= lens - && !strcmp(src + size - lens, suffix)) - (void) strcpy(dst, src); - else - (void) sprintf(dst, "%s%s", src, suffix); + && !strcmp(src + size - lens, suffix)) { + _nc_STRCPY(dst, src, PATH_MAX); + } else { + _nc_SPRINTF(dst, _nc_SLIMIT(PATH_MAX) "%s%s", src, suffix); + } result = TRUE; } return result; } #endif -static bool -is_database(const char *path) +typedef void (DescHook) (int /* db_index */ , + int /* db_limit */ , + const char * /* term_name */ , + TERMTYPE2 * /* term */ ); + +static const char * +term_description(TERMTYPE2 *tp) { - bool result = FALSE; -#if USE_DATABASE - if (_nc_is_dir_path(path) && access(path, R_OK | X_OK) == 0) { - result = TRUE; - } -#endif -#if USE_TERMCAP - if (_nc_is_file_path(path) && access(path, R_OK) == 0) { - result = TRUE; + const char *desc; + + if (tp->term_names == 0 + || (desc = strrchr(tp->term_names, '|')) == 0 + || (*++desc == '\0')) { + desc = "(No description)"; } -#endif -#if USE_HASHED_DB - if (!result) { - char filename[PATH_MAX]; - if (_nc_is_file_path(path) && access(path, R_OK) == 0) { - result = TRUE; - } else if (make_db_name(filename, path, sizeof(filename))) { - if (_nc_is_file_path(filename) && access(filename, R_OK) == 0) { - result = TRUE; - } + + return desc; +} + +/* display a description for the type */ +static void +deschook(int db_index, int db_limit, const char *term_name, TERMTYPE2 *tp) +{ + (void) db_index; + (void) db_limit; + (void) printf("%-10s\t%s\n", term_name, term_description(tp)); +} + +static unsigned long +string_sum(const char *value) +{ + unsigned long result = 0; + + if ((intptr_t) value == (intptr_t) (-1)) { + result = ~result; + } else if (value) { + while (*value) { + result += UChar(*value); + ++value; } } -#endif return result; } -static void -deschook(const char *cn, TERMTYPE *tp) -/* display a description for the type */ +static unsigned long +checksum_of(TERMTYPE2 *tp) { - const char *desc; + unsigned long result = string_sum(tp->term_names); + unsigned i; - if ((desc = strrchr(tp->term_names, '|')) == 0 || *++desc == '\0') - desc = "(No description)"; + for (i = 0; i < NUM_BOOLEANS(tp); i++) { + result += (unsigned long) (tp->Booleans[i]); + } + for (i = 0; i < NUM_NUMBERS(tp); i++) { + result += (unsigned long) (tp->Numbers[i]); + } + for (i = 0; i < NUM_STRINGS(tp); i++) { + result += string_sum(tp->Strings[i]); + } + return result; +} - (void) printf("%-10s\t%s\n", cn, desc); +/* collect data, to sort before display */ +static void +sorthook(int db_index, int db_limit, const char *term_name, TERMTYPE2 *tp) +{ + TERMDATA *data = new_termdata(); + + data->db_index = db_index; + data->checksum = ((db_limit > 1) ? checksum_of(tp) : 0); + data->term_name = strmalloc(term_name); + data->description = strmalloc(term_description(tp)); } -#if USE_TERMCAP +#if NCURSES_USE_TERMCAP static void -show_termcap(char *buffer, - void (*hook) (const char *, TERMTYPE *tp)) +show_termcap(int db_index, int db_limit, char *buffer, DescHook hook) { - TERMTYPE data; + TERMTYPE2 data; char *next = strchr(buffer, ':'); char *last; char *list = buffer; @@ -146,27 +343,44 @@ show_termcap(char *buffer, if (last) ++last; - data.term_names = strdup(buffer); + memset(&data, 0, sizeof(data)); + data.term_names = strmalloc(buffer); while ((next = strtok(list, "|")) != 0) { if (next != last) - hook(next, &data); + hook(db_index, db_limit, next, &data); list = 0; } free(data.term_names); } #endif +#if NCURSES_USE_DATABASE +static char * +copy_entryname(DIRENT * src) +{ + size_t len = NAMLEN(src); + char *result = malloc(len + 1); + if (result == 0) + failed("copy entryname"); + memcpy(result, src->d_name, len); + result[len] = '\0'; + + return result; +} +#endif + static int typelist(int eargc, char *eargv[], - bool verbosity, - void (*hook) (const char *, TERMTYPE *tp)) + int verbosity, + DescHook hook) /* apply a function to each entry in given terminfo directories */ { int i; for (i = 0; i < eargc; i++) { -#if USE_DATABASE +#if NCURSES_USE_DATABASE if (_nc_is_dir_path(eargv[i])) { + char *cwd_buf = 0; DIR *termdir; DIRENT *subdir; @@ -175,36 +389,54 @@ typelist(int eargc, char *eargv[], (void) fprintf(stderr, "%s: can't open terminfo directory %s\n", _nc_progname, eargv[i]); - return (EXIT_FAILURE); - } else if (verbosity) + continue; + } + + if (verbosity) (void) printf("#\n#%s:\n#\n", eargv[i]); while ((subdir = readdir(termdir)) != 0) { - size_t len = NAMLEN(subdir); - char buf[PATH_MAX]; - char name_1[PATH_MAX]; + size_t cwd_len; + char *name_1; DIR *entrydir; DIRENT *entry; - strncpy(name_1, subdir->d_name, len)[len] = '\0'; - if (isDotname(name_1)) + name_1 = copy_entryname(subdir); + if (isDotname(name_1)) { + free(name_1); continue; + } + + cwd_len = NAMLEN(subdir) + strlen(eargv[i]) + 3; + cwd_buf = typeRealloc(char, cwd_len, cwd_buf); + if (cwd_buf == 0) + failed("realloc cwd_buf"); - (void) sprintf(buf, "%s/%s/", eargv[i], name_1); - if (chdir(buf) != 0) + assert(cwd_buf != 0); + + _nc_SPRINTF(cwd_buf, _nc_SLIMIT(cwd_len) + "%s/%s/", eargv[i], name_1); + free(name_1); + + if (chdir(cwd_buf) != 0) continue; entrydir = opendir("."); + if (entrydir == 0) { + perror(cwd_buf); + continue; + } while ((entry = readdir(entrydir)) != 0) { - char name_2[PATH_MAX]; - TERMTYPE lterm; + char *name_2; + TERMTYPE2 lterm; char *cn; int status; - len = NAMLEN(entry); - strncpy(name_2, entry->d_name, len)[len] = '\0'; - if (isDotname(name_2) || !_nc_is_file_path(name_2)) + name_2 = copy_entryname(entry); + if (isDotname(name_2) || !_nc_is_file_path(name_2)) { + free(name_2); continue; + } status = _nc_read_file_entry(name_2, <erm); if (status <= 0) { @@ -212,26 +444,34 @@ typelist(int eargc, char *eargv[], (void) fprintf(stderr, "%s: couldn't open terminfo file %s.\n", _nc_progname, name_2); - return (EXIT_FAILURE); + free(name_2); + continue; } /* only visit things once, by primary name */ cn = _nc_first_name(lterm.term_names); if (!strcmp(cn, name_2)) { /* apply the selected hook function */ - (*hook) (cn, <erm); + hook(i, eargc, cn, <erm); } - _nc_free_termtype(<erm); + _nc_free_termtype2(<erm); + free(name_2); } closedir(entrydir); } closedir(termdir); + if (cwd_buf != 0) + free(cwd_buf); + continue; } #if USE_HASHED_DB else { DB *capdbp; char filename[PATH_MAX]; + if (verbosity) + (void) printf("#\n#%s:\n#\n", eargv[i]); + if (make_db_name(filename, eargv[i], sizeof(filename))) { if ((capdbp = _nc_db_open(filename, FALSE)) != 0) { DBT key, data; @@ -239,7 +479,7 @@ typelist(int eargc, char *eargv[], code = _nc_db_first(capdbp, &key, &data); while (code == 0) { - TERMTYPE lterm; + TERMTYPE2 lterm; int used; char *have; char *cn; @@ -249,52 +489,59 @@ typelist(int eargc, char *eargv[], /* only visit things once, by primary name */ cn = _nc_first_name(lterm.term_names); /* apply the selected hook function */ - (*hook) (cn, <erm); - _nc_free_termtype(<erm); + hook(i, eargc, cn, <erm); + _nc_free_termtype2(<erm); } } code = _nc_db_next(capdbp, &key, &data); } _nc_db_close(capdbp); + continue; } } } -#endif -#endif -#if USE_TERMCAP +#endif /* USE_HASHED_DB */ +#endif /* NCURSES_USE_DATABASE */ +#if NCURSES_USE_TERMCAP #if HAVE_BSD_CGETENT - char *db_array[2]; - char *buffer = 0; + { + CGETENT_CONST char *db_array[2]; + char *buffer = 0; - if (verbosity) - (void) printf("#\n#%s:\n#\n", eargv[i]); + if (verbosity) + (void) printf("#\n#%s:\n#\n", eargv[i]); - db_array[0] = eargv[i]; - db_array[1] = 0; + db_array[0] = eargv[i]; + db_array[1] = 0; - if (cgetfirst(&buffer, db_array)) { - show_termcap(buffer, hook); - free(buffer); - while (cgetnext(&buffer, db_array)) { - show_termcap(buffer, hook); + if (cgetfirst(&buffer, db_array) > 0) { + show_termcap(i, eargc, buffer, hook); free(buffer); + while (cgetnext(&buffer, db_array) > 0) { + show_termcap(i, eargc, buffer, hook); + free(buffer); + } + cgetclose(); + continue; } } - cgetclose(); #else /* scan termcap text-file only */ if (_nc_is_file_path(eargv[i])) { char buffer[2048]; FILE *fp; + if (verbosity) + (void) printf("#\n#%s:\n#\n", eargv[i]); + if ((fp = fopen(eargv[i], "r")) != 0) { while (fgets(buffer, sizeof(buffer), fp) != 0) { if (*buffer == '#') continue; if (isspace(*buffer)) continue; - show_termcap(buffer, hook); + show_termcap(i, eargc, buffer, hook); } fclose(fp); } @@ -303,13 +550,18 @@ typelist(int eargc, char *eargv[], #endif } + if (hook == sorthook) { + show_termdata(eargc, eargv); + free_termdata(); + } + return (EXIT_SUCCESS); } static void usage(void) { - (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname); + (void) fprintf(stderr, "usage: %s [-ahsuUV] [-v n] [file...]\n", _nc_progname); ExitProgram(EXIT_FAILURE); } @@ -320,26 +572,27 @@ main(int argc, char *argv[]) bool direct_dependencies = FALSE; bool invert_dependencies = FALSE; bool header = FALSE; - int i; + char *report_file = 0; int code; int this_opt, last_opt = '?'; - int v_opt = 0; + unsigned v_opt = 0; + DescHook *hook = deschook; _nc_progname = _nc_rootname(argv[0]); - while ((this_opt = getopt(argc, argv, "0123456789ahuvUV")) != EOF) { + while ((this_opt = getopt(argc, argv, "0123456789ahsu:vU:V")) != -1) { /* handle optional parameter */ if (isdigit(this_opt)) { switch (last_opt) { case 'v': - v_opt = (this_opt - '0'); + v_opt = (unsigned) (this_opt - '0'); break; default: if (isdigit(last_opt)) v_opt *= 10; else v_opt = 0; - v_opt += (this_opt - '0'); + v_opt += (unsigned) (this_opt - '0'); last_opt = this_opt; } continue; @@ -351,14 +604,19 @@ main(int argc, char *argv[]) case 'h': header = TRUE; break; + case 's': + hook = sorthook; + break; case 'u': direct_dependencies = TRUE; + report_file = optarg; break; case 'v': v_opt = 1; break; case 'U': invert_dependencies = TRUE; + report_file = optarg; break; case 'V': puts(curses_version()); @@ -369,15 +627,15 @@ main(int argc, char *argv[]) } set_trace_level(v_opt); - if (direct_dependencies || invert_dependencies) { - if (freopen(argv[optind], "r", stdin) == 0) { + if (report_file != 0) { + if (freopen(report_file, "r", stdin) == 0) { (void) fflush(stdout); - fprintf(stderr, "%s: can't open %s\n", _nc_progname, argv[optind]); + fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file); ExitProgram(EXIT_FAILURE); } /* parse entries out of the source file */ - _nc_set_source(argv[optind]); + _nc_set_source(report_file); _nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK); } @@ -387,7 +645,7 @@ main(int argc, char *argv[]) for_entry_list(qp) { if (qp->nuses) { - int j; + unsigned j; (void) printf("%s:", _nc_first_name(qp->tterm.term_names)); for (j = 0; j < qp->nuses; j++) @@ -402,11 +660,13 @@ main(int argc, char *argv[]) /* maybe we want a reverse-dependency listing? */ if (invert_dependencies) { ENTRY *qp, *rp; - int matchcount; for_entry_list(qp) { - matchcount = 0; + int matchcount = 0; + for_entry_list(rp) { + unsigned i; + if (rp->nuses == 0) continue; @@ -431,70 +691,54 @@ main(int argc, char *argv[]) * If we get this far, user wants a simple terminal type listing. */ if (optind < argc) { - code = typelist(argc - optind, argv + optind, header, deschook); + code = typelist(argc - optind, argv + optind, header, hook); } else if (all_dirs) { DBDIRS state; int offset; int pass; - const char *path; char **eargv = 0; code = EXIT_FAILURE; for (pass = 0; pass < 2; ++pass) { - unsigned count = 0; + size_t count = 0; + const char *path; _nc_first_db(&state, &offset); while ((path = _nc_next_db(&state, &offset)) != 0) { - if (!is_database(path)) { - ; - } else if (eargv != 0) { - unsigned n; - int found = FALSE; - - /* eliminate duplicates */ - for (n = 0; n < count; ++n) { - if (!strcmp(path, eargv[n])) { - found = TRUE; - break; - } - } - if (!found) { - eargv[count] = strdup(path); - ++count; - } - } else { - ++count; + if (quick_prefix(path)) + continue; + if (pass) { + eargv[count] = strmalloc(path); } + ++count; } if (!pass) { - eargv = typeCalloc(char *, count + 1); + eargv = allocArgv(count); + if (eargv == 0) + failed("eargv"); } else { - code = typelist((int) count, eargv, header, deschook); - while (count-- > 0) - free(eargv[count]); - free(eargv); + code = typelist((int) count, eargv, header, hook); + freeArgv(eargv); } } } else { DBDIRS state; int offset; const char *path; - char *eargv[3]; - int count = 0; + char **eargv = allocArgv((size_t) 2); + size_t count = 0; + if (eargv == 0) + failed("eargv"); _nc_first_db(&state, &offset); - while ((path = _nc_next_db(&state, &offset)) != 0) { - if (is_database(path)) { - eargv[count++] = strdup(path); - break; - } + if ((path = _nc_next_db(&state, &offset)) != 0) { + if (!quick_prefix(path)) + eargv[count++] = strmalloc(path); } - eargv[count] = 0; - code = typelist(count, eargv, header, deschook); + code = typelist((int) count, eargv, header, hook); - while (count-- > 0) - free(eargv[count]); + freeArgv(eargv); } _nc_last_db();