+static PICS_HEAD *
+parse_xpm(char **data)
+{
+ int state = 0;
+ PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
+ RGB_NAME *by_name;
+ int n;
+ int cells = 0;
+ int color = 0;
+ int cpp = 1; /* chars per pixel */
+ int num[6];
+ int which = 0;
+ char ch;
+ const char *cs;
+ char *s;
+ char buf[BUFSIZ];
+ char arg1[BUFSIZ];
+ char arg2[BUFSIZ];
+ char arg3[BUFSIZ];
+ char **list = 0;
+
+ for (n = 0; data[n] != 0; ++n) {
+ if (strlen(s = data[n]) >= sizeof(buf) - 1)
+ continue;
+ switch (state) {
+ case 0:
+ if (match_c(s, " /* XPM */ ")) {
+ state = 1;
+ }
+ break;
+ case 1:
+ if (match_c(s, " static char * %s [] = %c ", arg1, &ch) &&
+ ch == L_CURLY) {
+ result->name = strdup(arg1);
+ state = 2;
+ }
+ break;
+ case 2:
+ if (match_c(s, " \" %d %d %d %d \" , ",
+ num + 0, num + 1, num + 2, num + 3) ||
+ match_c(s, " \" %d %d %d %d %d %d \" , ",
+ num + 0, num + 1, num + 2, num + 3, num + 4, num + 5)) {
+ result->wide = num[0];
+ result->high = num[1];
+ result->colors = num[2];
+ result->pairs = typeCalloc(PICS_PAIR, result->colors);
+ cells = (result->wide * result->high);
+ result->cells = typeCalloc(PICS_CELL, cells);
+ list = typeCalloc(char *, result->colors);
+ cpp = num[3];
+ state = 3;
+ }
+ break;
+ case 3:
+ if (!match_colors(s, cpp, arg1, arg2, arg3)) {
+ break;
+ }
+ list[color] = strdup(arg1);
+ if ((by_name = lookup_rgb(arg3)) != 0) {
+ result->pairs[color].fg = by_name->value;
+ } else if (*arg3 == '#') {
+ char *rgb = arg3 + 1;
+ unsigned long value = strtoul(rgb, &s, 16);
+ switch ((int) strlen(rgb)) {
+ case 6:
+ break;
+ case 12:
+ value = (((value >> 24) & 0xff0000L)
+ | ((value >> 16) & 0xff00L)
+ | ((value >> 8) & 0xffL));
+ break;
+ default:
+ printf("unexpected rgb value %s\n", rgb);
+ break;
+ }
+ result->pairs[color].fg = (int) value;
+ } else {
+ result->pairs[color].fg = 0; /* actually an error */
+ }
+ if (++color >= result->colors)
+ state = 4;
+ break;
+ case 4:
+ if (*(cs = skip_cs(s)) == '"') {
+ ++cs;
+ while (*cs != '\0' && *cs != '"') {
+ int c;
+
+ for (c = 0; c < result->colors; ++c) {
+ if (!strncmp(cs, list[c], (size_t) cpp)) {
+ result->cells[which].ch = list[c][0];
+ result->cells[which].fg = c;
+ break;
+ }
+ }
+
+ if (result->cells[which].ch == 0) {
+ result->cells[which].ch = '?';
+ result->cells[which].fg = 0;
+ }
+
+ if (++which >= cells) {
+ state = 5;
+ break;
+ }
+ for (c = cpp; c > 0; --c, ++cs) ;
+ }
+ }
+ break;
+ }
+ }
+
+ if (result && list) {
+ for (n = 0; n < result->colors; ++n)
+ free(list[n]);
+ free(list);
+ }
+
+ if (state < 5) {
+ free_pics_head(result);
+ result = 0;
+ }
+
+ return result;
+}
+
+/*
+ * The obscurely-named "convert" is provided by ImageMagick
+ */
+static PICS_HEAD *
+parse_img(const char *filename)
+{
+ char *cmd = malloc(strlen(filename) + 256);
+ FILE *pp;
+ char buffer[BUFSIZ];
+ bool failed = FALSE;
+ PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
+ int pic_x = 0;
+ int pic_y = 0;
+ int width = in_curses ? COLS : 80;
+
+ sprintf(cmd, "identify \"%s\"", filename);
+
+ if ((pp = popen(cmd, "r")) != 0) {
+ if (fgets(buffer, sizeof(buffer), pp) != 0) {
+ size_t n = strlen(filename);
+ if (strlen(buffer) > n &&
+ !strncmp(buffer, filename, n) &&
+ isspace(UChar(buffer[n])) &&
+ sscanf(skip_word(buffer + n), " %dx%d ", &pic_x, &pic_y) == 2) {
+ /* distort image to make it show normally on terminal */
+ pic_x = (166 * pic_x) / 100;
+ } else {
+ pic_x = pic_y = 0;
+ }
+ }
+ pclose(pp);
+ }
+ 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);
+
+ if ((pp = popen(cmd, "r")) != 0) {
+ int count = 0;
+ int col = 0;
+ int row = 0;
+ int len = 0;
+ while (fgets(buffer, sizeof(buffer), pp) != 0) {
+ if (strlen(buffer) > 160) { /* 80 columns would be enough */
+ failed = TRUE;
+ break;
+ }
+ if (count++ == 0) {
+ if (match_c(buffer,
+ "# ImageMagick pixel enumeration: %d,%d,%d,srgba ",
+ &col, &row, &len)) {
+ result->name = strdup(filename);
+ result->wide = col;
+ result->high = row;
+ result->colors = 256;
+ result->pairs = typeCalloc(PICS_PAIR, result->colors);
+ result->cells = typeCalloc(PICS_CELL, (size_t) (col * row));
+ } else {
+ failed = TRUE;
+ break;
+ }
+ } else {
+ /* subsequent lines begin "col,row: (r,g,b,a) #RGB" */
+ int r, g, b, nocolor;
+ unsigned check;
+ int which, c;
+ char *t;
+ char *s = t = strchr(buffer, '#');
+ 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 > 255 ||
+ g > 255 ||
+ b > 255 ||
+ check != (unsigned) ((r << 16) | (g << 8) | b)) {
+ failed = TRUE;
+ break;
+ }
+ for (c = 0; c < result->colors; ++c) {
+ if (result->pairs[c].fg == (int) check) {
+ break;
+ } else if (result->pairs[c].fg == 0) {
+ result->pairs[c].fg = (int) check;
+ break;
+ }
+ }
+ if (c >= result->colors) {
+ int more = (result->colors * 3) / 2;
+ PICS_PAIR *p = typeRealloc(PICS_PAIR, more, result->pairs);
+ if (p != 0) {
+ result->colors = more;
+ result->pairs = p;
+ result->pairs[c].fg = (int) check;
+ result->pairs[c].bg = 0;
+ while (++c < more) {
+ result->pairs[c].fg = 0;
+ result->pairs[c].bg = 0;
+ }
+ }
+ }
+ which = col + (row * result->wide);
+ result->cells[which].ch = ((in_curses ||
+ check == 0xffffff)
+ ? ' '
+ : '#');
+ result->cells[which].fg = (c < result->colors) ? c : -1;
+ } else {
+ failed = TRUE;
+ break;
+ }
+ }
+ }
+ pclose(pp);
+ if (!failed) {
+ for (len = result->colors; len > 3; len--) {
+ if (result->pairs[len - 1].fg == 0) {
+ result->colors = len - 1;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ finish:
+ free(cmd);
+
+ if (failed) {
+ free_pics_head(result);
+ result = 0;
+ }
+
+ return result;
+}
+
+static PICS_HEAD *
+read_picture(const char *filename, char **data)
+{
+ PICS_HEAD *pics;
+ if ((pics = parse_xbm(data)) == 0) {
+ if ((pics = parse_xpm(data)) == 0) {
+ if ((pics = parse_img(filename)) == 0) {
+ free_data(data);
+ giveup("unexpected file-format for \"%s\"", filename);
+ }
+ }
+ }
+ return pics;
+}
+