ncurses 6.0 - patch 20170527
[ncurses.git] / test / picsmap.c
1 /****************************************************************************
2  * Copyright (c) 2017 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * $Id: picsmap.c,v 1.29 2017/05/28 00:19:58 tom Exp $
30  *
31  * Author: Thomas E. Dickey
32  *
33  * A little more interesting than "dots", read a simple image into memory and
34  * measure the time taken to paint it normally vs randomly.
35  *
36  * TODO write cells/second to stderr (or log)
37  * TODO write picture left-to-right/top-to-bottom
38  * TODO write picture randomly
39  * TODO add one-shot option vs repeat-count before exiting
40  * TODO add option for assumed palette of terminal
41  * TODO add option for init_color
42  * TODO add option for init_color vs init_extended_color
43  * TODO add option for init_pair vs alloc_pair
44  * TODO use pad to allow pictures larger than screen
45  */
46 #include <test.priv.h>
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50
51 #define  L_BLOCK '['
52 #define  R_BLOCK ']'
53
54 #define  L_CURLY '{'
55 #define  R_CURLY '}'
56
57 typedef struct {
58     int ch;                     /* nominal character to display */
59     int fg;                     /* foreground color */
60 } PICS_CELL;
61
62 typedef struct {
63     int fg;
64     int bg;
65 } PICS_PAIR;
66
67 typedef struct {
68     char *name;
69     int high;
70     int wide;
71     int colors;
72     PICS_PAIR *pairs;
73     PICS_CELL *cells;
74 } PICS_HEAD;
75
76 typedef struct {
77     const char *name;
78     int value;
79 } RGB_DATA;
80
81 static bool in_curses = FALSE;
82 static RGB_DATA *rgb_table;
83
84 static void
85 free_data(char **data)
86 {
87     free(data[0]);
88     free(data);
89 }
90
91 static void
92 free_pics_head(PICS_HEAD * pics)
93 {
94     free(pics->pairs);
95     free(pics->cells);
96     free(pics);
97 }
98
99 /*
100  * Simplify reading xbm/xpm files by first making an array of lines.  Blank
101  * lines are filtered out.
102  */
103 static char **
104 read_file(const char *filename)
105 {
106     char **result = 0;
107     struct stat sb;
108
109     printf("** %s\n", filename);
110     if (stat(filename, &sb) == 0
111         && (sb.st_mode & S_IFMT) == S_IFREG
112         && sb.st_size != 0) {
113         size_t size = (size_t) sb.st_size;
114         char *blob = typeMalloc(char, size + 1);
115         bool had_line = TRUE;
116         unsigned j;
117         unsigned k = 0;
118
119         result = typeCalloc(char *, size + 1);
120         if (blob != 0 && result != 0) {
121             FILE *fp = fopen(filename, "r");
122             if (fp != 0) {
123                 if (fread(blob, sizeof(char), size, fp) == size) {
124                     for (j = 0; (size_t) j < size; ++j) {
125                         if (blob[j] == '\n') {
126                             blob[j] = '\0';
127                             had_line = TRUE;
128                         } else if (had_line) {
129                             had_line = FALSE;
130                             result[k++] = blob + j;
131                         }
132                     }
133                     result[k] = 0;
134                 }
135                 fclose(fp);
136             }
137         }
138         if (k == 0) {
139             free(blob);
140             free(result);
141             result = 0;
142         }
143     }
144     return result;
145 }
146
147 static void
148 usage(void)
149 {
150     static const char *msg[] =
151     {
152         "Usage: picsmap [-x rgb-path] [xbm-file [...]]"
153     };
154     size_t n;
155
156     fflush(stdout);
157     for (n = 0; n < SIZEOF(msg); n++)
158         fprintf(stderr, "%s\n", msg[n]);
159     ExitProgram(EXIT_FAILURE);
160 }
161
162 static void
163 giveup(const char *fmt,...)
164 {
165     va_list ap;
166     if (in_curses)
167         endwin();
168     fflush(stdout);
169     va_start(ap, fmt);
170     vfprintf(stderr, fmt, ap);
171     fputc('\n', stderr);
172     va_end(ap);
173     usage();
174 }
175
176 static int
177 map_color(int value)
178 {
179     int r = (value & 0xff0000) >> 16;
180     int g = (value & 0x00ff00) >> 8;
181     int b = (value & 0x0000ff) >> 0;
182     /* TODO simple mapping into COLOR_BLACK .. COLOR_WHITE */
183     int result = ((r >= 128) << 2) + ((g >= 128) << 1) + (b >= 128);
184     return result;
185 }
186
187 static int
188 bytes_of(int value)
189 {
190     if (value & 7) {
191         value |= 7;
192         value++;
193     }
194     return value;
195 }
196
197 static int match_c(const char *, const char *,...) GCC_SCANFLIKE(2,3);
198
199 static char *
200 skip_s(char *s)
201 {
202     while (isspace(UChar(*s)))
203         s++;
204     return s;
205 }
206
207 static const char *
208 skip_cs(const char *s)
209 {
210     while (isspace(UChar(*s)))
211         s++;
212     return s;
213 }
214
215 static int
216 match_c(const char *source, const char *pattern,...)
217 {
218     int limit = (int) strlen(source);
219     const char *last_s = source + limit;
220     va_list ap;
221     int ch;
222     int *ip;
223     char *cp;
224     long lv;
225
226     va_start(ap, pattern);
227
228     limit = -1;
229     while (*pattern != '\0') {
230         ch = UChar(*pattern++);
231         /* blank in the pattern matches zero-or-more blanks in source */
232         if (isspace(ch)) {
233             source = skip_cs(source);
234             continue;
235         }
236         /* %c, %d, %s are like sscanf except for special treatment of blanks */
237         if (ch == '%' && *pattern != '\0' && strchr("cdnsx", *pattern)) {
238             bool found = FALSE;
239             ch = *pattern++;
240             switch (ch) {
241             case 'c':
242                 cp = va_arg(ap, char *);
243                 do {
244                     *cp++ = *source++;
245                 } while (--limit > 0);
246                 break;
247             case 'd':
248             case 'x':
249                 limit = -1;
250                 ip = va_arg(ap, int *);
251                 lv = strtol(source, &cp, ch == 'd' ? 10 : 16);
252                 if (cp != 0 && cp != source) {
253                     *ip = (int) lv;
254                     source = cp;
255                 } else {
256                     goto finish;
257                 }
258                 break;
259             case 'n':
260                 /* not really sscanf... */
261                 limit = *va_arg(ap, int *);
262                 break;
263             case 's':
264                 limit = -1;
265                 cp = va_arg(ap, char *);
266                 while (*source != '\0') {
267                     ch = UChar(*source);
268                     if (isspace(ch)) {
269                         break;
270                     } else if (found && (ch == *skip_cs(pattern))) {
271                         break;
272                     } else {
273                         *cp++ = *source++;
274                         found = TRUE;
275                     }
276                 }
277                 *cp = '\0';
278                 break;
279             }
280             continue;
281         }
282         /* other characters are matched literally */
283         if (*source++ != ch) {
284             break;
285         }
286     }
287   finish:
288
289     va_end(ap);
290     if (source > last_s)
291         source = last_s;
292     return (*source || *pattern) ? 0 : 1;
293 }
294
295 static int
296 match_colors(const char *source, int cpp, char *arg1, char *arg2, char *arg3)
297 {
298     int result = 0;
299
300     /* most files use a quasi-fixed format */
301     if (match_c(source, " \"%n%c %s %s \" , ", &cpp, arg1, arg2, arg3)) {
302         arg1[cpp] = '\0';
303         result = 1;
304     } else {
305         char *t;
306         const char *s = skip_cs(source);
307         size_t have = strlen(source);
308
309         if (*s++ == '"' && have > ((size_t) cpp + 2)) {
310             memcpy(arg1, s, (size_t) cpp);
311             s += cpp;
312             while (*s++ == '\t') {
313                 for (t = arg2; (*s != '\0') && strchr("\t\"", *s) == 0;) {
314                     if (*s == ' ') {
315                         s = skip_cs(s);
316                         break;
317                     }
318                     *t++ = *s++;
319                     *t = '\0';
320                 }
321                 for (t = arg3; (*s != '\0') && strchr("\t\"", *s) == 0;) {
322                     *t++ = *s++;
323                     *t = '\0';
324                 }
325                 if (!strcmp(arg2, "c")) {
326                     result = 1;
327                     break;
328                 }
329             }
330         }
331     }
332     return result;
333 }
334
335 static RGB_DATA *
336 parse_rgb(char **data)
337 {
338     char buf[BUFSIZ];
339     int n;
340     unsigned long r, g, b;
341     char *s, *t;
342     size_t item = 0;
343     size_t need;
344     RGB_DATA *result = 0;
345
346     for (need = 0; data[need] != 0; ++need) ;
347     result = typeCalloc(RGB_DATA, need + 2);
348
349     for (n = 0; data[n] != 0; ++n) {
350         if (strlen(t = data[n]) >= sizeof(buf) - 1)
351             continue;
352         if (*(s = skip_s(t)) == '!')
353             continue;
354
355         r = strtoul(s, &t, 10);
356         s = skip_s(t);
357         g = strtoul(s, &t, 10);
358         s = skip_s(t);
359         b = strtoul(s, &t, 10);
360         s = skip_s(t);
361
362         result[item].name = s;
363         t = s + strlen(s);
364         while (t-- != s && isspace(UChar(*t))) {
365             *t = '\0';
366         }
367         result[item].value = (int) ((r & 0xff) << 16 | (g & 0xff) << 8 | (b
368                                                                           & 0xff));
369         ++item;
370     }
371
372     result[item].name = "none";
373     result[item].value = -1;
374
375     return result;
376 }
377
378 static RGB_DATA *
379 lookup_rgb(const char *name)
380 {
381     RGB_DATA *result = 0;
382     if (rgb_table != 0) {
383         int n;
384         for (n = 0; rgb_table[n].name != 0; ++n) {
385             if (!strcasecmp(name, rgb_table[n].name)) {
386                 result = &rgb_table[n];
387                 break;
388             }
389         }
390     }
391     return result;
392 }
393
394 static PICS_HEAD *
395 parse_xbm(char **data)
396 {
397     int n;
398     int state = 0;
399     char buf[BUFSIZ];
400     int num;
401     char ch;
402     char *s;
403     char *t;
404     PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
405     size_t which = 0;
406     size_t cells = 0;
407
408     for (n = 0; data[n] != 0; ++n) {
409         if (strlen(s = data[n]) >= sizeof(buf) - 1)
410             continue;
411         switch (state) {
412         case 0:
413         case 1:
414         case 2:
415             if (sscanf(s, "#define %s %d%c", buf, &num, &ch) >= 2) {
416                 if ((t = strstr(buf, "_width")) != 0) {
417                     state |= 1;
418                     result->wide = bytes_of(num);
419                 } else if ((t = strstr(buf, "_height")) != 0) {
420                     state |= 2;
421                     result->high = num;
422                 }
423                 *t = '\0';
424                 if (result->name) {
425                     if (strcmp(result->name, buf)) {
426                         goto finish;
427                     }
428                 } else {
429                     result->name = strdup(buf);
430                 }
431             }
432             break;
433         case 3:
434             if (sscanf(s, "static char %[^_ ]_bits[]%c", buf, &ch) >= 1) {
435                 if (strcmp(result->name, buf)) {
436                     goto finish;
437                 }
438                 state = 4;
439                 cells = (size_t) (result->wide * result->high);
440                 result->cells = typeCalloc(PICS_CELL, cells);
441                 if ((s = strchr(s, L_CURLY)) == 0)
442                     break;
443                 ++s;
444             } else {
445                 break;
446             }
447         case 4:
448             while (*s != '\0') {
449                 while (isspace(UChar(*s))) {
450                     ++s;
451                 }
452                 if (isdigit(UChar(*s))) {
453                     long value = strtol(s, &t, 0);
454                     int b;
455                     if (t != s || value > 255 || value < 0) {
456                         s = t;
457                     } else {
458                         state = -1;
459                         goto finish;
460                     }
461                     /* TODO: which order? */
462                     for (b = 0; b < 8; ++b) {
463                         if (((1L << b) & value) != 0) {
464                             result->cells[which].ch = '*';
465                             result->cells[which].fg = 1;
466                         } else {
467                             result->cells[which].ch = ' ';
468                             result->cells[which].fg = 0;
469                         }
470                         if (++which > cells) {
471                             state = -1;
472                             goto finish;
473                         }
474                     }
475                 }
476                 if (*s == R_CURLY) {
477                     state = 5;
478                     goto finish;
479                 } else if (*s == ',') {
480                     ++s;
481                 }
482             }
483             break;
484         default:
485             break;
486         }
487     }
488   finish:
489     if (state < 4) {
490         if (result) {
491             free_pics_head(result);
492             result = 0;
493         }
494     } else {
495         result->colors = 2;
496         result->pairs = typeCalloc(PICS_PAIR, 2);
497         result->pairs[1].fg = 0xffffff;
498     }
499     return result;
500 }
501
502 static PICS_HEAD *
503 parse_xpm(char **data)
504 {
505     int state = 0;
506     PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
507     RGB_DATA *by_name;
508     int n;
509     int cells = 0;
510     int color = 0;
511     int cpp = 1;                /* chars per pixel */
512     int num[6];
513     int which = 0;
514     char ch;
515     const char *cs;
516     char *s;
517     char buf[BUFSIZ];
518     char arg1[BUFSIZ];
519     char arg2[BUFSIZ];
520     char arg3[BUFSIZ];
521     char **list = 0;
522
523     for (n = 0; data[n] != 0; ++n) {
524         if (strlen(s = data[n]) >= sizeof(buf) - 1)
525             continue;
526         switch (state) {
527         case 0:
528             if (match_c(s, " /* XPM */ ")) {
529                 state = 1;
530             }
531             break;
532         case 1:
533             if (match_c(s, " static char * %s [] = %c ", arg1, &ch) &&
534                 ch == L_CURLY) {
535                 result->name = strdup(arg1);
536                 state = 2;
537             }
538             break;
539         case 2:
540             if (match_c(s, " \" %d %d %d %d \" , ",
541                         num + 0, num + 1, num + 2, num + 3) ||
542                 match_c(s, " \" %d %d %d %d %d %d \" , ",
543                         num + 0, num + 1, num + 2, num + 3, num + 4, num + 5)) {
544                 result->wide = num[0];
545                 result->high = num[1];
546                 result->colors = num[2];
547                 result->pairs = typeCalloc(PICS_PAIR, result->colors);
548                 cells = (size_t) (result->wide * result->high);
549                 result->cells = typeCalloc(PICS_CELL, cells);
550                 list = typeCalloc(char *, result->colors);
551                 cpp = num[3];
552                 state = 3;
553             }
554             break;
555         case 3:
556             if (!match_colors(s, cpp, arg1, arg2, arg3)) {
557                 break;
558             }
559             list[color] = strdup(arg1);
560             if ((by_name = lookup_rgb(arg3)) != 0) {
561                 result->pairs[color].fg = by_name->value;
562             } else if (*arg3 == '#') {
563                 char *rgb = arg3 + 1;
564                 unsigned long value = strtoul(rgb, &s, 16);
565                 switch ((int) strlen(rgb)) {
566                 case 6:
567                     break;
568                 case 12:
569                     value = (((value >> 24) & 0xff0000L)
570                              | ((value >> 16) & 0xff00L)
571                              | ((value >> 8) & 0xffL));
572                     break;
573                 default:
574                     printf("unexpected rgb value %s\n", rgb);
575                     break;
576                 }
577                 result->pairs[color].fg = (int) value;
578             } else {
579                 result->pairs[color].fg = 0;    /* actually an error */
580             }
581             if (++color >= result->colors)
582                 state = 4;
583             break;
584         case 4:
585             if (*(cs = skip_cs(s)) == '"') {
586                 ++cs;
587                 while (*cs != '\0' && *cs != '"') {
588                     int c;
589
590                     for (c = 0; c < result->colors; ++c) {
591                         if (!strncmp(cs, list[c], (size_t) cpp)) {
592                             result->cells[which].ch = list[c][0];
593                             result->cells[which].fg = c;
594                             break;
595                         }
596                     }
597
598                     if (result->cells[which].ch == 0) {
599                         result->cells[which].ch = '?';
600                         result->cells[which].fg = 0;
601                     }
602
603                     if (++which >= cells) {
604                         state = 5;
605                         break;
606                     }
607                     for (c = cpp; c > 0; --c, ++cs) ;
608                 }
609             }
610             break;
611         }
612     }
613
614     if (result && list) {
615         for (n = 0; n < result->colors; ++n)
616             free(list[n]);
617         free(list);
618     }
619
620     if (state < 5) {
621         free_pics_head(result);
622         result = 0;
623     }
624
625     return result;
626 }
627
628 /*
629  * The obscurely-named "convert" is provided by ImageMagick
630  */
631 static PICS_HEAD *
632 parse_img(const char *filename)
633 {
634     char *cmd = malloc(strlen(filename) + 80);
635     FILE *pp;
636     char buffer[BUFSIZ];
637     bool failed = FALSE;
638     PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
639
640     sprintf(cmd, "convert -thumbnail %dx \"%s\" txt:-", COLS, filename);
641     if ((pp = popen(cmd, "r")) != 0) {
642         int count = 0;
643         int col = 0;
644         int row = 0;
645         int len = 0;
646         while (fgets(buffer, sizeof(buffer), pp) != 0) {
647             if (strlen(buffer) > 160) {         /* 80 columns would be enough */
648                 failed = TRUE;
649                 break;
650             }
651             if (count++ == 0) {
652                 if (match_c(buffer,
653                             "# ImageMagick pixel enumeration: %d,%d,%d,srgba ",
654                             &col, &row, &len)) {
655                     result->name = strdup(filename);
656                     result->wide = col;
657                     result->high = row;
658                     result->colors = 256;
659                     result->pairs = typeCalloc(PICS_PAIR, result->colors);
660                     result->cells = typeCalloc(PICS_CELL, (size_t) (col * row));
661                 } else {
662                     failed = TRUE;
663                     break;
664                 }
665             } else {
666                 /* subsequent lines begin "col,row: (r,g,b,a) #RGB" */
667                 int r, g, b;
668                 unsigned check;
669                 int which, c;
670                 char *s = strchr(buffer, '#');
671                 if (s != 0) {
672                     /* after the "#RGB", there are differences - just ignore */
673                     while (*s != '\0' && !isspace(UChar(*s)))
674                         ++s;
675                     *++s = '\0';
676                 }
677                 if (match_c(buffer,
678                             "%d,%d: (%d,%d,%d,255) #%x ",
679                             &col, &row,
680                             &r, &g, &b,
681                             &check)) {
682                     if (r > 255 ||
683                         g > 255 ||
684                         b > 255 ||
685                         check != (unsigned) ((r << 16) | (g << 8) | b)) {
686                         failed = TRUE;
687                         break;
688                     }
689                     for (c = 0; c < result->colors; ++c) {
690                         if (result->pairs[c].fg == (int) check) {
691                             break;
692                         } else if (result->pairs[c].fg == 0) {
693                             result->pairs[c].fg = (int) check;
694                             break;
695                         }
696                     }
697                     if (c >= result->colors) {
698                         int more = (result->colors * 3) / 2;
699                         PICS_PAIR *p = typeRealloc(PICS_PAIR, more, result->pairs);
700                         if (p != 0) {
701                             result->colors = more;
702                             result->pairs = p;
703                             result->pairs[c].fg = (int) check;
704                             result->pairs[c].bg = 0;
705                             while (++c < more) {
706                                 result->pairs[c].fg = 0;
707                                 result->pairs[c].bg = 0;
708                             }
709                         }
710                     }
711                     which = col + (row * result->wide);
712                     result->cells[which].ch = '#';      /* TODO: space? */
713                     result->cells[which].fg = (c < result->colors) ? c : -1;
714                 } else {
715                     failed = TRUE;
716                     break;
717                 }
718             }
719         }
720         pclose(pp);
721         if (!failed) {
722             for (len = result->colors; len > 3; len--) {
723                 if (result->pairs[len - 1].fg == 0) {
724                     result->colors = len - 1;
725                 } else {
726                     break;
727                 }
728             }
729         }
730     }
731     free(cmd);
732
733     if (failed) {
734         free_pics_head(result);
735         result = 0;
736     }
737
738     return result;
739 }
740
741 static PICS_HEAD *
742 read_picture(const char *filename, char **data)
743 {
744     PICS_HEAD *pics;
745     if ((pics = parse_xbm(data)) == 0) {
746         if ((pics = parse_xpm(data)) == 0) {
747             if ((pics = parse_img(filename)) == 0) {
748                 free_data(data);
749                 giveup("unexpected file-format for \"%s\"", filename);
750             }
751         }
752     }
753     return pics;
754 }
755
756 static void
757 dump_picture(PICS_HEAD * pics)
758 {
759     int y, x;
760
761     printf("Name %s\n", pics->name);
762     printf("Size %dx%d\n", pics->high, pics->wide);
763     printf("Color\n");
764     for (y = 0; y < pics->colors; ++y) {
765         if (pics->pairs[y].fg < 0) {
766             printf(" %3d: %d\n", y, pics->pairs[y].fg);
767         } else {
768             printf(" %3d: #%06x\n", y, pics->pairs[y].fg);
769         }
770     }
771     for (y = 0; y < pics->high; ++y) {
772         for (x = 0; x < pics->wide; ++x) {
773             putchar(pics->cells[y * pics->wide + x].ch);
774         }
775         putchar('\n');
776     }
777 }
778
779 static void
780 show_picture(PICS_HEAD * pics)
781 {
782     int y, x;
783     int n;
784
785     if (has_colors()) {
786         for (n = 0; n < pics->colors; ++n) {
787             init_pair((short) (n + 1),
788                       (short) map_color(pics->pairs[n].fg),
789                       COLOR_BLACK);
790         }
791         attrset(COLOR_PAIR(1));
792         erase();
793     }
794     for (y = 0; y < pics->high; ++y) {
795         if (y >= LINES)
796             break;
797         move(y, 0);
798         for (x = 0; x < pics->wide; ++x) {
799             if (x >= COLS)
800                 break;
801             n = (y * pics->wide + x);
802             attrset(COLOR_PAIR(pics->cells[n].fg + 1));
803             addch((chtype) pics->cells[n].ch);
804         }
805     }
806     mvgetch(0, 0);
807     endwin();
808 }
809
810 int
811 main(int argc, char *argv[])
812 {
813     int n;
814     const char *rgb_path = "/etc/X11/rgb.txt";
815
816     while ((n = getopt(argc, argv, "r:")) != -1) {
817         switch (n) {
818         case 'r':
819             rgb_path = optarg;
820             break;
821         default:
822             usage();
823             break;
824         }
825     }
826
827     if (argc > 1) {
828         char **rgb_data = read_file(rgb_path);
829
830         if (rgb_data)
831             rgb_table = parse_rgb(rgb_data);
832
833         if (isatty(fileno(stdout))) {
834             in_curses = TRUE;
835             initscr();
836             cbreak();
837             noecho();
838             if (has_colors())
839                 start_color();
840             scrollok(stdscr, FALSE);
841             endwin();
842         }
843
844         for (n = 1; n < argc; ++n) {
845             PICS_HEAD *pics;
846             char **data = read_file(argv[n]);
847
848             if (data == 0) {
849                 giveup("cannot read \"%s\"", argv[n]);
850             }
851             pics = read_picture(argv[n], data);
852             if (in_curses) {
853                 show_picture(pics);
854             } else {
855                 dump_picture(pics);
856             }
857             free_data(data);
858             free_pics_head(pics);
859         }
860         free_data(rgb_data);
861         free(rgb_table);
862     } else {
863         usage();
864     }
865     ExitProgram(EXIT_SUCCESS);
866 }