[libpng16] Renamed pnginflate to png-fix-too-far-back in contrib/tools.
diff --git a/contrib/tools/png-fix-too-far-back.c b/contrib/tools/png-fix-too-far-back.c
new file mode 100644
index 0000000..116d597
--- /dev/null
+++ b/contrib/tools/png-fix-too-far-back.c
@@ -0,0 +1,1190 @@
+/* png-fix-too-far-back.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.3 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Tool to check and fix the zlib inflate 'too far back' problem, see the usage
+ * message for more information.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Define the following to use this program against your installed libpng,
+ * rather than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+#if PNG_LIBPNG_VER < 10603 /* 1.6.3 */
+#  error "png-fix-too-far-back will not work with libpng prior to 1.6.3"
+#endif
+
+#ifdef PNG_READ_SUPPORTED
+#include <zlib.h>
+
+#ifndef PNG_MAXIMUM_INFLATE_WINDOW
+#  error "png-fix-too-far-back not supported in this libpng version"
+#endif
+
+#if PNG_ZLIB_VERNUM >= 0x1240
+
+/* Copied from pngpriv.h */
+#ifdef __cplusplus
+#  define png_voidcast(type, value) static_cast<type>(value)
+#  define png_constcast(type, value) const_cast<type>(value)
+#  define png_aligncast(type, value) \
+   static_cast<type>(static_cast<void*>(value))
+#  define png_aligncastconst(type, value) \
+   static_cast<type>(static_cast<const void*>(value))
+#else
+#  define png_voidcast(type, value) (value)
+#  define png_constcast(type, value) ((type)(value))
+#  define png_aligncast(type, value) ((void*)(value))
+#  define png_aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+static int idat_error = 0;
+static int verbose = 0;
+static int errors = 0;
+static int warnings = 0;
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+   static int set_option = 0;
+#endif
+static const char *name = "stdin";
+static uLong crc_IDAT_head; /* CRC32 of "IDAT" */
+static uLong crc_IEND;
+static z_stream z_idat;
+
+/* Control structure for the temporary file */
+typedef struct
+{
+   size_t      image_size;
+   off_t       file_size;
+   fpos_t      header_pos;
+   fpos_t      crc_pos;
+   uLong       crc_tail;  /* CRC of bytes after header */
+   png_uint_32 len_tail;  /* Count thereof */
+   png_byte    header[2];
+
+   /* Image info */
+   png_uint_32 width;
+   png_uint_32 height;
+   png_byte    bit_depth;
+   png_byte    color_type;
+   png_byte    compression_method;
+   png_byte    filter_method;
+   png_byte    interlace_method;
+} IDAT_info;
+
+static png_uint_32
+mult(png_uint_32 n, png_uint_32 m)
+{
+   if ((n + (m-1)) / m > 0xffffffff/m)
+   {
+      fprintf(stderr, "%s: overflow (%lu, %u)\n", name, (unsigned long)n, m);
+      exit(2);
+   }
+
+   return n * m;
+}
+
+static size_t
+image_size(const IDAT_info *info)
+{
+   unsigned int pd = info->bit_depth;
+   size_t cb;
+
+   switch (info->color_type)
+   {
+      case 0: case 3:
+         break;
+
+      case 2: /* rgb */
+         pd *= 3;
+         break;
+
+      case 4: /* ga */
+         pd *= 2;
+         break;
+
+      case 6: /* rgba */
+         pd *= 4;
+         break;
+
+      default:
+         fprintf(stderr, "%s: invalid color type (%d)\n", name,
+            info->color_type);
+         exit(2);
+   }
+
+   switch (info->interlace_method)
+   {
+      case PNG_INTERLACE_ADAM7:
+         /* Interlacing makes the image larger because of the replication of
+          * both the filter byte and the padding to a byte boundary.
+          */
+         {
+            int pass;
+
+            for (cb=0, pass=0; pass<=6; ++pass)
+            {
+               png_uint_32 pw = PNG_PASS_COLS(info->width, pass);
+
+               if (pw > 0)
+                  cb += mult(((mult(pd, pw)+7) >> 3)+1,
+                        PNG_PASS_ROWS(info->height, pass));
+            }
+         }
+         break;
+
+      case PNG_INTERLACE_NONE:
+         cb = mult(info->height, 1+((mult(info->width, pd) + 7) >> 3));
+         break;
+
+      default:
+         fprintf(stderr, "%s: invalid interlace type %d\n", name,
+            info->interlace_method);
+         exit(2);
+   }
+
+   return cb;
+}
+
+static int
+image_windowBits(const IDAT_info *info)
+{
+   size_t cb = image_size(info);
+
+   if (cb > 16384) return 15;
+   if (cb >  8192) return 14;
+   if (cb >  4096) return 13;
+   if (cb >  2048) return 12;
+   if (cb >  1024) return 11;
+   if (cb >   512) return 10;
+   if (cb >   256) return  9;
+   return 8;
+}
+
+static void
+error_handler(png_structp png_ptr, png_const_charp message)
+{
+   if (strcmp(message, "IDAT: invalid distance too far back") == 0)
+      idat_error = 1;
+
+   else if (errors || verbose)
+      fprintf(stderr, "%s: %s\n", name, message);
+
+   png_longjmp(png_ptr, 1);
+}
+
+static void
+warning_handler(png_structp png_ptr, png_const_charp message)
+{
+   if (warnings || verbose)
+      fprintf(stderr, "%s: %s\n", name, message);
+
+   (void)png_ptr;
+}
+
+static int
+read_png(FILE *fp)
+{
+   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,
+      error_handler, warning_handler);
+   png_infop info_ptr = NULL;
+   png_bytep row = NULL, display = NULL;
+
+   if (png_ptr == NULL)
+      return 0;
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      if (row != NULL) free(row);
+      if (display != NULL) free(display);
+      return 0;
+   }
+
+#  ifdef PNG_MAXIMUM_INFLATE_WINDOW
+      png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, set_option != 0);
+#  endif
+
+   png_init_io(png_ptr, fp);
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+      png_error(png_ptr, "OOM allocating info structure");
+
+   png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
+
+   png_read_info(png_ptr, info_ptr);
+
+   /* Limit the decompression buffer size to 1 - this ensures that overlong
+    * length codes are always detected.
+    */
+   png_set_compression_buffer_size(png_ptr, 1);
+
+   {
+      png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+      row = png_voidcast(png_byte*, malloc(rowbytes));
+      display = png_voidcast(png_byte*, malloc(rowbytes));
+
+      if (row == NULL || display == NULL)
+         png_error(png_ptr, "OOM allocating row buffers");
+
+      {
+         png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+         int passes = png_set_interlace_handling(png_ptr);
+         int pass;
+
+         png_start_read_image(png_ptr);
+
+         for (pass = 0; pass < passes; ++pass)
+         {
+            png_uint_32 y = height;
+
+            /* NOTE: this trashes the row each time; interlace handling won't
+             * work, but this avoids memory thrashing for speed testing.
+             */
+            while (y-- > 0)
+               png_read_row(png_ptr, row, display);
+         }
+      }
+   }
+
+   /* Make sure to read to the end of the file: */
+   png_read_end(png_ptr, info_ptr);
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+   free(row);
+   free(display);
+   return 1;
+}
+
+/* Chunk tags (copied from pngpriv.h) */
+#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
+#define PNG_CHUNK(b1,b2,b3,b4) \
+   (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
+
+#define png_IHDR PNG_CHUNK( 73,  72,  68,  82)
+#define png_IDAT PNG_CHUNK( 73,  68,  65,  84)
+#define png_IEND PNG_CHUNK( 73,  69,  78,  68)
+#define png_PLTE PNG_CHUNK( 80,  76,  84,  69)
+#define png_bKGD PNG_CHUNK( 98,  75,  71,  68)
+#define png_cHRM PNG_CHUNK( 99,  72,  82,  77)
+#define png_gAMA PNG_CHUNK(103,  65,  77,  65)
+#define png_hIST PNG_CHUNK(104,  73,  83,  84)
+#define png_iCCP PNG_CHUNK(105,  67,  67,  80)
+#define png_iTXt PNG_CHUNK(105,  84,  88, 116)
+#define png_oFFs PNG_CHUNK(111,  70,  70, 115)
+#define png_pCAL PNG_CHUNK(112,  67,  65,  76)
+#define png_sCAL PNG_CHUNK(115,  67,  65,  76)
+#define png_pHYs PNG_CHUNK(112,  72,  89, 115)
+#define png_sBIT PNG_CHUNK(115,  66,  73,  84)
+#define png_sPLT PNG_CHUNK(115,  80,  76,  84)
+#define png_sRGB PNG_CHUNK(115,  82,  71,  66)
+#define png_sTER PNG_CHUNK(115,  84,  69,  82)
+#define png_tEXt PNG_CHUNK(116,  69,  88, 116)
+#define png_tIME PNG_CHUNK(116,  73,  77,  69)
+#define png_tRNS PNG_CHUNK(116,  82,  78,  83)
+#define png_zTXt PNG_CHUNK(122,  84,  88, 116)
+
+static void
+rx(FILE *fp, png_bytep buf, off_t cb)
+{
+   if (fread(buf,cb,1,fp) != 1) {
+      fprintf(stderr, "%s: failed to read %lu bytes\n", name,
+         (unsigned long)cb);
+      exit(2);
+   }
+}
+
+static png_uint_32
+r32(FILE *fp)
+{
+   png_byte buf[4];
+   rx(fp, buf, 4);
+   return ((((((png_uint_32)buf[0] << 8)+buf[1]) << 8)+buf[2]) << 8) + buf[3];
+}
+
+static void
+wx(FILE *fp, png_const_bytep buf, off_t cb)
+{
+   if (fwrite(buf,cb,1,fp) != 1) {
+      fprintf(stderr, "%s: failed to write %lu bytes\n", name,
+         (unsigned long)cb);
+      exit(3);
+   }
+}
+
+static void
+w32(FILE *fp, png_uint_32 val)
+{
+   png_byte buf[4];
+   buf[0] = (png_byte)(val >> 24);
+   buf[1] = (png_byte)(val >> 16);
+   buf[2] = (png_byte)(val >>  8);
+   buf[3] = (png_byte)(val);
+   wx(fp, buf, 4);
+}
+
+static void
+wcrc(FILE *fp, uLong crc)
+{
+   /* Safe cast because a CRC is 32 bits */
+   w32(fp, (png_uint_32)crc);
+}
+
+static void
+copy(FILE *fp, FILE *fpIn, off_t cb)
+{
+   png_byte buffer[1024];
+
+   while (cb >= 1024)
+   {
+      rx(fpIn, buffer, 1024);
+      wx(fp, buffer, 1024);
+      cb -= 1024;
+   }
+
+   if (cb > 0)
+   {
+      rx(fpIn, buffer, cb);
+      wx(fp, buffer, cb);
+   }
+}
+
+static void
+skip_bytes(FILE *fpIn, png_uint_32 cb)
+{
+   png_byte buffer[1024];
+
+   while (cb >= 1024)
+   {
+      rx(fpIn, buffer, 1024);
+      cb -= 1024;
+   }
+
+   if (cb > 0)
+      rx(fpIn, buffer, cb);
+}
+
+static void
+safe_getpos(FILE *fp, fpos_t *pos)
+{
+   if (fgetpos(fp, pos))
+   {
+      perror("tmpfile");
+      fprintf(stderr, "%s: tmpfile fgetpos failed\n", name);
+      exit(3);
+   }
+}
+
+static void
+safe_setpos(FILE *fp, fpos_t *pos)
+{
+   if (fflush(fp))
+   {
+      perror("tmpfile");
+      fprintf(stderr, "%s: tmpfile fflush failed\n", name);
+      exit(3);
+   }
+
+   if (fsetpos(fp, pos))
+   {
+      perror("tmpfile");
+      fprintf(stderr, "%s: tmpfile fsetpos failed\n", name);
+      exit(3);
+   }
+}
+
+static void
+idat_update(FILE *fp, IDAT_info *info)
+{
+   uLong crc;
+
+   safe_setpos(fp, &info->header_pos);
+   wx(fp, info->header, 2);
+
+   crc = crc32(crc_IDAT_head, info->header, 2);
+   crc = crc32_combine(crc, info->crc_tail, info->len_tail);
+
+   safe_setpos(fp, &info->crc_pos);
+   wcrc(fp, crc);
+}
+
+static void
+set_bits(const char *file, FILE *fp, IDAT_info *info, int bits)
+{
+   int byte1 = (info->header[0] & 0xf) + ((bits-8) << 4);
+   int byte2 = info->header[1] & 0xe0;
+
+   /* The checksum calculation: */
+   byte2 += 0x1f - ((byte1 << 8) + byte2) % 0x1f;
+
+   info->header[0] = (png_byte)byte1;
+   info->header[1] = (png_byte)byte2;
+
+   if (verbose)
+      fprintf(stderr, "%s: trying windowBits %d (Z_CMF = 0x%x)\n", file, bits,
+         byte1);
+
+   idat_update(fp, info);
+}
+
+static void
+ptagchar(png_uint_32 ch)
+{
+ ch &= 0xff;
+ if (isprint(ch))
+    putc(ch, stderr);
+
+ else
+    fprintf(stderr, "[%02x]", ch);
+}
+
+static void
+ptag(png_uint_32 tag)
+{
+   if (tag != 0)
+   {
+      ptag(tag >> 8);
+      ptagchar(tag);
+   }
+}
+
+static int
+fix_one(FILE *fp, FILE *fpIn, IDAT_info *info, png_uint_32 max_IDAT, int strip)
+{
+   int state = 0;
+      /*   0: at beginning, before first IDAT
+       *   1: read first CMF header byte
+       *   2: read second byte, in first IDAT
+       *   3: after first IDAT
+       *  +4: saw deflate stream end.
+       */
+   int         truncated_idat = 0; /* Count of spurious IDAT bytes */
+   uLong       crc_idat = 0;       /* Running CRC of current IDAT */
+   png_uint_32 len_IDAT = 0;       /* Length of current IDAT */
+   fpos_t      pos_IDAT_length;    /* fpos_t of length field in current IDAT */
+
+   /* The signature: */
+   {
+      png_byte buf[8];
+      rx(fpIn, buf, 8);
+      wx(fp, buf, 8);
+   }
+
+   info->file_size = 45; /* signature + IHDR + IEND */
+
+   for (;;) /* Chunk for loop */
+   {
+      png_uint_32 len = r32(fpIn);
+      png_uint_32 tag = r32(fpIn);
+
+      if (tag == png_IHDR)
+      {
+         /* Need width, height, color type, bit depth and interlace for the
+          * file.
+          */
+         info->width = r32(fpIn);
+         info->height = r32(fpIn);
+         rx(fpIn, &info->bit_depth, 1);
+         rx(fpIn, &info->color_type, 1);
+         rx(fpIn, &info->compression_method, 1);
+         rx(fpIn, &info->filter_method, 1);
+         rx(fpIn, &info->interlace_method, 1);
+
+         /* And write the information. */
+         w32(fp, len);
+         w32(fp, tag);
+         w32(fp, info->width);
+         w32(fp, info->height);
+         wx(fp, &info->bit_depth, 1);
+         wx(fp, &info->color_type, 1);
+         wx(fp, &info->compression_method, 1);
+         wx(fp, &info->filter_method, 1);
+         wx(fp, &info->interlace_method, 1);
+
+         /* Copy the CRC */
+         copy(fp, fpIn, 4);
+      }
+
+      else if (tag == png_IEND)
+      {
+         /* Ok, write an IEND chunk and finish. */
+         w32(fp, 0);
+         w32(fp, png_IEND);
+         wcrc(fp, crc_IEND);
+         break;
+      }
+
+      else if (tag == png_IDAT && len > 0)
+      {
+         /* Write the chunk header now if it hasn't been written yet */
+         if (len_IDAT == 0)
+         {
+            /* The length is set at the end: */
+            safe_getpos(fp, &pos_IDAT_length);
+            w32(fp, max_IDAT); /* length, not yet written */
+            w32(fp, png_IDAT);
+
+            if (state == 0) /* Start of first IDAT */
+            {
+               safe_getpos(fp, &info->header_pos);
+               /* This will become info->crc_tail: */
+               crc_idat = crc32(0L, Z_NULL, 0);
+            }
+
+            else
+               crc_idat = crc_IDAT_head;
+         }
+
+         /* Do the zlib 2-byte header, it gets written out but not added
+          * to the CRC (yet):
+          */
+         while (len > 0 && state < 2)
+         {
+            rx(fpIn, info->header + state, 1);
+            wx(fp, info->header + state, 1);
+            ++len_IDAT;
+            --len;
+
+            if (state++ == 1)
+            {
+               /* The zlib stream is used to validate the compressed IDAT
+                * data in the most relaxed way possible.
+                */
+               png_byte bdummy;
+               int ret;
+
+               z_idat.next_in = info->header;
+               z_idat.avail_in = 2;
+               z_idat.next_out = &bdummy; /* Else Z_STREAM_ERROR! */
+               z_idat.avail_out = 0;
+
+               ret = inflate(&z_idat, Z_NO_FLUSH);
+               if (ret != Z_OK || z_idat.avail_in != 0)
+               {
+                  fprintf(stderr,
+                     "%s: unexpected/invalid inflate result %d \"%s\"\n",
+                     name, ret, z_idat.msg);
+                  return 1;
+               }
+            }
+         } /* while in zlib header */
+
+         /* Process further bytes in the IDAT chunk */
+         while (len > 0 && state < 4)
+         {
+            png_byte b;
+
+            rx(fpIn, &b, 1);
+            --len;
+
+            /* Do this 1 byte at a time to guarantee
+             * detecting errors (in particular zlib can skip the
+             * 'too-far-back' error if the output buffer is bigger than
+             * the window size.)
+             */
+            z_idat.next_in = &b;
+            z_idat.avail_in = 1;
+
+            do
+            {
+               int ret;
+               png_byte bout;
+
+               z_idat.next_out = &bout;
+               z_idat.avail_out = 1;
+
+               ret = inflate(&z_idat, Z_SYNC_FLUSH);
+
+               if (z_idat.avail_out == 0)
+                  ++info->image_size;
+
+               switch (ret)
+               {
+                  case Z_OK:
+                     /* Just keep going */
+                     break;
+
+                  case Z_BUF_ERROR:
+                     if (z_idat.avail_in > 0)
+                     {
+                        fprintf(stderr,
+                           "%s: unexpected buffer error \"%s\"\n",
+                           name, z_idat.msg);
+                        return 1;
+                     }
+                     goto end_loop;
+
+                  case Z_STREAM_END:
+                     /* End of stream */
+                     state |= 4;
+                     goto end_loop;
+
+                  default:
+                     fprintf(stderr, "%s: bad zlib stream %d, \"%s\"\n",
+                        name, ret, z_idat.msg);
+                     return 1;
+               }
+            } while (z_idat.avail_in > 0 || z_idat.avail_out == 0);
+
+            /* The byte need not be consumed, if, for example, there is a
+             * spurious byte after the end of the zlib data.
+             */
+         end_loop:
+            if (z_idat.avail_in == 0)
+            {
+               /* Write it and update the length information and running
+                * CRC.
+                */
+               wx(fp, &b, 1);
+               crc_idat = crc32(crc_idat, &b, 1);
+               ++len_IDAT;
+            }
+
+            else
+               ++truncated_idat;
+
+            if (len_IDAT >= max_IDAT || state >= 4)
+            {
+               /* Either the IDAT chunk is full or we've seen the end of
+                * the deflate stream, or both.  Flush the chunk and handle
+                * the details of the first chunk.
+                */
+               fpos_t save;
+
+               if ((state & 3) < 3) /* First IDAT */
+               {
+                  safe_getpos(fp, &info->crc_pos);
+                  info->crc_tail = crc_idat;
+                  info->len_tail = len_IDAT-2;
+               }
+
+               /* This is not the correct value for the first IDAT! */
+               wcrc(fp, crc_idat);
+               state |= 3;
+
+               /* Update the length if it is not max_IDAT: */
+               if (len_IDAT != max_IDAT)
+               {
+                  safe_getpos(fp, &save);
+                  safe_setpos(fp, &pos_IDAT_length);
+                  w32(fp, len_IDAT);
+                  safe_setpos(fp, &save);
+               }
+
+               /* Add this IDAT to the file size: */
+               info->file_size += 12 + len_IDAT;
+            }
+         } /* while len > 0 && state < 4 */
+
+         /* The above loop only exits on 0 bytes left or end of stream. If
+          * the stream ended with bytes left, discard them:
+          */
+         if (len > 0)
+         {
+            truncated_idat += len;
+            /* Skip those bytes and the CRC */
+            skip_bytes(fpIn, len+4);
+         }
+
+         else
+            skip_bytes(fpIn, 4); /* The CRC */
+      } /* IDAT and len > 0 */
+
+      else
+      {
+         int skip = 0;
+
+         if (tag == png_IDAT)
+            skip = 1;
+
+         else if (state == 0)
+         {
+            /* Chunk before IDAT */
+            if (!skip) switch (strip)
+            {
+               case 0: /* Don't strip */
+                  break;
+
+               case 1:  /* Keep gAMA, sRGB */
+                  if (tag == png_gAMA || tag == png_sRGB)
+                     break;
+                  /* Fall through */
+
+               default: /* Keep only IHDR, PLTE, tRNS */
+                  if (tag == png_IHDR || tag == png_PLTE || tag == png_tRNS)
+                     break;
+
+                  skip = 1;
+                  break;
+            }
+         }
+
+         else if (state >= 4)
+         {
+            /* Keep nothing after IDAT if stripping: */
+            skip = strip;
+         }
+
+         else
+         {
+            /* This is either an unterminated deflate stream or a spurious
+             * non-IDAT chunk in the list of IDAT chunks.  Both are fatal
+             * errors.
+             */
+            fprintf(stderr, "%s: tag '", name);
+            ptag(tag);
+            fprintf(stderr, "' after unterminated IDAT\n");
+            break;
+         }
+
+         /* Skip or send? */
+         if (skip)
+         {
+            if (tag != png_IDAT && (tag & 0x20000000) == 0)
+            {
+               fprintf(stderr, "%s: unknown critical chunk '", name);
+               ptag(tag);
+               fprintf(stderr, "'\n");
+               return 1;
+            }
+
+            /* Skip this tag */
+            if (fseek(fpIn, len+4, SEEK_CUR))
+            {
+               perror(name);
+               fprintf(stderr, "%s: seek failed\n", name);
+               return 1;
+            }
+         }
+
+         else /* Keep this tag */
+         {
+            w32(fp, len);
+            w32(fp, tag);
+            copy(fp, fpIn, len+4);
+            info->file_size += 12+len;
+         }
+      } /* Not IDAT or len == 0 */
+   } /* Chunk for loop */
+
+   /* Break out of the loop on error or end */
+   if (state >= 4)
+   {
+      if (truncated_idat)
+         fprintf(stderr, "%s: removed %d bytes from end of IDAT\n", name,
+            truncated_idat);
+
+      return 0; /* success */
+   }
+
+   /* This is somewhat generic but it works: */
+   fprintf(stderr, "%s: unterminated/truncated PNG (%d)\n", name, state);
+
+   return 1;
+}
+
+static FILE *fpIn;
+
+static int
+fix_file(FILE *fp, const char *file, png_uint_32 max_IDAT, int inplace,
+   int strip, int optimize, const char *output)
+{
+   IDAT_info info;
+   int imageBits, oldBits, bestBits, lowBits, newBits, ok_read;
+
+   memset(&info, 0, sizeof info);
+
+   name = file;
+   idat_error = 0;
+
+   /* fpIn is closed by the caller if necessary */
+   fpIn = fopen(file, "rb");
+   if (fpIn == NULL)
+   {
+      perror(file);
+      fprintf(stderr, "%s: open failed\n", file);
+      return 1;
+   }
+
+   /* With no arguments just check this file */
+   if (optimize == 0 && strip == 0 && output == NULL)
+      return !read_png(fpIn);
+
+   /* Otherwise, maybe, fix it */
+   if (fix_one(fp, fpIn, &info, max_IDAT, strip))
+      return 1;
+
+   /* oldBits may be invalid, imageBits is always OK, newBits always records the
+    * actual window bits of the temporary file (fp).
+    */
+   bestBits = imageBits = image_windowBits(&info);
+   newBits = oldBits = 8+(info.header[0] >> 4);
+   ok_read = 0; /* records a successful read */
+
+   /* Find the optimal (lowest) newBits */
+   if (optimize)
+      for (lowBits=8; lowBits < bestBits;)
+   {
+      /* This will always set 'newBits' to a value lower than 'bestBits' because
+       * 'lowBits' is less than 'bestBits':
+       */
+      newBits = (bestBits + lowBits) >> 1;
+
+      set_bits(file, fp, &info, newBits);
+
+      rewind(fp);
+      idat_error = 0;
+
+      if (!read_png(fp))
+      {
+         /* If idat_error is *not* set this is some other problem */
+         if (!idat_error)
+            return 1;
+
+         /* This is the hypothetical case where the IDAT has too much data *and*
+          * the window size is wrong.  In fact this should never happen because
+          * of the way libpng handles a deflate stream that produces extra data.
+          */
+         if (newBits >= imageBits)
+         {
+            fprintf(stderr, "%s: imageBits(%d) too low (%d)\n", file, imageBits,
+               newBits);
+            return 1;
+         }
+
+         if (lowBits <= newBits)
+            lowBits = newBits+1;
+      }
+
+      else
+      {
+         bestBits = newBits;
+         ok_read = 1;
+      }
+   }
+
+   else if (bestBits > oldBits)
+   {
+      /* See if the original value is ok */
+      rewind(fp);
+      idat_error = 0;
+
+      if (read_png(fp))
+      {
+         ok_read = 1;
+         bestBits = oldBits;
+      }
+
+      else if (!idat_error)
+         return 1;
+
+      /* Otherwise there is an IDAT error and no optimization is being done, so
+       * just use imageBits (which is already set in bestBits).
+       */
+   }
+
+   if (newBits != bestBits)
+   {
+      /* Update the header to the required value */
+      newBits = bestBits;
+
+      set_bits(file, fp, &info, newBits);
+   }
+
+   if (!ok_read)
+   {
+      /* bestBits has not been checked */
+      idat_error = 0;
+      rewind(fp);
+      ok_read = read_png(fp);
+
+      if (idat_error)
+      {
+         /* This should never happen */
+         fprintf(stderr, "%s: imageBits(%d) too low [%d]\n", file, imageBits,
+            newBits);
+         return 1;
+      }
+
+      /* This means that the PNG has some other error */
+      if (!ok_read)
+         return 1;
+   }
+
+   /* Have a valid newBits */
+   if (optimize)
+      printf("%2d %2d %2d %s %s %d %s\n", newBits, oldBits, imageBits,
+         newBits < imageBits ? "<" : "=",
+         newBits < oldBits ? "reduce  " :
+            (newBits > oldBits ? "INCREASE" : "ok      "),
+         newBits - oldBits, name);
+
+#  ifdef PNG_MAXIMUM_INFLATE_WINDOW
+      /* Because setting libpng to use the maximum window bits breaks the
+       * read_png test above.
+       */
+      if (set_option)
+         return 0;
+#  endif
+
+   if (output != NULL || (inplace && (bestBits != oldBits || strip)))
+   {
+      FILE *fpOut;
+      
+      if (output != NULL)
+         fpOut = fopen(output, "wb");
+
+      else
+      {
+         fpOut = freopen(file, "wb", fpIn);
+         fpIn = NULL;
+      }
+      
+      if (fpOut == NULL)
+      {
+         perror(output);
+         fprintf(stderr, "%s: %s: open failed\n", file, output);
+         exit(3);
+      }
+
+      rewind(fp);
+      copy(fpOut, fp, info.file_size);
+
+      if (fflush(fpOut) || ferror(fpOut) || fclose(fpOut))
+      {
+         perror(output != NULL ? output : file);
+         fprintf(stderr, "%s: %s: close failed\n", file, output);
+         if (output != NULL)
+            remove(output);
+         exit(3);
+      }
+   }
+
+   return 0;
+}
+
+static void
+usage(const char *prog, int rc)
+{
+   fprintf(stderr,
+      "Usage: %s {[options] png-file}\n", prog);
+   fprintf(stderr,
+      "  Tests, optimizes and fixes the zlib header in PNG files.\n"
+      "  Optionally, when fixing, strips ancilliary chunks from the file.\n");
+   fprintf(stderr,
+      "\nOptions:\n"
+#  ifdef PNG_MAXIMUM_INFLATE_WINDOW
+      "  --test: Test the PNG_MAXIMUM_INFLATE_WINDOW option.\n"
+#  endif
+      "  --optimize (-o): Find the smallest deflate window size for the file.\n"
+      "                   Also outputs a summary for each file.\n"
+      "  --strip (-s): Remove chunks except for IHDR, PLTE, IEND, tRNS, gAMA,\n"
+      "                sRGB.  If given twice remove gAMA and sRGB as well.\n"
+      "  --errors (-e): Output errors from libpng (except too-far-back).\n");
+   fprintf(stderr,
+      "  --warnings (-w): Output warnings from libpng.\n"
+      "  --verbose (-v): Output more verbose messages.\n"
+      "  --max=<number>: Output IDAT chunks sized <mumber>.  If not given the\n"
+      "                  the IDAT chunks will be the maximum size permitted\n"
+      "                  (2^31-1 bytes.)\n"
+      "  --out=<file>: Save the result for the next PNG to <file>.\n"
+      "  --inplace (-i): Modify the file in place.\n");
+   fprintf(stderr,
+      "\nExit codes:\n"
+      "  0: Success, all files pass the test, all output written ok.\n"
+      "  1: At least one file had a read error, all files checked.\n"
+      "  2: A file had an unrecoverable error (integer overflow, bad format),\n"
+      "     the program exited immediately, without processing further files.\n"
+      "  3: An IO or out of memory error, or a file could not be opened.h\n");
+   fprintf(stderr,
+      "\nDescription:\n"
+      "  %s checks each PNG file on the command line for errors.\n"
+      "  By default it is silent and just exits with an error code (as above)\n"
+      "  if any error is detected.  With --optimize, --strip or --out,\n"
+      "  however, the zlib \"invalid distance too far back\" error is fixed\n"
+      "  and the program exits with a 0 success code unless some other error\n"
+      "  is encountered.\n"
+      "\n", prog);
+   fprintf(stderr,
+      "  Use --errors to display the other errors, use --optimize to test\n"
+      "  different values for the deflate \"window bits\" parameter and find\n"
+      "  the smallest that works.\n"
+      "\n"
+      "  Notice that some PNG files with the zlib header problem can still be\n"
+      "  read by libpng.  This program will still detect the error.\n"
+      "\n");
+   fprintf(stderr,
+      "  The output produced with --optimize is as follows:\n"
+      "\n"
+      "     opt-bits curr-bits image-bits opt-flag opt-type change file\n"
+      "\n"
+      "   opt-bits:   The minimum window bits (8-15) that works, if the file\n"
+      "               is written this is the value that will be stored.\n"
+      "   curr-bits:  The value currently stored in the file.\n");
+   fprintf(stderr,
+      "   image-bits: The window bits value corresponding to the size of the\n"
+      "               uncompressed PNG image data.  When --optimize is not\n"
+      "               given but --strip is this value will be used if lower\n"
+      "               than the current value.\n"
+      "   opt-flag: < if the optimized bit value is less than that implied by\n"
+      "               the PNG image size (opt-bits < image-bits)\n"
+      "             = if optimization is not possible (opt-bits = image-bits)\n"
+      "   opt-type: reduce   if opts-bits < curr-bits\n");
+   fprintf(stderr,
+      "             ok       if opt-bits = curr-bits (no change required)\n"
+      "             INCREASE if opt-bits > curr-bits (the file has the bug)\n"
+      "   change:     opt-bits - curr-bits, so negative if optimization is\n"
+      "               possible, 0 if no change is required, positive if the\n"
+      "               bug is present.\n"
+      "   file:       The file name.\n");
+
+   exit(rc);
+}
+
+int
+main(int argc, const char **argv)
+{
+   int err, strip = 0, optimize = 0, inplace = 0, done = 0;
+   png_uint_32 max_IDAT = 0x7fffffff;
+   FILE *fp;
+   const char *outfile = NULL;
+   const char *prog = *argv;
+   static const png_byte idat_bytes[4] = { 73,  68,  65,  84 };
+   static const png_byte iend_bytes[4] = { 73,  69,  78,  68 };
+
+   /* Initialize this first, could be stored as a constant: */
+   crc_IEND = crc_IDAT_head = crc32(0L, Z_NULL, 0);
+   crc_IDAT_head = crc32(crc_IDAT_head, idat_bytes, 4);
+   crc_IEND = crc32(crc_IEND, iend_bytes, 4);
+
+   z_idat.next_in = Z_NULL;
+   z_idat.avail_in = 0;
+   z_idat.zalloc = Z_NULL;
+   z_idat.zfree = Z_NULL;
+   z_idat.opaque = Z_NULL;
+
+   err = inflateInit(&z_idat);
+   if (err != Z_OK)
+   {
+      fprintf(stderr, "inflateInit failed %d \"%s\"\n", err, z_idat.msg);
+      inflateEnd(&z_idat);
+      return 3;
+   }
+
+   fp = tmpfile();
+   if (fp == NULL)
+   {
+      perror("tmpfile");
+      fprintf(stderr, "could not open a temporary file\n");
+      return 3;
+   }
+
+   err = 0;
+   while (--argc > 0)
+   {
+      ++argv;
+
+      if (strcmp(*argv, "--inplace") == 0 || strcmp(*argv, "-i") == 0)
+         ++inplace;
+
+      else if (strncmp(*argv, "--max=", 6) == 0)
+         max_IDAT = (png_uint_32)atol(6+*argv);
+
+      else if (strcmp(*argv, "--optimize") == 0 || strcmp(*argv, "-o") == 0)
+         ++optimize;
+
+      else if (strncmp(*argv, "--out=", 6) == 0)
+         outfile = 6+*argv;
+
+      else if (strcmp(*argv, "--strip") == 0 || strcmp(*argv, "-s") == 0)
+         ++strip;
+
+      else if (strcmp(*argv, "--errors") == 0 || strcmp(*argv, "-e") == 0)
+         ++errors;
+
+      else if (strcmp(*argv, "--warnings") == 0 || strcmp(*argv, "-w") == 0)
+         ++warnings;
+
+      else if (strcmp(*argv, "--verbose") == 0 || strcmp(*argv, "-v") == 0)
+         ++verbose;
+
+#     ifdef PNG_MAXIMUM_INFLATE_WINDOW
+         else if (strcmp(*argv, "--test") == 0)
+            ++set_option;
+#     endif
+
+      else if ((*argv)[0] == '-')
+         usage(prog, 3);
+
+      else
+      {
+         int ret;
+
+         err +=
+            fix_file(fp, *argv, max_IDAT, inplace, strip, optimize, outfile);
+
+         if (fpIn != NULL)
+         {
+            fclose(fpIn);
+            fpIn = NULL;
+         }
+
+         z_idat.next_in = z_idat.next_out = Z_NULL;
+         z_idat.avail_in = z_idat.avail_out = 0;
+         ret = inflateReset(&z_idat);
+         if (ret != Z_OK)
+         {
+            fprintf(stderr, "inflateReset failed %d \"%s\"\n", ret, z_idat.msg);
+            inflateEnd(&z_idat);
+            return 3;
+         }
+
+         rewind(fp);
+         outfile = NULL;
+         ++done;
+      }
+   }
+
+   inflateEnd(&z_idat);
+
+   if (!done)
+      usage(prog, 0);
+
+   return err != 0;
+}
+
+#else /* PNG_ZLIB_VERNUM < 0x1240 */
+int
+main(void)
+{
+   fprintf(stderr,
+      "png-fix-too-far-back needs libpng with a zlib >=1.2.4 (not 0x%x)\n",
+      PNG_ZLIB_VERNUM);
+   return 77;
+}
+#endif /* PNG_ZLIB_VERNUM */
+
+#else /* No read support */
+
+int
+main(void)
+{
+   fprintf(stderr, "png-fix-too-far-back does not work without read support\n");
+   return 77;
+}
+#endif /* PNG_READ_SUPPORTED */