+static bool debugging = FALSE;
+static bool quiet = FALSE;
+static int slow_time = -1;
+static RGB_NAME *rgb_table;
+static RGB_DATA *all_colors;
+static HOW_MUCH how_much;
+
+static int reading_last;
+static int reading_size;
+static FG_NODE *reading_ncols;
+
+#if HAVE_TSEARCH
+static void *reading_ntree;
+#endif
+
+#if HAVE_ALLOC_PAIR && HAVE_INIT_EXTENDED_COLOR
+#define USE_EXTENDED_COLORS 1
+static bool use_extended_pairs = FALSE;
+static bool use_extended_colors = FALSE;
+#else
+#define USE_EXTENDED_COLORS 0
+#endif
+
+static void
+logmsg(const char *fmt,...)
+{
+ if (logfp != 0) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(logfp, fmt, ap);
+ va_end(ap);
+ fputc('\n', logfp);
+ fflush(logfp);
+ }
+}
+
+static void
+logmsg2(const char *fmt,...)
+{
+ if (logfp != 0) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(logfp, fmt, ap);
+ va_end(ap);
+ fflush(logfp);
+ }
+}
+
+static void
+close_log(void)
+{
+ if (logfp != 0) {
+ logmsg("Allocations:");
+ logmsg("%8ld file", (long) how_much.file);
+ logmsg("%8ld name", (long) how_much.name);
+ logmsg("%8ld list", (long) how_much.list);
+ logmsg("%8ld data", (long) how_much.data);
+ logmsg("%8ld head", (long) how_much.head);
+ logmsg("%8ld pair", (long) how_much.pair);
+ logmsg("%8ld cell", (long) how_much.cell);
+ logmsg("%8ld window", LINES * COLS * (long) sizeof(NCURSES_CH_T));
+ fclose(logfp);
+ logfp = 0;
+ }
+}
+
+static void
+cleanup(int code)
+{
+ stop_curses();
+ close_log();
+ ExitProgram(code);
+ /* NOTREACHED */
+}
+
+static void
+failed(const char *msg)
+{
+ int save = errno;
+ perror(msg);
+ logmsg("failed with %s", strerror(save));
+ cleanup(EXIT_FAILURE);
+}
+
+static void
+warning(const char *fmt,...)
+{
+ if (logfp != 0) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(logfp, fmt, ap);
+ va_end(ap);
+ fputc('\n', logfp);
+ fflush(logfp);
+ } else {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ cleanup(EXIT_FAILURE);
+ }
+}
+
+static void
+free_data(char **data)
+{
+ if (data != 0) {
+ free(data[0]);
+ free(data);
+ }
+}
+
+static PICS_HEAD *
+free_pics_head(PICS_HEAD * pics)
+{
+ if (pics != 0) {
+ free(pics->fgcol);
+ free(pics->cells);
+ free(pics->name);
+ free(pics);
+ pics = 0;
+ }
+ return pics;
+}
+
+static void
+begin_c_values(int size)
+{
+ reading_last = 0;
+ reading_size = size;
+ reading_ncols = typeCalloc(FG_NODE, size + 1);
+ how_much.pair += (sizeof(FG_NODE) * (size_t) size);
+ /* black is always the first slot, to work around P2I/I2P logic */
+ gather_c_values(0);
+}
+
+#if HAVE_TSEARCH
+static int
+compare_c_values(const void *p, const void *q)
+{
+ const int a = P2I(p);
+ const int b = P2I(q);
+ return (reading_ncols[a].fgcol - reading_ncols[b].fgcol);
+}
+
+#ifdef DEBUG_TSEARCH
+static void
+check_c_values(int ln)
+{
+ static int oops = 5;
+ FG_NODE **ft;
+ int n;
+ for (n = 0; n < reading_last; ++n) {
+ ft = tfind(I2P(n), &reading_ntree, compare_c_values);
+ if (ft != 0) {
+ int q = P2I(*ft);
+ if (reading_ncols[q].fgcol != reading_ncols[n].fgcol) {
+ logmsg("@%d, %d:%d (%d) %d %d fgcol %06X %06X", ln, n,
+ reading_last - 1,
+ reading_size,
+ q, n,
+ reading_ncols[n].fgcol,
+ reading_ncols[q].fgcol);
+ }
+ } else {
+ logmsg("@%d, %d:%d (%d) ? %d null %06X", ln, n,
+ reading_last - 1,
+ reading_size,
+ n,
+ reading_ncols[n].fgcol);
+ if (oops-- <= 0)
+ return;
+ }
+ }
+}
+#else
+#define check_c_values(n) /* nothing */
+#endif
+#endif
+
+static int
+gather_c_values(int fg)
+{
+ int found = -1;
+#if HAVE_TSEARCH
+ FG_NODE **ft;
+ int next = reading_last;
+
+ reading_ncols[next].fgcol = fg;
+ reading_ncols[next].count = 0;
+
+ check_c_values(__LINE__);
+ if ((ft = tfind(I2P(next), &reading_ntree, compare_c_values)) != 0) {
+ found = P2I(*ft);
+ } else {
+ if (reading_last + 2 >= reading_size) {
+ int more = ((MAX(reading_last, reading_size) + 2) * 3) / 2;
+ int last = reading_last + 1;
+ FG_NODE *p = typeRealloc(FG_NODE, more, reading_ncols);
+ if (p == 0)
+ goto done;
+
+ reading_size = more;
+ reading_ncols = p;
+ memset(reading_ncols + last, 0,
+ sizeof(FG_NODE) * (size_t) (more - last));
+ check_c_values(__LINE__);
+ }
+ ++reading_last;
+ how_much.pair += sizeof(FG_NODE);
+ if ((ft = tsearch(I2P(next), &reading_ntree, compare_c_values)) != 0) {
+ found = P2I(*ft);
+ if (found != next)
+ logmsg("OOPS expected slot %d, got %d", next, found);
+ debugmsg("allocated color #%d as #%06X", next, fg);
+ check_c_values(__LINE__);
+ }
+ }
+#else
+ int n;
+
+ for (n = 0; n < reading_last; ++n) {
+ if (reading_ncols[n].fgcol == fg) {
+ found = n;
+ break;
+ }
+ }
+ if (found < 0) {
+ if (reading_last + 2 >= reading_size) {
+ int more = ((reading_last + 2) * 3) / 2;
+ FG_NODE *p = typeRealloc(FG_NODE, more, reading_ncols);
+ if (p == 0)
+ goto done;
+
+ how_much.pair -= (sizeof(FG_NODE) * (size_t) reading_size);
+ how_much.pair += (sizeof(FG_NODE) * (size_t) more);
+ reading_size = more;
+ reading_ncols = p;
+ memset(reading_ncols + reading_last, 0,
+ sizeof(FG_NODE) * (size_t) (more - reading_last));
+ }
+ reading_ncols[reading_last].fgcol = fg;
+ found = reading_last++;
+ }
+#endif
+ done:
+ return found;
+}
+
+static void
+finish_c_values(PICS_HEAD * head)
+{
+ head->colors = reading_last;
+ head->fgcol = reading_ncols;
+
+ reading_last = 0;
+ reading_size = 0;
+ reading_ncols = 0;
+}
+
+#if HAVE_TSEARCH && HAVE_TDESTROY
+static void
+never_free(void *node GCC_UNUSED)
+{
+}
+#endif
+
+static void
+dispose_c_values(void)
+{
+#if HAVE_TSEARCH
+ if (reading_ntree != 0) {
+#if HAVE_TDESTROY
+ tdestroy(reading_ntree, never_free);
+#else
+ int n;
+ for (n = 0; n < reading_last; ++n) {
+ tdelete(I2P(n), &reading_ntree, compare_c_values);
+ }
+#endif
+ reading_ntree = 0;
+ }
+#endif
+ if (reading_ncols != 0) {
+ free(reading_ncols);
+ reading_ncols = 0;
+ }
+ reading_last = 0;
+ reading_size = 0;
+}
+
+static int
+is_file(const char *filename, struct stat *sb)
+{
+ int result = 0;
+ if (stat(filename, sb) == 0
+ && (sb->st_mode & S_IFMT) == S_IFREG
+ && sb->st_size != 0) {
+ result = 1;
+ }
+ debugmsg("is_file(%s) %d", filename, result);
+ return result;
+}