+ _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)
+{
+ static const char suffix[] = DBM_SUFFIX;
+
+ bool result = FALSE;
+ 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)) {
+ _nc_STRCPY(dst, src, PATH_MAX);
+ } else {
+ _nc_SPRINTF(dst, _nc_SLIMIT(PATH_MAX) "%.*s%s",
+ (int) (PATH_MAX - sizeof(suffix)),
+ src, suffix);
+ }
+ result = TRUE;
+ }
+ return result;
+}
+#endif
+
+typedef void (DescHook) (int /* db_index */ ,
+ int /* db_limit */ ,
+ const char * /* term_name */ ,
+ TERMTYPE2 * /* term */ );
+
+static const char *
+term_description(TERMTYPE2 *tp)
+{
+ const char *desc;
+
+ if (tp->term_names == 0
+ || (desc = strrchr(tp->term_names, '|')) == 0
+ || (*++desc == '\0')) {
+ desc = "(No description)";
+ }
+
+ 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;
+ }
+ }
+ return result;
+}
+
+static unsigned long
+checksum_of(TERMTYPE2 *tp)
+{
+ unsigned long result = string_sum(tp->term_names);
+ unsigned i;
+
+ 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;
+}
+
+/* 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 NCURSES_USE_TERMCAP
+/*
+ * Check if the buffer contents are printable ASCII, ensuring that we do not
+ * accidentally pick up incompatible binary content from a hashed database.
+ */
+static bool
+is_termcap(char *buffer)
+{
+ bool result = TRUE;
+ while (*buffer != '\0') {
+ int ch = UChar(*buffer++);
+ if (ch == '\t')
+ continue;
+ if (ch < ' ' || ch > '~') {
+ result = FALSE;
+ break;
+ }
+ }
+ return result;
+}
+
+static void
+show_termcap(int db_index, int db_limit, char *buffer, DescHook hook)
+{
+ TERMTYPE2 data;
+ char *next = strchr(buffer, ':');
+ char *last;
+ char *list = buffer;
+
+ if (next)
+ *next = '\0';
+
+ last = strrchr(buffer, '|');
+ if (last)
+ ++last;
+
+ memset(&data, 0, sizeof(data));
+ data.term_names = strmalloc(buffer);
+ while ((next = strtok(list, "|")) != 0) {
+ if (next != last)
+ hook(db_index, db_limit, next, &data);
+ list = 0;
+ }
+ free(data.term_names);