/****************************************************************************
- * Copyright (c) 2017,2018 Free Software Foundation, Inc. *
+ * Copyright 2018-2021,2022 Thomas E. Dickey *
+ * Copyright 2017,2018 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 *
* authorization. *
****************************************************************************/
/*
- * $Id: picsmap.c,v 1.118 2018/03/24 22:37:42 tom Exp $
+ * $Id: picsmap.c,v 1.145 2022/04/16 18:21:05 tom Exp $
*
* Author: Thomas E. Dickey
*
#define P2I(n) (((int)(my_intptr_t)(n)) - 1)
#define I2P(n) (void *)(my_intptr_t)((n) + 1)
-#define stop_curses() if (in_curses) endwin()
+#define pause_curses() if (in_curses) stop_curses()
#define debugmsg if (debugging) logmsg
#define debugmsg2 if (debugging) logmsg2
-static void cleanup(int) GCC_NORETURN;
-static void giveup(const char *fmt,...) GCC_PRINTFLIKE(1, 2);
-static void logmsg(const char *fmt,...) GCC_PRINTFLIKE(1, 2);
-static void logmsg2(const char *fmt,...) GCC_PRINTFLIKE(1, 2);
-static void warning(const char *fmt,...) GCC_PRINTFLIKE(1, 2);
+static GCC_NORETURN void cleanup(int);
+static void giveup(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
+static void logmsg(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
+static void logmsg2(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
+static void warning(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
static int gather_c_values(int);
static FILE *logfp = 0;
static void *reading_ntree;
#endif
-#if HAVE_ALLOC_PAIR && HAVE_INIT_EXTENDED_COLOR
+#if HAVE_ALLOC_PAIR && USE_EXTENDED_COLOR
#define USE_EXTENDED_COLORS 1
static bool use_extended_pairs = FALSE;
static bool use_extended_colors = FALSE;
#endif
static void
-logmsg(const char *fmt,...)
+logmsg(const char *fmt, ...)
{
if (logfp != 0) {
va_list ap;
}
static void
-logmsg2(const char *fmt,...)
+logmsg2(const char *fmt, ...)
{
if (logfp != 0) {
va_list ap;
static void
cleanup(int code)
{
- stop_curses();
+ pause_curses();
close_log();
ExitProgram(code);
/* NOTREACHED */
}
static void
-warning(const char *fmt,...)
+warning(const char *fmt, ...)
{
if (logfp != 0) {
va_list ap;
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
struct stat sb;
if (!quiet) {
- stop_curses();
+ pause_curses();
printf("** %s\n", filename);
}
if (is_file(filename, &sb)) {
size_t size = (size_t) sb.st_size;
char *blob = typeCalloc(char, size + 1);
- bool had_line = TRUE;
bool binary = FALSE;
- unsigned j;
unsigned k = 0;
result = typeCalloc(char *, size + 1);
FILE *fp = fopen(filename, "r");
if (fp != 0) {
logmsg("opened %s", filename);
+
if (fread(blob, sizeof(char), size, fp) == size) {
+ bool had_line = TRUE;
+ unsigned j;
+
for (j = 0; (size_t) j < size; ++j) {
if (blob[j] == '\0' ||
(UChar(blob[j]) < 32 &&
};
size_t n;
- stop_curses();
+ pause_curses();
fflush(stdout);
for (n = 0; n < SIZEOF(msg); n++)
}
static void
-giveup(const char *fmt,...)
+giveup(const char *fmt, ...)
{
va_list ap;
- stop_curses();
+ pause_curses();
fflush(stdout);
va_start(ap, fmt);
{
static const char *data_dir = DATA_DIR;
char **result = 0;
- char *full_name = malloc(strlen(data_dir) + 20 + strlen(filename));
+ size_t last = strlen(filename);
+ size_t need = (strlen(data_dir) + 20 + last);
+ char *full_name = malloc(need);
char *s;
struct stat sb;
*(s = full_name) = '\0';
if (tries & 1) {
if (strchr(filename, '/') == 0) {
- sprintf(full_name, "%s/", data_dir);
+ _nc_SPRINTF(full_name, _nc_SLIMIT(need) "%s/", data_dir);
} else {
continue;
}
}
s += strlen(s);
+ if (((size_t) (s - full_name) + last + 1) >= need)
+ continue;
- strcpy(s, filename);
+ _nc_STRCAT(full_name, filename, need);
if (tries & 4) {
char *t = s;
+ char *tc;
int num;
char chr;
int found = 0;
if (*t == '-') {
if (sscanf(t, "-%d%c", &num, &chr) == 2 &&
chr == 'c' &&
- !(strncmp) (strchr(t, chr), "color", 5)) {
+ (tc = strchr(t, chr)) != 0 &&
+ !(strncmp) (tc, "color", 5)) {
found = 1;
}
break;
}
if (found && (t != s)
&& (strncmp) (s, "xterm", (size_t) (t - s))) {
- sprintf(s, "xterm%s", filename + (t - s));
+ _nc_SPRINTF(s, _nc_SLIMIT(need - (size_t) (s - full_name))
+ "xterm%s", filename + (t - s));
} else {
continue;
}
}
- s += strlen(s);
if (tries & 2) {
int len = (int) strlen(filename);
if (len <= 4 || strcmp(filename + len - 4, ".dat")) {
- strcpy(s, ".dat");
+ _nc_STRCAT(full_name, ".dat", need);
} else {
continue;
}
if ((power2 != COLORS) || ((shift % 3) != 0)) {
if (all_colors == 0) {
init_palette(getenv("TERM"));
- }
- if (all_colors == 0) {
- giveup("With %d colors, you need a palette-file", COLORS);
+ if (all_colors == 0) {
+ giveup("With %d colors, you need a palette-file", COLORS);
+ }
}
}
}
return value;
}
-static int match_c(const char *, const char *,...) GCC_SCANFLIKE(2,3);
+static int match_c(const char *, const char *, ...) GCC_SCANFLIKE(2,3);
static char *
skip_s(char *s)
}
static int
-match_c(const char *source, const char *pattern,...)
+match_c(const char *source, const char *pattern, ...)
{
int limit = (int) strlen(source);
const char *last_s = source + limit;
int ch;
int *ip;
char *cp;
+ float *fp;
long lv;
va_start(ap, pattern);
continue;
}
/* %c, %d, %s are like sscanf except for special treatment of blanks */
- if (ch == '%' && *pattern != '\0' && strchr("cdnsx", *pattern)) {
+ if (ch == '%' && *pattern != '\0' && strchr("%cdnfsx", *pattern)) {
bool found = FALSE;
ch = *pattern++;
switch (ch) {
+ case '%':
+ source++;
+ break;
case 'c':
cp = va_arg(ap, char *);
do {
goto finish;
}
break;
+ case 'f':
+ /* floating point for pixels... */
+ fp = va_arg(ap, float *);
+ lv = strtol(source, &cp, 10);
+ if (cp == 0 || cp == source)
+ goto finish;
+ *fp = (float) lv;
+ source = cp;
+ if (*source == '.') {
+ lv = strtol(++source, &cp, 10);
+ if (cp == 0 || cp == source)
+ goto finish;
+ {
+ float scale = 1.0f;
+ int digits = (int) (cp - source);
+ while (digits-- > 0) {
+ scale *= 10.0f;
+ }
+ *fp += (float) lv / scale;
+ }
+ source = cp;
+ }
+ break;
case 'n':
/* not really sscanf... */
limit = *va_arg(ap, int *);
arg1[cpp] = '\0';
result = 1;
} else {
- char *t;
const char *s = skip_cs(source);
size_t have = strlen(source);
memcpy(arg1, s, (size_t) cpp);
s += cpp;
while (*s++ == '\t') {
+ char *t;
for (t = arg2; (*s != '\0') && strchr("\t\"", *s) == 0;) {
if (*s == ' ') {
s = skip_cs(s);
return result;
}
+#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
+
+static int
+CaselessCmp(const char *a, const char *b)
+{ /* strcasecmp isn't portable */
+ while (*a && *b) {
+ int cmp = LOWERCASE(*a) - LOWERCASE(*b);
+ if (cmp != 0)
+ break;
+ a++, b++;
+ }
+ return LOWERCASE(*a) - LOWERCASE(*b);
+}
+
static RGB_NAME *
lookup_rgb(const char *name)
{
if (rgb_table != 0) {
int n;
for (n = 0; rgb_table[n].name != 0; ++n) {
- if (!strcasecmp(name, rgb_table[n].name)) {
+ if (!CaselessCmp(name, rgb_table[n].name)) {
result = &rgb_table[n];
break;
}
{
int n;
int state = 0;
- char buf[BUFSIZ];
+ char buf[2048];
int num;
char ch;
char *s;
case 0:
case 1:
case 2:
- if (sscanf(s, "#define %s %d%c", buf, &num, &ch) >= 2) {
+ if (sscanf(s, "#define %1024s %d%c", buf, &num, &ch) >= 2) {
if ((t = strstr(buf, "_width")) != 0) {
state |= 1;
result->wide = (short) bytes_of(num);
} else if ((t = strstr(buf, "_height")) != 0) {
state |= 2;
result->high = (short) num;
+ } else {
+ break;
}
*t = '\0';
if (result->name) {
}
break;
case 3:
- if (sscanf(s, "static char %[^_ ]_bits[]%c", buf, &ch) >= 1) {
+ if (sscanf(s, "static char %1024[^_ ]_bits[]%c", buf, &ch) >= 1) {
if (strcmp(result->name, buf)) {
goto finish;
}
if (num_colors >= result->colors) {
finish_c_values(result);
state = 4;
- if (list != 0 && list[0] == 0)
+ if (list[0] == 0)
list[0] = strdup("\033");
}
break;
static PICS_HEAD *
parse_img(const char *filename)
{
- char *cmd = malloc(strlen(filename) + 256);
+ size_t need = strlen(filename) + 256;
+ char *cmd = malloc(need);
FILE *pp;
char buffer[BUFSIZ];
char dummy[BUFSIZ];
int pic_y = 0;
int width = in_curses ? COLS : 80;
- sprintf(cmd, "identify \"%s\"", filename);
+ _nc_SPRINTF(cmd, _nc_SLIMIT(need) "identify \"%s\"", filename);
if (quiet)
- strcat(cmd, " 2>/dev/null");
+ _nc_STRCAT(cmd, " 2>/dev/null", need);
logmsg("...opening pipe to %s", cmd);
if (pic_x <= 0 || pic_y <= 0)
goto finish;
- sprintf(cmd, "convert " "-resize %dx%d\\! " "-thumbnail %dx \"%s\" "
- "-define txt:compliance=SVG txt:-",
- pic_x, pic_y, width, filename);
+ _nc_SPRINTF(cmd, _nc_SLIMIT(need)
+ "convert " "-resize %dx%d\\! " "-thumbnail %dx \"%s\" "
+ "-define txt:compliance=SVG txt:-",
+ pic_x, pic_y, width, filename);
if (quiet)
- strcat(cmd, " 2>/dev/null");
+ _nc_STRCAT(cmd, " 2>/dev/null", need);
logmsg("...opening pipe to %s", cmd);
if ((pp = popen(cmd, "r")) != 0) {
break;
}
} else {
- /* subsequent lines begin "col,row: (r,g,b,a) #RGB" */
+ /*
+ * subsequent lines begin "col,row: (r,g,b,a) #RGB".
+ * Those r/g/b could be integers (0..255) or float-percentages.
+ */
int r, g, b, nocolor;
+ float rf, gf, bf;
unsigned check;
- int which, c;
char *t;
char *s = t = strchr(buffer, '#');
+ bool matched = FALSE;
+
if (s != 0) {
/* after the "#RGB", there are differences - just ignore */
while (*s != '\0' && !isspace(UChar(*s)))
++s;
*++s = '\0';
}
+
if (match_c(buffer,
"%d,%d: (%d,%d,%d,%d) #%x ",
&col, &row,
&r, &g, &b, &nocolor,
&check)) {
- if ((s - t) > 8) /* 6 hex digits vs 8 */
- check /= 256;
- if (r > MaxRGB ||
- g > MaxRGB ||
- b > MaxRGB ||
- check != (unsigned) ((r << 16) | (g << 8) | b)) {
+ matched = TRUE;
+ } else if (match_c(buffer,
+ "%d,%d: (%f%%,%f%%,%f%%,%d) #%x ",
+ &col, &row,
+ &rf, &gf, &bf, &nocolor,
+ &check) ||
+ match_c(buffer,
+ "%d,%d: (%f%%,%f%%,%f%%) #%x ",
+ &col, &row,
+ &rf, &gf, &bf,
+ &check)) {
+ matched = TRUE;
+
+#define fp_fix(n) (int) (MaxRGB * (((n) > 100.0 ? 100.0 : (n)) / 100.0))
+
+ r = fp_fix(rf);
+ g = fp_fix(gf);
+ b = fp_fix(bf);
+ }
+ if ((s - t) > 8) /* 6 hex digits vs 8 */
+ check /= 256;
+ if (matched) {
+ int which, c;
+ int want_r = (check >> 16) & 0xff;
+ int want_g = (check >> 8) & 0xff;
+ int want_b = (check >> 0) & 0xff;
+
+#define fp_err(tst,ref) ((tst > MaxRGB) || ((tst - ref)*(tst - ref)) > 4)
+
+ if (fp_err(r, want_r) ||
+ fp_err(g, want_g) ||
+ fp_err(b, want_b)) {
okay = FALSE;
break;
}
static void
init_display(const char *palette_path, int opt_d)
{
+ (void) opt_d;
if (isatty(fileno(stdout))) {
in_curses = TRUE;
+ setlocale(LC_ALL, "");
initscr();
cbreak();
noecho();
init_palette(palette_path);
}
scrollok(stdscr, FALSE);
- exit_curses();
+ stop_curses();
}
}
{
int y, x;
int n;
- int my_pair, my_color;
debugmsg("called show_picture");
logmsg("...using %dx%d screen", LINES, COLS);
if (has_colors()) {
logmsg("...using %d colors", pics->colors);
for (n = 0; n < pics->colors; ++n) {
- my_pair = (n + 1);
- my_color = map_color(fg_color(pics, n));
+ int my_pair = (n + 1);
+ int my_color = map_color(fg_color(pics, n));
#if USE_EXTENDED_COLORS
if (use_extended_pairs) {
init_extended_pair(my_pair, my_color, my_color);
if (y >= LINES)
break;
move(y, 0);
+
for (x = 0; x < pics->wide; ++x) {
+ int my_pair;
+
if (x >= COLS)
break;
n = (y * pics->wide + x);
static void
report_colors(PICS_HEAD * pics)
{
- int j, k;
- int high;
- int wide = 4;
int accum;
double level;
+ int j;
int shift;
int total;
char buffer[256];
*/
if (debugging && (pics->colors < 1000)) {
int digits = 0;
+ int high;
+ int wide = 4;
for (j = pics->colors; j != 0; j /= 10) {
++digits;
if (j < 10)
++digits;
}
+ if (digits > 8)
+ digits = 8;
logmsg("These colors were used:");
high = (pics->colors + wide - 1) / wide;
for (j = 0; j < high && j < pics->colors; ++j) {
+ int k;
char *s = buffer;
*s = '\0';
for (k = 0; k < wide; ++k) {
int n = j + (k * high);
+ size_t want = (sizeof(buffer) - (size_t) (s - buffer));
+ if (want < 100 || want >= sizeof(buffer))
+ break;
if (n >= pics->colors)
break;
if (k) {
*s++ = ' ';
if (digits < 8) {
- sprintf(s, "%*s", 8 - digits, " ");
+ _nc_SPRINTF(s, _nc_SLIMIT(want) "%*s", 8 - digits,
+ " ");
s += strlen(s);
}
}
if (pics->fgcol[n].fgcol >= 0) {
- sprintf(s, "%3d #%06X %*d", n,
- pics->fgcol[n].fgcol,
- digits, pics->fgcol[n].count);
+ _nc_SPRINTF(s, _nc_SLIMIT(want) "%3d #%06X %*d", n,
+ pics->fgcol[n].fgcol,
+ digits, pics->fgcol[n].count);
} else {
- sprintf(s, "%3d (empty) %*d", n,
- digits, pics->fgcol[n].count);
+ _nc_SPRINTF(s, _nc_SLIMIT(want) "%3d (empty) %*d", n,
+ digits, pics->fgcol[n].count);
}
s += strlen(s);
if ((s - buffer) > 100)