"Fossies" - the Fresh Open Source Software Archive

Member "gifsicle-1.92/src/gifdiff.c" (18 Apr 2019, 18913 Bytes) of package /linux/misc/gifsicle-1.92.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gifdiff.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.91_vs_1.92.

    1 /* gifdiff.c - Gifdiff compares GIF images for identical appearance.
    2    Copyright (C) 1998-2019 Eddie Kohler, ekohler@gmail.com
    3    This file is part of gifdiff, in the gifsicle package.
    4 
    5    Gifdiff is free software. It is distributed under the GNU Public License,
    6    version 2; you can copy, distribute, or alter it at will, as long
    7    as this notice is kept intact and this source code is made available. There
    8    is no warranty, express or implied. */
    9 
   10 #include <config.h>
   11 #include <lcdfgif/gif.h>
   12 #include <lcdf/clp.h>
   13 #include <stdarg.h>
   14 #include <stdio.h>
   15 #include <string.h>
   16 #include <errno.h>
   17 #if HAVE_UNISTD_H
   18 # include <unistd.h>
   19 #endif
   20 
   21 #define QUIET_OPT               300
   22 #define HELP_OPT                301
   23 #define VERSION_OPT             302
   24 #define IGNORE_REDUNDANCY_OPT   303
   25 #define REDUNDANCY_OPT          304
   26 #define IGNORE_BACKGROUND_OPT   305
   27 #define BACKGROUND_OPT          306
   28 
   29 const Clp_Option options[] = {
   30   { "help", 'h', HELP_OPT, 0, 0 },
   31   { "brief", 'q', QUIET_OPT, 0, Clp_Negate },
   32   { "redudancy", 0, REDUNDANCY_OPT, 0, Clp_Negate },
   33   { "ignore-redundancy", 'w', IGNORE_REDUNDANCY_OPT, 0, Clp_Negate },
   34   { "bg", 0, BACKGROUND_OPT, 0, Clp_Negate },
   35   { "ignore-bg", 0, IGNORE_BACKGROUND_OPT, 0, Clp_Negate },
   36   { "background", 0, BACKGROUND_OPT, 0, Clp_Negate },
   37   { "ignore-background", 'B', IGNORE_BACKGROUND_OPT, 0, Clp_Negate },
   38   { "version", 'v', VERSION_OPT, 0, 0 }
   39 };
   40 
   41 const char *program_name;
   42 
   43 static const char *filename1;
   44 static const char *filename2;
   45 
   46 static unsigned screen_width, screen_height;
   47 #define TRANSP (0)
   48 
   49 static uint16_t *gdata[2];
   50 static uint16_t *glast[2];
   51 static uint16_t *scratch;
   52 static uint16_t *line;
   53 
   54 static int brief;
   55 static int ignore_redundancy;
   56 static int ignore_background;
   57 
   58 static Clp_Parser* clp;
   59 
   60 
   61 static void
   62 combine_colormaps(Gif_Colormap *gfcm, Gif_Colormap *newcm)
   63 {
   64   int i, gfcm_ncol = gfcm ? gfcm->ncol : 0;
   65   for (i = 0; i < gfcm_ncol; i++) {
   66     Gif_Color *c = &gfcm->col[i];
   67     c->pixel = Gif_AddColor(newcm, c, 1);
   68   }
   69 }
   70 
   71 static void
   72 fill_area(uint16_t *data, int l, int t, int w, int h, uint16_t val)
   73 {
   74   int x;
   75   data += screen_width * t + l;
   76   for (; h > 0; --h) {
   77     for (x = w; x > 0; --x)
   78       *data++ = val;
   79     data += screen_width - w;
   80   }
   81 }
   82 
   83 static void
   84 copy_area(uint16_t *dst, const uint16_t *src, int l, int t, int w, int h)
   85 {
   86   dst += screen_width * t + l;
   87   src += screen_width * t + l;
   88   for (; h > 0; --h, dst += screen_width, src += screen_width)
   89     memcpy(dst, src, sizeof(uint16_t) * w);
   90 }
   91 
   92 static void
   93 expand_bounds(int *lf, int *tp, int *rt, int *bt, const Gif_Image *gfi)
   94 {
   95   int empty = (*lf >= *rt || *tp >= *bt);
   96   if (empty || gfi->left < *lf)
   97     *lf = gfi->left;
   98   if (empty || gfi->top < *tp)
   99     *tp = gfi->top;
  100   if (empty || gfi->left + gfi->width > *rt)
  101     *rt = gfi->left + gfi->width;
  102   if (empty || gfi->top + gfi->height > *bt)
  103     *bt = gfi->top + gfi->height;
  104 }
  105 
  106 
  107 static int
  108 apply_image(int is_second, Gif_Stream *gfs, int imageno, uint16_t background)
  109 {
  110   int i, x, y, any_change;
  111   Gif_Image *gfi = gfs->images[imageno];
  112   Gif_Image *pgfi = imageno ? gfs->images[imageno - 1] : 0;
  113   int width = gfi->width;
  114   uint16_t map[256];
  115   uint16_t *data = gdata[is_second];
  116   uint16_t *last = glast[is_second];
  117   Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global;
  118   int gfcm_ncol = gfcm ? gfcm->ncol : 0;
  119 
  120   /* set up colormap */
  121   for (i = 0; i < gfcm_ncol; ++i)
  122     map[i] = gfcm->col[i].pixel;
  123   for (i = gfcm_ncol; i < 256; ++i)
  124     map[i] = 1;
  125   if (gfi->transparent >= 0 && gfi->transparent < 256)
  126     map[gfi->transparent] = TRANSP;
  127 
  128   /* if this image's disposal is 'previous', save the post-disposal version in
  129      'scratch' */
  130   if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) {
  131     copy_area(scratch, data, gfi->left, gfi->top, gfi->width, gfi->height);
  132     if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS)
  133       copy_area(scratch, last, pgfi->left, pgfi->top, pgfi->width, pgfi->height);
  134     else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND)
  135       fill_area(scratch, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background);
  136   }
  137 
  138   /* uncompress and clip */
  139   Gif_UncompressImage(gfs, gfi);
  140   Gif_ClipImage(gfi, 0, 0, screen_width, screen_height);
  141 
  142   any_change = imageno == 0;
  143   {
  144     int lf = 0, tp = 0, rt = 0, bt = 0;
  145     expand_bounds(&lf, &tp, &rt, &bt, gfi);
  146     if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS)
  147       expand_bounds(&lf, &tp, &rt, &bt, pgfi);
  148     else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND) {
  149       expand_bounds(&lf, &tp, &rt, &bt, pgfi);
  150       fill_area(last, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background);
  151     } else
  152       pgfi = 0;
  153     for (y = tp; y < bt; ++y) {
  154       uint16_t *outd = data + screen_width * y + lf;
  155       if (!any_change)
  156         memcpy(line, outd, (rt - lf) * sizeof(uint16_t));
  157       if (pgfi && y >= pgfi->top && y < pgfi->top + pgfi->height)
  158         memcpy(outd + pgfi->left - lf,
  159                last + screen_width * y + pgfi->left,
  160                pgfi->width * sizeof(uint16_t));
  161       if (y >= gfi->top && y < gfi->top + gfi->height) {
  162         uint16_t *xoutd = outd + gfi->left - lf;
  163         const uint8_t *ind = gfi->img[y - gfi->top];
  164         for (x = 0; x < width; ++x, ++ind, ++xoutd)
  165           if (map[*ind] != TRANSP)
  166             *xoutd = map[*ind];
  167       }
  168       if (!any_change && memcmp(line, outd, (rt - lf) * sizeof(uint16_t)) != 0)
  169         any_change = 1;
  170     }
  171   }
  172 
  173   Gif_ReleaseUncompressedImage(gfi);
  174   Gif_ReleaseCompressedImage(gfi);
  175 
  176   /* switch 'glast' with 'scratch' if necessary */
  177   if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) {
  178     uint16_t *x = scratch;
  179     scratch = glast[is_second];
  180     glast[is_second] = x;
  181   }
  182 
  183   return any_change;
  184 }
  185 
  186 
  187 #define SAME 0
  188 #define DIFFERENT 1
  189 static int was_different;
  190 
  191 static void
  192 different(const char *format, ...)
  193 {
  194   va_list val;
  195   va_start(val, format);
  196   if (!brief) {
  197     vfprintf(stdout, format, val);
  198     fputc('\n', stdout);
  199   }
  200   va_end(val);
  201   was_different = 1;
  202 }
  203 
  204 
  205 static void
  206 name_loopcount(int loopcount, char *buf)
  207 {
  208   if (loopcount < 0)
  209     strcpy(buf, "none");
  210   else if (loopcount == 0)
  211     strcpy(buf, "forever");
  212   else
  213     sprintf(buf, "%d", loopcount);
  214 }
  215 
  216 static void
  217 name_delay(int delay, char *buf)
  218 {
  219   if (delay == 0)
  220     strcpy(buf, "none");
  221   else
  222     sprintf(buf, "%d.%02ds", delay / 100, delay % 100);
  223 }
  224 
  225 static void
  226 name_color(int color, Gif_Colormap *gfcm, char *buf)
  227 {
  228   if (color == TRANSP)
  229     strcpy(buf, "transparent");
  230   else {
  231     Gif_Color *c = &gfcm->col[color];
  232     sprintf(buf, "#%02X%02X%02X", c->gfc_red, c->gfc_green, c->gfc_blue);
  233   }
  234 }
  235 
  236 
  237 int
  238 compare(Gif_Stream *s1, Gif_Stream *s2)
  239 {
  240   Gif_Colormap *newcm;
  241   int imageno1, imageno2, background1, background2;
  242   char buf1[256], buf2[256], fbuf[256];
  243 
  244   was_different = 0;
  245 
  246   /* Compare image counts and screen sizes. If either of these differs, quit
  247      early. */
  248   Gif_CalculateScreenSize(s1, 0);
  249   Gif_CalculateScreenSize(s2, 0);
  250 
  251   if (s1->screen_width != s2->screen_width
  252       || s1->screen_height != s2->screen_height) {
  253     different("screen sizes differ: <%dx%d >%dx%d", s1->screen_width,
  254               s1->screen_height, s2->screen_width, s2->screen_height);
  255     return DIFFERENT;
  256   }
  257 
  258   if (s1->screen_width == 0 || s1->screen_height == 0
  259       || s2->screen_width == 0 || s2->screen_height == 0) {
  260     /* paranoia -- don't think this can happen */
  261     different("zero screen sizes");
  262     return DIFFERENT;
  263   }
  264 
  265   if (s1->nimages == 0 || s2->nimages == 0) {
  266     if (s1->nimages != s2->nimages) {
  267       different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages);
  268       return DIFFERENT;
  269     } else
  270       return SAME;
  271   }
  272 
  273   /* Create arrays for the image data */
  274   screen_width = s1->screen_width;
  275   screen_height = s1->screen_height;
  276 
  277   gdata[0] = Gif_NewArray(uint16_t, screen_width * screen_height);
  278   gdata[1] = Gif_NewArray(uint16_t, screen_width * screen_height);
  279   glast[0] = Gif_NewArray(uint16_t, screen_width * screen_height);
  280   glast[1] = Gif_NewArray(uint16_t, screen_width * screen_height);
  281   scratch = Gif_NewArray(uint16_t, screen_width * screen_height);
  282   line = Gif_NewArray(uint16_t, screen_width);
  283 
  284   /* Merge all distinct colors from the two images into one colormap, setting
  285      the 'pixel' slots in the images' colormaps to the corresponding values
  286      in the merged colormap. Don't forget transparency */
  287   newcm = Gif_NewFullColormap(1, 256);
  288   combine_colormaps(s1->global, newcm);
  289   combine_colormaps(s2->global, newcm);
  290   for (imageno1 = 0; imageno1 < s1->nimages; ++imageno1)
  291     combine_colormaps(s1->images[imageno1]->local, newcm);
  292   for (imageno2 = 0; imageno2 < s2->nimages; ++imageno2)
  293     combine_colormaps(s2->images[imageno2]->local, newcm);
  294 
  295   /* Choose the background values */
  296   background1 = background2 = TRANSP;
  297   if ((s1->nimages == 0 || s1->images[0]->transparent < 0)
  298       && s1->global && s1->background < s1->global->ncol)
  299       background1 = s1->global->col[ s1->background ].pixel;
  300   if ((s2->nimages == 0 || s2->images[0]->transparent < 0)
  301       && s2->global && s2->background < s2->global->ncol)
  302       background2 = s2->global->col[ s2->background ].pixel;
  303 
  304   /* Clear screens */
  305   fill_area(gdata[0], 0, 0, screen_width, screen_height, TRANSP);
  306   fill_area(gdata[1], 0, 0, screen_width, screen_height, TRANSP);
  307 
  308   /* Loopcounts differ? */
  309   if (s1->loopcount != s2->loopcount) {
  310     name_loopcount(s1->loopcount, buf1);
  311     name_loopcount(s2->loopcount, buf2);
  312     different("loop counts differ: <%s >%s", buf1, buf2);
  313   }
  314 
  315   /* Loop over frames, comparing image data and delays */
  316   apply_image(0, s1, 0, background1);
  317   apply_image(1, s2, 0, background2);
  318   imageno1 = imageno2 = 0;
  319   while (imageno1 != s1->nimages && imageno2 != s2->nimages) {
  320     int fi1 = imageno1, fi2 = imageno2,
  321       delay1 = s1->images[fi1]->delay, delay2 = s2->images[fi2]->delay;
  322 
  323     /* get message right */
  324     if (imageno1 == imageno2)
  325       sprintf(fbuf, "#%d", imageno1);
  326     else
  327       sprintf(fbuf, "<#%d >#%d", imageno1, imageno2);
  328 
  329     /* compare pixels */
  330     if (memcmp(gdata[0], gdata[1],
  331                screen_width * screen_height * sizeof(uint16_t)) != 0) {
  332       unsigned d, c = screen_width * screen_height;
  333       uint16_t *d1 = gdata[0], *d2 = gdata[1];
  334       for (d = 0; d < c; d++, d1++, d2++)
  335         if (*d1 != *d2) {
  336           name_color(*d1, newcm, buf1);
  337           name_color(*d2, newcm, buf2);
  338           different("frame %s pixels differ: %d,%d <%s >%s",
  339                     fbuf, d % screen_width, d / screen_width, buf1, buf2);
  340           break;
  341         }
  342     }
  343 
  344     /* compare background */
  345     if (!ignore_background && background1 != background2
  346         && (imageno1 == 0 || s1->images[imageno1 - 1]->disposal == GIF_DISPOSAL_BACKGROUND)
  347         && (imageno2 == 0 || s2->images[imageno2 - 1]->disposal == GIF_DISPOSAL_BACKGROUND)) {
  348         unsigned d, c = screen_width * screen_height;
  349         uint16_t *d1 = gdata[0], *d2 = gdata[1];
  350         for (d = 0; d < c; ++d, ++d1, ++d2)
  351             if (*d1 == TRANSP || *d2 == TRANSP) {
  352                 name_color(background1, newcm, buf1);
  353                 name_color(background2, newcm, buf2);
  354                 different("frame %s background pixels differ: %d,%d <%s >%s",
  355                           fbuf, d % screen_width, d / screen_width, buf1, buf2);
  356                 background1 = background2 = TRANSP;
  357                 break;
  358             }
  359     }
  360 
  361     /* move to next images, skipping redundancy */
  362     for (++imageno1;
  363          imageno1 < s1->nimages && !apply_image(0, s1, imageno1, background1);
  364          ++imageno1)
  365       delay1 += s1->images[imageno1]->delay;
  366     for (++imageno2;
  367          imageno2 < s2->nimages && !apply_image(1, s2, imageno2, background2);
  368          ++imageno2)
  369       delay2 += s2->images[imageno2]->delay;
  370 
  371     if (!ignore_redundancy) {
  372       fi1 = (imageno1 - fi1) - (imageno2 - fi2);
  373       for (; fi1 > 0; --fi1)
  374         different("extra redundant frame: <#%d", imageno1 - fi1);
  375       for (; fi1 < 0; ++fi1)
  376         different("extra redundant frame: >#%d", imageno2 + fi1);
  377     }
  378 
  379     if (delay1 != delay2) {
  380       name_delay(delay1, buf1);
  381       name_delay(delay2, buf2);
  382       different("frame %s delays differ: <%s >%s", fbuf, buf1, buf2);
  383     }
  384   }
  385 
  386   if (imageno1 != s1->nimages || imageno2 != s2->nimages)
  387     different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages);
  388 
  389   /* That's it! */
  390   Gif_DeleteColormap(newcm);
  391   Gif_DeleteArray(gdata[0]);
  392   Gif_DeleteArray(gdata[1]);
  393   Gif_DeleteArray(glast[0]);
  394   Gif_DeleteArray(glast[1]);
  395   Gif_DeleteArray(scratch);
  396   Gif_DeleteArray(line);
  397 
  398   return was_different ? DIFFERENT : SAME;
  399 }
  400 
  401 
  402 void short_usage(void) {
  403     Clp_fprintf(clp, stderr, "Usage: %s [OPTION]... FILE1 FILE2\n\
  404 Try %<%s --help%> for more information.\n",
  405                 program_name, program_name);
  406 }
  407 
  408 void usage(void) {
  409     Clp_fprintf(clp, stdout, "\
  410 %<Gifdiff%> compares two GIF files (either images or animations) for identical\n\
  411 visual appearance. An animation and an optimized version of the same animation\n\
  412 should compare as the same. Gifdiff exits with status 0 if the images are\n\
  413 the same, 1 if they%,re different, and 2 if there was some error.\n\
  414 \n\
  415 Usage: %s [OPTION]... FILE1 FILE2\n\n", program_name);
  416     Clp_fprintf(clp, stdout, "\
  417 Options:\n\
  418   -q, --brief                   Don%,t report detailed differences.\n\
  419   -w, --ignore-redundancy       Ignore differences in redundant frames.\n\
  420   -B, --ignore-background       Ignore differences in background colors.\n\
  421   -h, --help                    Print this message and exit.\n\
  422   -v, --version                 Print version number and exit.\n\
  423 \n\
  424 Report bugs to <ekohler@gmail.com>.\n");
  425 }
  426 
  427 
  428 void fatal_error(const char* format, ...) {
  429     char buf[BUFSIZ];
  430     int n = snprintf(buf, BUFSIZ, "%s: ", program_name);
  431     va_list val;
  432     va_start(val, format);
  433     Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
  434     va_end(val);
  435     fputs(buf, stderr);
  436     exit(2);                    /* exit(2) for trouble */
  437 }
  438 
  439 void error(const char* format, ...) {
  440     char buf[BUFSIZ];
  441     int n = snprintf(buf, BUFSIZ, "%s: ", program_name);
  442     va_list val;
  443     va_start(val, format);
  444     Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
  445     va_end(val);
  446     fputs(buf, stderr);
  447 }
  448 
  449 static int gifread_error_count;
  450 
  451 static void
  452 gifread_error(Gif_Stream* gfs, Gif_Image* gfi,
  453               int is_error, const char *message)
  454 {
  455   static int last_is_error = 0;
  456   static int last_which_image = 0;
  457   static char last_message[256];
  458   static int different_error_count = 0;
  459   static int same_error_count = 0;
  460   int which_image = Gif_ImageNumber(gfs, gfi);
  461   const char *filename = gfs->landmark;
  462   if (which_image < 0)
  463     which_image = gfs->nimages;
  464 
  465   if (gifread_error_count == 0) {
  466     last_which_image = -1;
  467     last_message[0] = 0;
  468     different_error_count = 0;
  469   }
  470 
  471   gifread_error_count++;
  472   if (last_message[0] && different_error_count <= 10
  473       && (last_which_image != which_image || message == 0
  474           || strcmp(message, last_message) != 0)) {
  475     const char *etype = last_is_error ? "error" : "warning";
  476     error("While reading %<%s%> frame #%d:\n", filename, last_which_image);
  477     if (same_error_count == 1)
  478       error("  %s: %s\n", etype, last_message);
  479     else if (same_error_count > 0)
  480       error("  %s: %s (%d times)\n", etype, last_message, same_error_count);
  481     same_error_count = 0;
  482     last_message[0] = 0;
  483   }
  484 
  485   if (message) {
  486     if (last_message[0] == 0)
  487       different_error_count++;
  488     same_error_count++;
  489     strcpy(last_message, message);
  490     last_which_image = which_image;
  491     last_is_error = is_error;
  492   } else
  493     last_message[0] = 0;
  494 
  495   if (different_error_count == 11 && message) {
  496     error("(more errors while reading %<%s%>)\n", filename);
  497     different_error_count++;
  498   }
  499 }
  500 
  501 static Gif_Stream *
  502 read_stream(const char **filename)
  503 {
  504     FILE *f;
  505     Gif_Stream *gfs;
  506     if (*filename == 0) {
  507 #if 0
  508         /* Since gifdiff always takes explicit filename arguments,
  509            allow explicit reads from terminal. */
  510 #ifndef OUTPUT_GIF_TO_TERMINAL
  511         if (isatty(fileno(stdin))) {
  512             fatal_error("<stdin>: is a terminal\n");
  513             return NULL;
  514         }
  515 #endif
  516 #endif
  517         f = stdin;
  518 #if defined(_MSDOS) || defined(_WIN32)
  519         _setmode(_fileno(stdin), _O_BINARY);
  520 #elif defined(__DJGPP__)
  521         setmode(fileno(stdin), O_BINARY);
  522 #elif defined(__EMX__)
  523         _fsetmode(stdin, "b");
  524 #endif
  525         *filename = "<stdin>";
  526     } else {
  527         f = fopen(*filename, "rb");
  528         if (!f)
  529             fatal_error("%s: %s\n", *filename, strerror(errno));
  530     }
  531     gifread_error_count = 0;
  532     gfs = Gif_FullReadFile(f, GIF_READ_COMPRESSED, *filename, gifread_error);
  533     if (!gfs)
  534         fatal_error("%s: file not in GIF format\n", *filename);
  535     return gfs;
  536 }
  537 
  538 int
  539 main(int argc, char *argv[])
  540 {
  541   int how_many_inputs = 0;
  542   int status;
  543   const char **inputp;
  544   Gif_Stream *gfs1, *gfs2;
  545 
  546   clp = Clp_NewParser(argc, (const char * const *)argv,
  547                       sizeof(options) / sizeof(options[0]), options);
  548 
  549   program_name = Clp_ProgramName(clp);
  550   brief = 0;
  551 
  552   while (1) {
  553     int opt = Clp_Next(clp);
  554     switch (opt) {
  555 
  556      case HELP_OPT:
  557       usage();
  558       exit(0);
  559       break;
  560 
  561      case VERSION_OPT:
  562       printf("gifdiff (LCDF Gifsicle) %s\n", VERSION);
  563       printf("Copyright (C) 1998-2019 Eddie Kohler\n\
  564 This is free software; see the source for copying conditions.\n\
  565 There is NO warranty, not even for merchantability or fitness for a\n\
  566 particular purpose.\n");
  567       exit(0);
  568       break;
  569 
  570      case QUIET_OPT:
  571       brief = !clp->negated;
  572       break;
  573 
  574      case IGNORE_REDUNDANCY_OPT:
  575       ignore_redundancy = !clp->negated;
  576       break;
  577 
  578      case REDUNDANCY_OPT:
  579       ignore_redundancy = !!clp->negated;
  580       break;
  581 
  582      case IGNORE_BACKGROUND_OPT:
  583       ignore_background = !clp->negated;
  584       break;
  585 
  586      case BACKGROUND_OPT:
  587       ignore_background = !!clp->negated;
  588       break;
  589 
  590      case Clp_NotOption:
  591       if (how_many_inputs == 2) {
  592         error("too many file arguments\n");
  593         goto bad_option;
  594       }
  595       inputp = (how_many_inputs == 0 ? &filename1 : &filename2);
  596       how_many_inputs++;
  597       if (strcmp(clp->vstr, "-") == 0)
  598         *inputp = 0;
  599       else
  600         *inputp = clp->vstr;
  601       break;
  602 
  603      bad_option:
  604      case Clp_BadOption:
  605       short_usage();
  606       exit(1);
  607       break;
  608 
  609      case Clp_Done:
  610       goto done;
  611 
  612     }
  613   }
  614 
  615  done:
  616 
  617   if (how_many_inputs < 2)
  618     fatal_error("need exactly 2 file arguments\n");
  619   if (filename1 == 0 && filename2 == 0)
  620     fatal_error("can%,t read both files from stdin\n");
  621 
  622   gfs1 = read_stream(&filename1);
  623   gfs2 = read_stream(&filename2);
  624 
  625   status = (compare(gfs1, gfs2) == DIFFERENT);
  626   if (status == 1 && brief)
  627     printf("GIF files %s and %s differ\n", filename1, filename2);
  628 
  629   Gif_DeleteStream(gfs1);
  630   Gif_DeleteStream(gfs2);
  631   return status;
  632 }