5403 lines
181 KiB
C
5403 lines
181 KiB
C
/*
|
|
* pngcheck: Authenticate the structure of a PNG file and dump info about
|
|
* it if desired.
|
|
*
|
|
* This program checks the PNG signature bytes (with tests for various forms
|
|
* of text-mode corruption), chunks (CRCs, dependencies, out-of-range values),
|
|
* and compressed image data (IDAT zlib stream). In addition, it optionally
|
|
* dumps the contents of PNG, JNG and MNG image streams in more-or-less human-
|
|
* readable form.
|
|
*
|
|
* NOTE: this program is currently NOT EBCDIC-compatible!
|
|
* (as of July 2007)
|
|
*
|
|
* Maintainer: Greg Roelofs <newt@pobox.com>
|
|
* ChangeLog: see CHANGELOG file
|
|
*/
|
|
|
|
/*============================================================================
|
|
*
|
|
* Copyright 1995-2017 by Alexander Lehmann <lehmann@usa.net>,
|
|
* Andreas Dilger <adilger@enel.ucalgary.ca>,
|
|
* Glenn Randers-Pehrson <randeg@alum.rpi.edu>,
|
|
* Greg Roelofs <newt@pobox.com>,
|
|
* John Bowler <jbowler@acm.org>,
|
|
* Tom Lane <tgl@sss.pgh.pa.us>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose and without fee is hereby granted, provided
|
|
* that the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation. This software is provided "as is" without express or
|
|
* implied warranty.
|
|
*
|
|
*===========================================================================*/
|
|
|
|
#define VERSION "2.4.0-beta09 of 9 January 2017"
|
|
|
|
/*
|
|
* NOTE: current MNG support is informational; error-checking is MINIMAL!
|
|
*
|
|
*
|
|
* Currently supported chunks, in order of appearance in pngcheck() function:
|
|
*
|
|
* IHDR JHDR MHDR // PNG/JNG/MNG header chunks
|
|
*
|
|
* PLTE IDAT IEND // critical PNG chunks
|
|
*
|
|
* bKGD cHRM fRAc gAMA gIFg gIFt gIFx hIST // ancillary PNG chunks
|
|
* iCCP iTXt oFFs pCAL pHYs sBIT sCAL sPLT
|
|
* sRGB tEXt zTXt tIME tRNS
|
|
*
|
|
* cmOD cmPP cpIp mkBF mkBS mkBT mkTS pcLb // known private PNG chunks
|
|
* prVW spAL // [msOG = ??]
|
|
*
|
|
* JDAT JSEP // critical JNG chunks
|
|
*
|
|
* DHDR FRAM SAVE SEEK nEED DEFI BACK MOVE // MNG chunks
|
|
* CLON SHOW CLIP LOOP ENDL PROM fPRI eXPI
|
|
* BASI IPNG PPLT PAST TERM DISC pHYg DROP
|
|
* DBYK ORDR MAGN MEND
|
|
*
|
|
* Known unregistered, "public" chunks (i.e., invalid and now flagged as such):
|
|
*
|
|
* pRVW nULL tXMP
|
|
*
|
|
*
|
|
* GRR to do:
|
|
* - normalize error levels (mainly usage of kMinorError vs. kMajorError)
|
|
* - fix tEXt chunk: small buffers or lots of text => truncation
|
|
* (see pngcheck-1.99.4-test.c.dif)
|
|
* - fix iCCP, sPLT chunks: small buffers or large chunks => truncation?
|
|
* - update existing MNG support to version 1.0 (DHDR bug just fixed 2010!)
|
|
* - add JNG restrictions to bKGD
|
|
* - allow top-level ancillary PNGs in MNG (i.e., subsequent ones may be NULL)
|
|
* * add MNG profile report based on actual chunks found
|
|
* - split out each chunk's code into XXXX() function (e.g., IDAT(), tRNS())
|
|
* - DOS/Win32 wildcard support beyond emx+gcc, MSVC (Borland wildargs.obj?)
|
|
* - EBCDIC support (minimal?)
|
|
* - go back and make sure validation checks not dependent on verbosity level
|
|
*
|
|
*
|
|
* GRR NOTE: The MNG "top level" concept is not explicitly defined anywhere
|
|
* in the MNG 1.0 spec, but it refers to "global" chunks/values and apparently
|
|
* means before any embedded PNG or JNG images appear (i.e., before any IHDR
|
|
* or JHDR chunks are encountered). The top_level variable is set accordingly.
|
|
*/
|
|
|
|
/*
|
|
* Compilation example (GNU C, command line; replace "/zlibpath" appropriately):
|
|
*
|
|
* without zlib:
|
|
* gcc -O -o pngcheck pngcheck.c
|
|
* with zlib support (recommended):
|
|
* gcc -O -DUSE_ZLIB -I/zlibpath -o pngcheck pngcheck.c -L/zlibpath -lz
|
|
* or (static zlib):
|
|
* gcc -O -DUSE_ZLIB -I/zlibpath -o pngcheck pngcheck.c /zlibpath/libz.a
|
|
*
|
|
* Windows compilation example (MSVC, command line, assuming VCVARS32.BAT or
|
|
* whatever has been run):
|
|
*
|
|
* without zlib:
|
|
* cl -nologo -O -W3 -DWIN32 -c pngcheck.c
|
|
* link -nologo pngcheck.obj setargv.obj
|
|
* with zlib support (note that Win32 zlib is compiled as a DLL by default):
|
|
* cl -nologo -O -W3 -DWIN32 -DUSE_ZLIB -I/zlibpath -c pngcheck.c
|
|
* link -nologo pngcheck.obj setargv.obj \zlibpath\zlib.lib
|
|
* [copy pngcheck.exe and zlib.dll to installation directory]
|
|
*
|
|
* "setargv.obj" is included with MSVC and will be found if the batch file has
|
|
* been run. Either Borland or Watcom (both?) may use "wildargs.obj" instead.
|
|
* Both object files serve the same purpose: they expand wildcard arguments
|
|
* into a list of files on the local file system, just as Unix shells do by
|
|
* default ("globbing"). Note that mingw32 + gcc (Unix-like compilation
|
|
* environment for Windows) apparently expands wildcards on its own, so no
|
|
* special object files are necessary for it. emx + gcc for OS/2 (and possibly
|
|
* rsxnt + gcc for Windows NT) has a special _wildcard() function call, which
|
|
* is already included at the top of main() below.
|
|
*
|
|
* zlib info: http://www.zlib.net/
|
|
* PNG/MNG/JNG info: http://www.libpng.org/pub/png/
|
|
* http://www.libpng.org/pub/mng/ and
|
|
* ftp://ftp.simplesystems.org/pub/libpng/mng/
|
|
* pngcheck sources: http://www.libpng.org/pub/png/apps/pngcheck.html
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#ifdef __riscos
|
|
/* not sure if this will work (fragile!), but relatively clean... */
|
|
struct stat { long st_size; };
|
|
# define stat(f,s) _swix(8 /*OS_File*/, 3 | 1<<27, 17, f, s.st_size)
|
|
# define isatty(fd) (!__iob[fd].__file)
|
|
#else
|
|
# include <fcntl.h>
|
|
# if defined(__MWERKS__) && defined(macintosh) /* pxm for CodeWarrior */
|
|
# include <types.h>
|
|
# include <stat.h>
|
|
# elif defined(applec) || defined(THINK_C) /* via Mark Fleming; not tested */
|
|
# include <Types.h>
|
|
# include <ToolUtils.h>
|
|
# else
|
|
# include <sys/types.h>
|
|
# include <sys/stat.h>
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(unix) || (defined(__MWERKS__) && defined(macintosh)) /* pxm */
|
|
# include <unistd.h> /* isatty() */
|
|
#endif
|
|
#ifdef WIN32
|
|
# include <io.h>
|
|
#endif
|
|
|
|
#ifdef USE_ZLIB
|
|
# include <zlib.h>
|
|
#endif
|
|
|
|
typedef unsigned char uch;
|
|
typedef unsigned short ush;
|
|
typedef unsigned long ulg;
|
|
|
|
/* printbuf state variables */
|
|
typedef struct printbuf_state {
|
|
int cr;
|
|
int lf;
|
|
int nul;
|
|
int control;
|
|
int esc;
|
|
} printbuf_state;
|
|
|
|
/* int main (int argc, char *argv[]); */
|
|
void usage (FILE *fpMsg);
|
|
#ifndef USE_ZLIB
|
|
void make_crc_table (void);
|
|
ulg update_crc (ulg crc, uch *buf, int len);
|
|
#endif
|
|
ulg getlong (FILE *fp, char *fname, char *where);
|
|
void putlong (FILE *fpOut, ulg ul);
|
|
void init_printbuf_state (printbuf_state *prbuf);
|
|
void print_buffer (printbuf_state *prbuf, uch *buffer, int size, int indent);
|
|
void report_printbuf (printbuf_state *prbuf, char *fname, char *chunkid);
|
|
int keywordlen (uch *buffer, int maxsize);
|
|
const char *getmonth (int m);
|
|
int ratio (ulg uc, ulg c);
|
|
ulg gcf (ulg a, ulg b);
|
|
int pngcheck (FILE *fp, char *_fname, int searching, FILE *fpOut);
|
|
int pnginfile (FILE *fp, char *fname, int ipng, int extracting);
|
|
void pngsearch (FILE *fp, char *fname, int extracting);
|
|
int check_magic (uch *magic, char *fname, int which);
|
|
int check_chunk_name (char *chunk_name, char *fname);
|
|
int check_keyword (uch *buffer, int maxsize, int *pKeylen,
|
|
char *keyword_name, char *chunkid, char *fname);
|
|
int check_text (uch *buffer, int maxsize, char *chunkid, char *fname);
|
|
int check_ascii_float (uch *buffer, int len, char *chunkid, char *fname);
|
|
|
|
#define BS 32000 /* size of read block for CRC calculation (and zlib) */
|
|
|
|
/* Mark's macros to extract big-endian short and long ints: */
|
|
#define SH(p) ((ush)(uch)((p)[1]) | ((ush)(uch)((p)[0]) << 8))
|
|
#define LG(p) ((ulg)(SH((p)+2)) | ((ulg)(SH(p)) << 16))
|
|
|
|
/* for check_magic(): */
|
|
#define DO_PNG 0
|
|
#define DO_MNG 1
|
|
#define DO_JNG 2
|
|
|
|
/* GRR 20070704: borrowed from GRR from/mailx hack */
|
|
#define COLOR_NORMAL "\033[0m"
|
|
#define COLOR_RED_BOLD "\033[40;31;1m"
|
|
#define COLOR_RED "\033[40;31m"
|
|
#define COLOR_GREEN_BOLD "\033[40;32;1m"
|
|
#define COLOR_GREEN "\033[40;32m"
|
|
#define COLOR_YELLOW_BOLD "\033[40;33;1m"
|
|
#define COLOR_YELLOW "\033[40;33m" /* chunk names */
|
|
#define COLOR_BLUE_BOLD "\033[40;34;1m"
|
|
#define COLOR_BLUE "\033[40;34m"
|
|
#define COLOR_MAGENTA_BOLD "\033[40;35;1m"
|
|
#define COLOR_MAGENTA "\033[40;35m"
|
|
#define COLOR_CYAN_BOLD "\033[40;36;1m"
|
|
#define COLOR_CYAN "\033[40;36m"
|
|
#define COLOR_WHITE_BOLD "\033[40;37;1m" /* filenames, filter seps */
|
|
#define COLOR_WHITE "\033[40;37m"
|
|
|
|
#define isASCIIalpha(x) (ascii_alpha_table[x] & 0x1)
|
|
|
|
#define ANCILLARY(chunkID) ((chunkID)[0] & 0x20)
|
|
#define PRIVATE(chunkID) ((chunkID)[1] & 0x20)
|
|
#define RESERVED(chunkID) ((chunkID)[2] & 0x20)
|
|
#define SAFECOPY(chunkID) ((chunkID)[3] & 0x20)
|
|
#define CRITICAL(chunkID) (!ANCILLARY(chunkID))
|
|
#define PUBLIC(chunkID) (!PRIVATE(chunkID))
|
|
|
|
#define set_err(x) global_error = ((global_error < (x))? (x) : global_error)
|
|
#define is_err(x) (global_error > (x) || (!force && global_error == (x)))
|
|
#define no_err(x) (global_error < (x) || (force && global_error == (x)))
|
|
|
|
enum {
|
|
kOK = 0,
|
|
kWarning, /* could be an error in some circumstances but not all */
|
|
kCommandLineError, /* pilot error */
|
|
kMinorError, /* minor spec errors (e.g., out-of-range values) */
|
|
kMajorError, /* file corruption, invalid chunk length/layout, etc. */
|
|
kCriticalError /* unexpected EOF or other file(system) error */
|
|
};
|
|
|
|
/* Command-line flag variables */
|
|
int verbose = 0; /* print chunk info */
|
|
int quiet = 0; /* print only error messages */
|
|
int printtext = 0; /* print tEXt chunks */
|
|
int printpal = 0; /* print PLTE/tRNS/hIST/sPLT contents */
|
|
int color = 0; /* print with ANSI colors to spice things up */
|
|
int sevenbit = 0; /* escape characters >=160 */
|
|
int force = 0; /* continue even if an error occurs (CRC error, etc) */
|
|
int check_windowbits = 1; /* more stringent zlib stream-checking */
|
|
int suppress_warnings = 0; /* don't fuss about ambiguous stuff */
|
|
int search = 0; /* hunt for PNGs in the file... */
|
|
int extract = 0; /* ...and extract them to arbitrary file names. */
|
|
int png = 0; /* it's a PNG */
|
|
int mng = 0; /* it's a MNG instead of a PNG (won't work in pipe) */
|
|
int jng = 0; /* it's a JNG */
|
|
|
|
int global_error = kOK; /* the current error status */
|
|
uch buffer[BS];
|
|
|
|
/* what the PNG, MNG and JNG magic numbers should be */
|
|
static const uch good_PNG_magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
|
|
static const uch good_MNG_magic[8] = {138, 77, 78, 71, 13, 10, 26, 10};
|
|
static const uch good_JNG_magic[8] = {139, 74, 78, 71, 13, 10, 26, 10};
|
|
|
|
/* GRR FIXME: could merge all three of these into single table (bit fields) */
|
|
|
|
/* GRR 20061203: for "isalpha()" that works even on EBCDIC machines */
|
|
static const uch ascii_alpha_table[256] = {
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
|
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
/* GRR 20070707: list of forbidden characters in various keywords */
|
|
static const uch latin1_keyword_forbidden[256] = {
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
/* GRR 20070707: list of discouraged (control) characters in tEXt/zTXt text */
|
|
static const uch latin1_text_discouraged[256] = {
|
|
1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
#ifdef USE_ZLIB
|
|
int first_idat = 1; /* flag: is this the first IDAT chunk? */
|
|
int zlib_error = 0; /* reset in IHDR section; used for IDAT */
|
|
int check_zlib = 1; /* validate zlib stream (just IDATs for now) */
|
|
unsigned zlib_windowbits = 15;
|
|
uch outbuf[BS];
|
|
z_stream zstrm;
|
|
const char **pass_color;
|
|
const char *color_off;
|
|
#else
|
|
ulg crc_table[256]; /* table of CRCs of all 8-bit messages */
|
|
int crc_table_computed = 0; /* flag: has the table been computed? */
|
|
#endif
|
|
|
|
|
|
static const char *inv = "INVALID";
|
|
|
|
|
|
/* PNG stuff */
|
|
|
|
static const char *png_type[] = { /* IHDR, tRNS, BASI, summary */
|
|
"grayscale",
|
|
"INVALID", /* can't use inv as initializer */
|
|
"RGB",
|
|
"palette", /* was "colormap" */
|
|
"grayscale+alpha",
|
|
"INVALID",
|
|
"RGB+alpha"
|
|
};
|
|
|
|
static const char *deflate_type[] = { /* IDAT */
|
|
"superfast",
|
|
"fast",
|
|
"default",
|
|
"maximum"
|
|
};
|
|
|
|
#ifdef USE_ZLIB
|
|
static const char *zlib_error_type[] = { /* IDAT */
|
|
"filesystem error",
|
|
"stream error",
|
|
"data error",
|
|
"memory error",
|
|
"buffering error",
|
|
"version error"
|
|
};
|
|
|
|
static const char *pass_color_enabled[] = { /* IDAT */
|
|
COLOR_NORMAL, /* color_off */
|
|
COLOR_WHITE, /* using 1-based indexing */
|
|
COLOR_BLUE,
|
|
COLOR_GREEN,
|
|
COLOR_YELLOW,
|
|
COLOR_RED,
|
|
COLOR_CYAN,
|
|
COLOR_MAGENTA
|
|
};
|
|
|
|
static const char *pass_color_disabled[] = { /* IDAT */
|
|
"", "", "", "", "", "", "", ""
|
|
};
|
|
#endif /* USE_ZLIB */
|
|
|
|
static const char *eqn_type[] = { /* pCAL */
|
|
"physical_value = p0 + p1 * original_sample / (x1-x0)",
|
|
"physical_value = p0 + p1 * exp(p2 * original_sample / (x1-x0))",
|
|
"physical_value = p0 + p1 * pow(p2, (original_sample / (x1-x0)))",
|
|
"physical_value = p0 + p1 * sinh(p2 * (original_sample - p3) / (x1-x0))"
|
|
};
|
|
|
|
static const int eqn_params[] = { 2, 3, 3, 4 }; /* pCAL */
|
|
|
|
static const char *rendering_intent[] = { /* sRGB */
|
|
"perceptual",
|
|
"relative colorimetric",
|
|
"saturation-preserving",
|
|
"absolute colorimetric"
|
|
};
|
|
|
|
|
|
/* JNG stuff */
|
|
|
|
static const char *jng_type[] = { /* JHDR, summary */
|
|
"grayscale",
|
|
"YCbCr",
|
|
"grayscale+alpha",
|
|
"YCbCr+alpha"
|
|
};
|
|
|
|
|
|
/* MNG stuff */
|
|
|
|
static const char *delta_type[] = { /* DHDR */
|
|
"full image replacement",
|
|
"block pixel addition",
|
|
"block alpha addition",
|
|
"block pixel replacement",
|
|
"block alpha replacement",
|
|
"no change"
|
|
};
|
|
|
|
static const char *termination_condition[] = { /* LOOP */
|
|
"deterministic",
|
|
"decoder discretion",
|
|
"user discretion",
|
|
"external signal"
|
|
};
|
|
|
|
static const char *termination_action[] = { /* TERM */
|
|
"show last frame indefinitely",
|
|
"cease displaying anything",
|
|
"show first frame after TERM",
|
|
"repeat sequence between TERM and MEND"
|
|
};
|
|
|
|
static const char *framing_mode[] = { /* FRAM */
|
|
"no change in framing mode",
|
|
"no background layer; interframe delay before each image displayed",
|
|
"no background layer; interframe delay before each FRAM chunk",
|
|
"interframe delay and background layer before each image displayed",
|
|
"interframe delay and background layer after each FRAM chunk"
|
|
};
|
|
|
|
static const char *change_interframe_delay[] = { /* FRAM */
|
|
"no change in interframe delay",
|
|
"change interframe delay for next subframe",
|
|
"change interframe delay and make default"
|
|
};
|
|
|
|
static const char *change_timeout_and_termination[] = { /* FRAM */
|
|
"no change in timeout and termination",
|
|
"deterministic change in timeout and termination for next subframe",
|
|
"deterministic change in timeout and termination; make default",
|
|
"decoder-discretion change in timeout and termination for next subframe",
|
|
"decoder-discretion change in timeout and termination; make default",
|
|
"user-discretion change in timeout and termination for next subframe",
|
|
"user-discretion change in timeout and termination; make default",
|
|
"change in timeout and termination for next subframe via signal",
|
|
"change in timeout and termination via signal; make default"
|
|
};
|
|
|
|
static const char *change_subframe_clipping_boundaries[] = { /* FRAM */
|
|
"no change in subframe clipping boundaries",
|
|
"change frame clipping boundaries for next subframe",
|
|
"change frame clipping boundaries and make default"
|
|
};
|
|
|
|
static const char *change_sync_id_list[] = { /* FRAM */
|
|
"no change in sync ID list",
|
|
"change sync ID list for next subframe:",
|
|
"change sync ID list and make default:"
|
|
};
|
|
|
|
static const char *clone_type[] = { /* CLON */
|
|
"full",
|
|
"partial",
|
|
"renumber"
|
|
};
|
|
|
|
static const char *do_not_show[] = { /* DEFI, CLON */
|
|
"potentially visible",
|
|
"do not show",
|
|
"same visibility as parent"
|
|
};
|
|
|
|
static const char *show_mode[] = { /* SHOW */
|
|
"make objects potentially visible and display",
|
|
"make objects invisible",
|
|
"display potentially visible objects",
|
|
"make objects potentially visible but do not display",
|
|
"toggle potentially visible and invisible objects; display visible ones",
|
|
"toggle potentially visible and invisible objects but do not display any",
|
|
"make next object potentially visible and display; make rest invisible",
|
|
"make next object potentially visible but do not display; make rest invisible"
|
|
};
|
|
|
|
static const char *entry_type[] = { /* SAVE */
|
|
"segment with full info",
|
|
"segment",
|
|
"subframe",
|
|
"exported image"
|
|
};
|
|
|
|
static const char *pplt_delta_type[] = { /* PPLT */
|
|
"replacement RGB samples",
|
|
"delta RGB samples",
|
|
"replacement alpha samples",
|
|
"delta alpha samples",
|
|
"replacement RGBA samples",
|
|
"delta RGBA samples"
|
|
};
|
|
|
|
static const char *composition_mode[] = { /* PAST */
|
|
"composite over",
|
|
"replace",
|
|
"composite under"
|
|
};
|
|
|
|
static const char *orientation[] = { /* PAST */
|
|
"same as source image",
|
|
"flipped left-right then up-down",
|
|
"flipped left-right",
|
|
"flipped up-down",
|
|
"tiled with source image"
|
|
};
|
|
|
|
static const char *order_type[] = { /* ORDR */
|
|
"anywhere",
|
|
"after IDAT and/or JDAT or JDAA",
|
|
"before IDAT and/or JDAT or JDAA",
|
|
"before IDAT but not before PLTE",
|
|
"before IDAT but not after PLTE"
|
|
};
|
|
|
|
static const char *magnification_method[] = { /* MAGN */
|
|
"no magnification",
|
|
"pixel replication of all samples",
|
|
"linear interpolation of all samples",
|
|
"replication of all samples from nearest pixel",
|
|
"linear interpolation of color, nearest-pixel replication of alpha",
|
|
"linear interpolation of alpha, nearest-pixel replication of color"
|
|
};
|
|
|
|
const char *brief_error_color = COLOR_RED_BOLD "ERROR" COLOR_NORMAL;
|
|
const char *brief_error_plain = "ERROR";
|
|
const char *brief_warn_color = COLOR_YELLOW_BOLD "WARN" COLOR_NORMAL;
|
|
const char *brief_warn_plain = "WARN";
|
|
const char *brief_OK_color = COLOR_GREEN_BOLD "OK" COLOR_NORMAL;
|
|
const char *brief_OK_plain = "OK";
|
|
|
|
const char *errors_color = COLOR_RED_BOLD "ERRORS DETECTED" COLOR_NORMAL;
|
|
const char *errors_plain = "ERRORS DETECTED";
|
|
const char *warnings_color = COLOR_YELLOW_BOLD "WARNINGS DETECTED" COLOR_NORMAL;
|
|
const char *warnings_plain = "WARNINGS DETECTED";
|
|
const char *no_err_color = COLOR_GREEN_BOLD "No errors detected" COLOR_NORMAL;
|
|
const char *no_err_plain = "No errors detected";
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
FILE *fp;
|
|
int i = 1;
|
|
int err = kOK;
|
|
int num_files = 0;
|
|
int num_errors = 0;
|
|
int num_warnings = 0;
|
|
const char *brief_error = color? brief_error_color : brief_error_plain;
|
|
const char *errors_detected = color? errors_color : errors_plain;
|
|
|
|
#ifdef __EMX__
|
|
_wildcard(&argc, &argv); /* Unix-like globbing for OS/2 and DOS */
|
|
#endif
|
|
|
|
while (argc > 1 && argv[1][0] == '-') {
|
|
switch (argv[1][i]) {
|
|
case '\0':
|
|
--argc;
|
|
++argv;
|
|
i = 1;
|
|
break;
|
|
case '7':
|
|
printtext = 1;
|
|
sevenbit = 1;
|
|
++i;
|
|
break;
|
|
case 'c':
|
|
color = 1;
|
|
++i;
|
|
break;
|
|
case 'f':
|
|
force = 1;
|
|
++i;
|
|
break;
|
|
case 'h':
|
|
usage(stdout);
|
|
return err;
|
|
case 'p':
|
|
printpal = 1;
|
|
++i;
|
|
break;
|
|
case 'S':
|
|
verbose = 0;
|
|
force = 1;
|
|
quiet = 2; /* summary */
|
|
++i;
|
|
break;
|
|
case 'q':
|
|
verbose = 0;
|
|
quiet = 1;
|
|
++i;
|
|
break;
|
|
case 's':
|
|
search = 1;
|
|
++i;
|
|
break;
|
|
case 't':
|
|
printtext = 1;
|
|
++i;
|
|
break;
|
|
case 'v':
|
|
++verbose; /* verbose == 2 means decode IDATs and print filter info */
|
|
quiet = 0; /* verbose == 4 means print pixel values, too */
|
|
++i;
|
|
break;
|
|
case 'w':
|
|
check_windowbits = 0;
|
|
++i;
|
|
break;
|
|
case 'x':
|
|
search = extract = 1;
|
|
++i;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "error: unknown option %c\n\n", argv[1][i]);
|
|
usage(stderr);
|
|
return kCommandLineError;
|
|
}
|
|
}
|
|
|
|
if (color) {
|
|
brief_error = brief_error_color;
|
|
errors_detected = errors_color;
|
|
#ifdef USE_ZLIB
|
|
pass_color = pass_color_enabled;
|
|
color_off = pass_color_enabled[0];
|
|
#endif
|
|
} else {
|
|
brief_error = brief_error_plain;
|
|
errors_detected = errors_plain;
|
|
#ifdef USE_ZLIB
|
|
pass_color = pass_color_disabled;
|
|
color_off = pass_color_disabled[0];
|
|
#endif
|
|
}
|
|
|
|
if (argc == 1) {
|
|
if (isatty(0)) { /* if stdin not redirected, give the user help */
|
|
usage(stdout);
|
|
} else {
|
|
char *fname = "stdin";
|
|
|
|
if (search)
|
|
pngsearch(stdin, fname, extract); /* currently returns void */
|
|
else
|
|
err = pngcheck(stdin, fname, 0, NULL);
|
|
++num_files;
|
|
if (err == kWarning)
|
|
++num_warnings;
|
|
else if (err > kWarning) {
|
|
++num_errors;
|
|
if (verbose)
|
|
printf("%s in %s\n", errors_detected, fname);
|
|
else if (quiet < 2)
|
|
printf("%s: %s%s%s\n", brief_error,
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"");
|
|
}
|
|
}
|
|
} else {
|
|
#ifdef USE_ZLIB
|
|
/* make sure we're using the zlib version we were compiled to use */
|
|
if (zlib_version[0] != ZLIB_VERSION[0]) {
|
|
printf("zlib error: incompatible version (expected %s,"
|
|
" using %s): skipping zlib check\n\n", ZLIB_VERSION, zlib_version);
|
|
check_zlib = 0;
|
|
if (verbose > 1)
|
|
verbose = 1;
|
|
} else if (strcmp(zlib_version, ZLIB_VERSION) != 0) {
|
|
printf("zlib warning: different version (expected %s,"
|
|
" using %s)\n\n", ZLIB_VERSION, zlib_version);
|
|
}
|
|
#endif /* USE_ZLIB */
|
|
|
|
/* main loop over files listed on command line */
|
|
for (i = 1; i < argc; ++i) {
|
|
char *fname = argv[i];
|
|
|
|
err = kOK;
|
|
if (strcmp(fname, "-") == 0) {
|
|
fname = "stdin";
|
|
fp = stdin;
|
|
} else if ((fp = fopen(fname, "rb")) == NULL) {
|
|
perror(fname);
|
|
err = kCriticalError;
|
|
}
|
|
|
|
if (err == kOK) {
|
|
if (search)
|
|
pngsearch(fp, fname, extract);
|
|
else
|
|
err = pngcheck(fp, fname, 0, NULL);
|
|
if (fp != stdin)
|
|
fclose(fp);
|
|
}
|
|
|
|
++num_files;
|
|
if (err == kWarning)
|
|
++num_warnings;
|
|
else if (err > kWarning) {
|
|
++num_errors;
|
|
if (verbose)
|
|
printf("%s in %s\n", errors_detected, fname);
|
|
else if (quiet < 2)
|
|
printf("%s: %s%s%s\n", brief_error,
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (num_errors > 0)
|
|
err = (num_errors > 127)? 127 : (num_errors < 2)? 2 : num_errors;
|
|
else if (num_warnings > 0)
|
|
err = 1;
|
|
if (!quiet && num_files > 1) {
|
|
printf("\n");
|
|
if (num_errors > 0)
|
|
printf("Errors were detected in %d of the %d files tested.\n",
|
|
num_errors, num_files);
|
|
if (num_warnings > 0)
|
|
printf("Warnings were detected in %d of the %d files tested.\n",
|
|
num_warnings, num_files);
|
|
if (num_errors + num_warnings < num_files)
|
|
printf("No errors were detected in %d of the %d files tested.\n",
|
|
num_files - (num_errors + num_warnings), num_files);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/* GRR 20061203 */
|
|
void usage(FILE *fpMsg)
|
|
{
|
|
fprintf(fpMsg, "PNGcheck, version %s,\n", VERSION);
|
|
fprintf(fpMsg, " by Alexander Lehmann, Andreas Dilger and Greg Roelofs.\n");
|
|
#ifdef USE_ZLIB
|
|
fprintf(fpMsg, " Compiled with zlib %s; using zlib %s.\n",
|
|
ZLIB_VERSION, zlib_version);
|
|
#endif
|
|
|
|
fprintf(fpMsg, "\n"
|
|
"Test PNG, JNG or MNG image files for corruption, and print size/type info."
|
|
"\n\n"
|
|
"Usage: pngcheck [-7cfpqtv] file.{png|jng|mng} [file2.{png|jng|mng} [...]]\n"
|
|
" or: ... | pngcheck [-7cfpqstvx]\n"
|
|
" or: pngcheck [-7cfpqstvx] file-containing-PNGs...\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -7 print contents of tEXt chunks, escape chars >=128 (for 7-bit terminals)\n"
|
|
" -c colorize output (for ANSI terminals)\n"
|
|
" -f force continuation even after major errors\n"
|
|
" -p print contents of PLTE, tRNS, hIST, sPLT and PPLT (can be used with -q)\n"
|
|
" -q test quietly (output only errors)\n"
|
|
" -S test quietly but output a summary\n"
|
|
" -s search for PNGs within another file\n"
|
|
" -t print contents of tEXt chunks (can be used with -q)\n"
|
|
" -v test verbosely (print most chunk data)\n"
|
|
#ifdef USE_ZLIB
|
|
" -vv test very verbosely (decode & print line filters)\n"
|
|
" -w suppress windowBits test (more-stringent compression check)\n"
|
|
#endif
|
|
" -x search for PNGs within another file and extract them when found\n"
|
|
"\n"
|
|
"Note: MNG support is more informational than conformance-oriented.\n"
|
|
);
|
|
|
|
fflush(fpMsg);
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_ZLIB
|
|
|
|
# define CRCCOMPL(c) c
|
|
# define CRCINIT (0)
|
|
# define update_crc crc32
|
|
|
|
#else /* !USE_ZLIB */
|
|
|
|
/* use these instead of ~crc and -1, since that doesn't work on machines
|
|
* that have 64-bit longs */
|
|
# define CRCCOMPL(c) ((c)^0xffffffff)
|
|
# define CRCINIT (CRCCOMPL(0))
|
|
|
|
/* make the table for a fast crc */
|
|
void make_crc_table(void)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < 256; ++n) {
|
|
ulg c;
|
|
int k;
|
|
|
|
c = (ulg)n;
|
|
for (k = 0; k < 8; ++k)
|
|
c = c & 1 ? 0xedb88320L ^ (c >> 1):c >> 1;
|
|
|
|
crc_table[n] = c;
|
|
}
|
|
crc_table_computed = 1;
|
|
}
|
|
|
|
|
|
|
|
/* update a running crc with the bytes buf[0..len-1]--the crc should be
|
|
initialized to all 1's, and the transmitted value is the 1's complement
|
|
of the final running crc. */
|
|
|
|
ulg update_crc(ulg crc, uch *buf, int len)
|
|
{
|
|
ulg c = crc;
|
|
uch *p = buf;
|
|
int n = len;
|
|
|
|
if (!crc_table_computed) {
|
|
make_crc_table();
|
|
}
|
|
|
|
if (n > 0) do {
|
|
c = crc_table[(c ^ (*p++)) & 0xff] ^ (c >> 8);
|
|
} while (--n);
|
|
return c;
|
|
}
|
|
|
|
#endif /* ?USE_ZLIB */
|
|
|
|
|
|
|
|
ulg getlong(FILE *fp, char *fname, char *where)
|
|
{
|
|
ulg res = 0;
|
|
int j;
|
|
|
|
for (j = 0; j < 4; ++j) {
|
|
int c;
|
|
|
|
if ((c = fgetc(fp)) == EOF) {
|
|
printf("%s EOF while reading %s\n", verbose? ":":fname, where);
|
|
set_err(kCriticalError);
|
|
return 0;
|
|
}
|
|
res <<= 8;
|
|
res |= c & 0xff;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
/* output a long when copying an embedded PNG out of a file. */
|
|
void putlong(FILE *fpOut, ulg ul)
|
|
{
|
|
putc(ul >> 24, fpOut);
|
|
putc(ul >> 16, fpOut);
|
|
putc(ul >> 8, fpOut);
|
|
putc(ul, fpOut);
|
|
}
|
|
|
|
|
|
|
|
/* print out "size" characters in buffer, taking care not to print control
|
|
chars other than whitespace, since this may open ways of attack by so-
|
|
called ANSI-bombs */
|
|
|
|
void init_printbuf_state(printbuf_state *prbuf)
|
|
{
|
|
prbuf->cr = 0;
|
|
prbuf->lf = 0;
|
|
prbuf->nul = 0;
|
|
prbuf->control = 0;
|
|
prbuf->esc = 0;
|
|
}
|
|
|
|
|
|
|
|
/* GRR EBCDIC WARNING */
|
|
void print_buffer(printbuf_state *prbuf, uch *buf, int size, int indent)
|
|
{
|
|
int linewidth = 79, ctg;
|
|
const char *term;
|
|
if (indent)
|
|
printf(" "), linewidth -= 4, term = "\\\n ";
|
|
else
|
|
term = "\\\n";
|
|
ctg = linewidth;
|
|
while (size--) {
|
|
uch c;
|
|
|
|
c = *buf++;
|
|
|
|
if ((c < ' ' && c != '\t' && c != '\n') ||
|
|
(sevenbit? c > 127 : (c >= 127 && c < 160))) {
|
|
if (ctg < 3) printf("%s", term), ctg = linewidth;
|
|
printf("\\%02X", c), ctg -= 3;
|
|
}
|
|
/*
|
|
else if (c == '\\')
|
|
printf("\\\\");
|
|
*/
|
|
else {
|
|
if (ctg < 1) printf("%s", term), ctg = linewidth;
|
|
putchar(c), ctg -= 1;
|
|
}
|
|
|
|
if (c < 32 || (c >= 127 && c < 160)) {
|
|
if (c == '\n') {
|
|
prbuf->lf = 1;
|
|
if (indent && size > 0)
|
|
printf(" ");
|
|
ctg = linewidth;
|
|
} else if (c == '\r')
|
|
prbuf->cr = 1;
|
|
else if (c == '\0')
|
|
prbuf->nul = 1;
|
|
else
|
|
prbuf->control = 1;
|
|
if (c == 27)
|
|
prbuf->esc = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void report_printbuf(printbuf_state *prbuf, char *fname, char *chunkid)
|
|
{
|
|
if (prbuf->cr) {
|
|
if (prbuf->lf) {
|
|
printf("%s %s chunk contains both CR and LF as line terminators\n",
|
|
verbose? "":fname, chunkid);
|
|
set_err(kMinorError);
|
|
} else {
|
|
printf("%s %s chunk contains only CR as line terminator\n",
|
|
verbose? "":fname, chunkid);
|
|
set_err(kMinorError);
|
|
}
|
|
}
|
|
if (prbuf->nul) {
|
|
printf("%s %s chunk contains null bytes\n", verbose? "":fname, chunkid);
|
|
set_err(kMinorError);
|
|
}
|
|
if (prbuf->control) {
|
|
printf("%s %s chunk contains one or more control characters%s\n",
|
|
verbose? "":fname, chunkid, prbuf->esc? " including Escape":"");
|
|
set_err(kMinorError);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int keywordlen(uch *buf, int maxsize)
|
|
{
|
|
int j = 0;
|
|
|
|
while (j < maxsize && buf[j])
|
|
++j;
|
|
|
|
return j;
|
|
}
|
|
|
|
|
|
|
|
const char *getmonth(int m)
|
|
{
|
|
static const char *month[] = {
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
};
|
|
|
|
return (m < 1 || m > 12)? inv : month[m-1];
|
|
}
|
|
|
|
|
|
|
|
int ratio(ulg uc, ulg c) /* GRR 19970621: swiped from UnZip 5.31 list.c */
|
|
{
|
|
ulg denom;
|
|
|
|
if (uc == 0)
|
|
return 0;
|
|
if (uc > 2000000L) { /* risk signed overflow if multiply numerator */
|
|
denom = uc / 1000L;
|
|
return ((uc >= c) ?
|
|
(int) ((uc-c + (denom>>1)) / denom) :
|
|
-((int) ((c-uc + (denom>>1)) / denom)));
|
|
} else { /* ^^^^^^^^ rounding */
|
|
denom = uc;
|
|
return ((uc >= c) ?
|
|
(int) ((1000L*(uc-c) + (denom>>1)) / denom) :
|
|
-((int) ((1000L*(c-uc) + (denom>>1)) / denom)));
|
|
} /* ^^^^^^^^ rounding */
|
|
}
|
|
|
|
|
|
|
|
/* GRR 20050724: Implementation of Euclidean Algorithm to find greatest
|
|
* common factor of two positive integers.
|
|
* (see http://mathworld.wolfram.com/EuclideanAlgorithm.html)
|
|
*/
|
|
ulg gcf(ulg a, ulg b)
|
|
{
|
|
ulg r;
|
|
|
|
if (b == 0)
|
|
return (a == 0)? 1 : a;
|
|
|
|
while ((r = a - (a/b)*b) != 0) {
|
|
a = b;
|
|
b = r;
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
#ifdef USE_ZLIB
|
|
/*
|
|
* inflate a deflated thing.
|
|
*/
|
|
typedef struct inflate_buffer {
|
|
struct inflate_buffer *next;
|
|
uInt size;
|
|
uch buffer[16];
|
|
} inflate_buffer;
|
|
|
|
void inflate_buffer_delete(inflate_buffer **ptr) {
|
|
if ((*ptr)->next)
|
|
inflate_buffer_delete(&(*ptr)->next);
|
|
free(*ptr);
|
|
*ptr = 0;
|
|
}
|
|
|
|
inflate_buffer **inflate_buffer_end(inflate_buffer **ptr) {
|
|
while (*ptr) ptr = &(*ptr)->next;
|
|
return ptr;
|
|
}
|
|
|
|
inflate_buffer *inflate_buffer_add(inflate_buffer **ptr,
|
|
const uch *bytes, uInt size) {
|
|
inflate_buffer *add = malloc((sizeof *add)+size-16);
|
|
if (add) {
|
|
add->next = 0;
|
|
add->size = size;
|
|
memcpy(add->buffer, bytes, size);
|
|
*inflate_buffer_end(ptr) = add;
|
|
}
|
|
return add;
|
|
}
|
|
|
|
size_t inflate_buffer_length(inflate_buffer *ptr) {
|
|
if (ptr)
|
|
return inflate_buffer_length(ptr->next) + ptr->size;
|
|
return 0;
|
|
}
|
|
|
|
typedef struct inflate_engine {
|
|
inflate_buffer *list;
|
|
z_stream zlib;
|
|
int oom;
|
|
uch temp[1024];
|
|
} inflate_engine;
|
|
|
|
int inflate_engine_init(inflate_engine *e) {
|
|
int rc;
|
|
memset(e, 0, sizeof *e);
|
|
rc = inflateInit(&e->zlib);
|
|
e->zlib.next_out = e->temp;
|
|
e->zlib.avail_out = sizeof e->temp;
|
|
return rc;
|
|
}
|
|
|
|
void inflate_engine_destroy(inflate_engine *e) {
|
|
if (e->list) inflate_buffer_delete(&e->list);
|
|
if (e->zlib.opaque) inflateEnd(&e->zlib);
|
|
}
|
|
|
|
void inflate_engine_stash(inflate_engine *e) {
|
|
if (e->zlib.next_out > e->temp && !e->oom) {
|
|
if (!inflate_buffer_add(&e->list, e->temp, e->zlib.next_out - e->temp)) {
|
|
inflate_buffer_delete(&e->list);
|
|
e->oom = 1;
|
|
}
|
|
}
|
|
e->zlib.next_out = e->temp;
|
|
e->zlib.avail_out = sizeof e->temp;
|
|
}
|
|
|
|
size_t inflate_engine_length(inflate_engine *e) {
|
|
return inflate_buffer_length(e->list);
|
|
}
|
|
|
|
int inflate_engine_process(inflate_engine *e, uch *bytes, size_t avail_in,
|
|
const char *chunkid, const char *fname, int end) {
|
|
int rc, stashed;
|
|
e->zlib.next_in = bytes;
|
|
|
|
for (;;) {
|
|
if (avail_in > (uInt)-1)
|
|
e->zlib.avail_in = (uInt)-1;
|
|
else
|
|
e->zlib.avail_in = (uInt)/*SAFE*/avail_in;
|
|
avail_in -= e->zlib.avail_in;
|
|
rc = inflate(&e->zlib, Z_NO_FLUSH);
|
|
avail_in += e->zlib.avail_in;
|
|
/* Stash the output first: */
|
|
stashed = (e->zlib.avail_out == 0);
|
|
if (stashed) inflate_engine_stash(e);
|
|
|
|
switch (rc) {
|
|
case Z_OK:
|
|
/* At the end may have to go round the loop again with no more input
|
|
* if we ran out of output.
|
|
*/
|
|
if (avail_in == 0 && (!end || !stashed)) return Z_OK;
|
|
continue;
|
|
|
|
case Z_STREAM_END:
|
|
inflate_engine_stash(e); /* Store any remaining bytes */
|
|
if (e->zlib.avail_in > 0) {
|
|
fprintf(stderr, "%s: %s: extra compressed data\n", fname, chunkid);
|
|
set_err(kWarning);
|
|
}
|
|
return Z_STREAM_END;
|
|
|
|
case Z_BUF_ERROR:
|
|
/* At the end we get this from the above attempt to finish if the
|
|
* stream really was truncated! Return Z_OK here because the code
|
|
* above provoked the buffer error.
|
|
*/
|
|
if (avail_in == 0 && !stashed && end) return Z_OK;
|
|
if (e->zlib.msg == NULL) e->zlib.msg = "jpegtopng bug"; /* BUG */
|
|
/* FALL THROUGH */
|
|
default: /* zlib error */
|
|
fprintf(stderr, "%s: %s: zlib code %d: '%s'\n", fname, chunkid, rc,
|
|
e->zlib.msg ? e->zlib.msg : "<nothing>");
|
|
set_err(kWarning);
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
void inflate_engine_print(printbuf_state *prbuf, inflate_engine *e, int indent)
|
|
{
|
|
inflate_buffer *buf = e->list;
|
|
|
|
while (buf) {
|
|
print_buffer(prbuf, buf->buffer, buf->size, indent);
|
|
buf = buf->next;
|
|
}
|
|
}
|
|
|
|
int inflate_engine_check_text(inflate_engine *e, char *chunkid, char *fname) {
|
|
inflate_buffer *buf = e->list;
|
|
|
|
while (buf) {
|
|
if (check_text(buf->buffer, buf->size, chunkid, fname))
|
|
return 1; /* text includes a '\0' */
|
|
buf = buf->next;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* USE_ZLIB */
|
|
|
|
|
|
|
|
int pngcheck(FILE *fp, char *fname, int searching, FILE *fpOut)
|
|
{
|
|
int i, j;
|
|
long sz; /* FIXME: should be ulg (not using negative values as flags...) */
|
|
uch magic[8];
|
|
char chunkid[5] = {'\0', '\0', '\0', '\0', '\0'};
|
|
char *and = "";
|
|
int toread;
|
|
int c;
|
|
int have_IHDR = 0, have_IEND = 0;
|
|
int have_MHDR = 0, have_MEND = 0;
|
|
int have_PLTE = 0;
|
|
int have_JHDR = 0, have_JSEP = 0, need_JSEP = 0;
|
|
int have_IDAT = 0, have_JDAT = 0, last_is_IDAT = 0, last_is_JDAT = 0;
|
|
int have_bKGD = 0, have_cHRM = 0, have_gAMA = 0, have_hIST = 0, have_iCCP = 0;
|
|
int have_oFFs = 0, have_pCAL = 0, have_pHYs = 0, have_sBIT = 0, have_sCAL = 0;
|
|
int have_sRGB = 0, have_sTER = 0, have_tIME = 0, have_tRNS = 0;
|
|
int have_SAVE = 0, have_TERM = 0, have_MAGN = 0, have_pHYg = 0;
|
|
int top_level = 1;
|
|
ulg zhead = 1; /* 0x10000 indicates both zlib header bytes read */
|
|
ulg crc, filecrc;
|
|
ulg layers = 0L, frames = 0L;
|
|
long num_chunks = 0L;
|
|
long w = 0L, h = 0L;
|
|
long mng_width = 0L, mng_height = 0L;
|
|
int vlc = -1, lc = -1;
|
|
uch ityp = 1;
|
|
int bitdepth = 0, sampledepth = 0, jtyp = 0, lace = 0, nplte = 0;
|
|
int jbitd = 0, alphadepth = 0;
|
|
int did_stat = 0;
|
|
#ifdef USE_ZLIB
|
|
unsigned long filter_counts[7][7];
|
|
int last_filter = 5;
|
|
#endif
|
|
printbuf_state prbuf_state;
|
|
struct stat statbuf;
|
|
static int first_file = 1;
|
|
const char *brief_warn = color? brief_warn_color : brief_warn_plain;
|
|
const char *brief_OK = color? brief_OK_color : brief_OK_plain;
|
|
const char *warnings_detected = color? warnings_color : warnings_plain;
|
|
const char *no_errors_detected = color? no_err_color : no_err_plain;
|
|
|
|
global_error = kOK;
|
|
|
|
#ifdef USE_ZLIB
|
|
{
|
|
int i;
|
|
for (i=0; i<7; ++i) {
|
|
int j;
|
|
for (j=0; j<7; ++j)
|
|
filter_counts[i][j] = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (verbose || printtext || printpal) {
|
|
printf("%sFile: %s%s%s", first_file? "":"\n", color? COLOR_WHITE_BOLD:"",
|
|
fname, color? COLOR_NORMAL:"");
|
|
|
|
if (searching) {
|
|
printf("\n");
|
|
} else {
|
|
stat(fname, &statbuf); /* know file exists => know stat() successful */
|
|
did_stat = 1;
|
|
/* typecast long since off_t may be 64-bit (e.g., IRIX): */
|
|
printf(" (%ld bytes)\n", (long)statbuf.st_size);
|
|
}
|
|
}
|
|
|
|
first_file = 0;
|
|
png = mng = jng = 0;
|
|
|
|
if (!searching) {
|
|
int check = 0;
|
|
|
|
if (fread(magic, 1, 8, fp)!=8) {
|
|
printf("%s cannot read PNG or MNG signature\n", verbose? "":fname);
|
|
set_err(kCriticalError);
|
|
return global_error;
|
|
}
|
|
|
|
if (magic[0]==0 && magic[1]>0 && magic[1]<=64 && magic[2]!=0) {
|
|
if (!quiet)
|
|
printf("%s (trying to skip MacBinary header)\n", verbose? "":fname);
|
|
|
|
if (fread(buffer, 1, 120, fp) != 120 || fread(magic, 1, 8, fp) != 8) {
|
|
printf(" cannot read past MacBinary header\n");
|
|
set_err(kCriticalError);
|
|
} else if ((check = check_magic(magic, fname, DO_PNG)) == 0) {
|
|
png = 1;
|
|
if (!quiet)
|
|
printf(" this PNG seems to be contained in a MacBinary file\n");
|
|
} else if ((check = check_magic(magic, fname, DO_MNG)) == 0) {
|
|
mng = 1;
|
|
if (!quiet)
|
|
printf(" this MNG seems to be contained in a MacBinary file\n");
|
|
} else if ((check = check_magic(magic, fname, DO_JNG)) == 0) {
|
|
jng = 1;
|
|
if (!quiet)
|
|
printf(" this JNG seems to be contained in a MacBinary file\n");
|
|
} else {
|
|
if (check == 2)
|
|
printf(" this is neither a PNG nor JNG image nor a MNG stream\n");
|
|
set_err(kCriticalError);
|
|
}
|
|
} else if ((check = check_magic(magic, fname, DO_PNG)) == 0) {
|
|
png = 1;
|
|
} else if (check == 1) { /* bytes 2-4 == "PNG" but others are bad */
|
|
set_err(kCriticalError);
|
|
} else if (check == 2) { /* not "PNG"; see if it's MNG or JNG instead */
|
|
if ((check = check_magic(magic, fname, DO_MNG)) == 0)
|
|
mng = 1; /* yup */
|
|
else if (check == 2 && (check = check_magic(magic, fname, DO_JNG)) == 0)
|
|
jng = 1; /* yup */
|
|
else {
|
|
set_err(kCriticalError);
|
|
if (check == 2)
|
|
printf("%s this is neither a PNG or JNG image nor a MNG stream\n",
|
|
verbose? "":fname);
|
|
}
|
|
}
|
|
|
|
if (is_err(kMinorError))
|
|
return global_error;
|
|
}
|
|
|
|
/*-------------------- BEGINNING OF IMMENSE WHILE-LOOP --------------------*/
|
|
|
|
while ((c = fgetc(fp)) != EOF) {
|
|
ungetc(c, fp);
|
|
|
|
if (((png || jng) && have_IEND) || (mng && have_MEND)) {
|
|
if (searching) /* start looking again in the next file */
|
|
return global_error;
|
|
|
|
if (!quiet)
|
|
printf("%s additional data after %cEND chunk\n", verbose? "":fname,
|
|
mng? 'M':'I');
|
|
|
|
set_err(kMinorError);
|
|
if (!force)
|
|
return global_error;
|
|
}
|
|
|
|
sz = getlong(fp, fname, "chunk length");
|
|
|
|
if (is_err(kMajorError)) /* FIXME: return only if !force? */
|
|
return global_error;
|
|
|
|
if (sz < 0 || sz > 0x7fffffff) { /* FIXME: convert to ulg, lose "< 0" */
|
|
printf("%s invalid chunk length (too large)\n", verbose? ":":fname);
|
|
set_err(kMajorError);
|
|
/* if (!force) */ /* code not yet vetted for negative sz */
|
|
return global_error;
|
|
}
|
|
|
|
if (fread(chunkid, 1, 4, fp) != 4) {
|
|
printf("%s EOF while reading chunk type\n", verbose? ":":fname);
|
|
set_err(kCriticalError);
|
|
return global_error;
|
|
}
|
|
|
|
/* GRR: add 4-character EBCDIC conversion here (chunkid) */
|
|
|
|
chunkid[4] = '\0';
|
|
++num_chunks;
|
|
|
|
if (check_chunk_name(chunkid, fname) != 0) {
|
|
set_err(kMajorError);
|
|
if (!force)
|
|
return global_error;
|
|
}
|
|
|
|
if (verbose)
|
|
printf(" chunk %s%s%s at offset 0x%05lx, length %ld",
|
|
color? COLOR_YELLOW:"", chunkid, color? COLOR_NORMAL:"",
|
|
ftell(fp)-4, sz);
|
|
|
|
if (is_err(kMajorError))
|
|
return global_error;
|
|
|
|
crc = update_crc(CRCINIT, (uch *)chunkid, 4);
|
|
|
|
if ((png && !have_IHDR && strcmp(chunkid,"IHDR")!=0) ||
|
|
(mng && !have_MHDR && strcmp(chunkid,"MHDR")!=0) ||
|
|
(jng && !have_JHDR && strcmp(chunkid,"JHDR")!=0))
|
|
{
|
|
printf("%s first chunk must be %cHDR\n",
|
|
verbose? ":":fname, png? 'I' : (mng? 'M':'J'));
|
|
set_err(kMinorError);
|
|
if (!force)
|
|
return global_error;
|
|
}
|
|
|
|
toread = (sz > BS)? BS:sz;
|
|
|
|
if (fread(buffer, 1, (size_t)toread, fp) != (size_t)toread) {
|
|
printf("%s EOF while reading %s%sdata\n",
|
|
verbose? ":":fname, verbose? "":chunkid, verbose? "":" ");
|
|
set_err(kCriticalError);
|
|
return global_error;
|
|
}
|
|
|
|
crc = update_crc(crc, (uch *)buffer, toread);
|
|
|
|
/*================================*
|
|
* PNG, JNG and MNG header chunks *
|
|
*================================*/
|
|
|
|
/*------*
|
|
| IHDR |
|
|
*------*/
|
|
if (strcmp(chunkid, "IHDR") == 0) {
|
|
if (png && have_IHDR) {
|
|
printf("%s multiple IHDR not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 13) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
int compr, filt;
|
|
|
|
w = LG(buffer);
|
|
h = LG(buffer+4);
|
|
if (w <= 0 || h <= 0 || w > 2147483647 || h > 2147483647) {
|
|
printf("%s invalid %simage dimensions (%ldx%ld)\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", w, h);
|
|
set_err(kMinorError);
|
|
}
|
|
bitdepth = sampledepth = (uch)buffer[8];
|
|
ityp = (uch)buffer[9];
|
|
if (ityp == 1 || ityp == 5 || ityp > sizeof(png_type)/sizeof(char*)) {
|
|
printf("%s invalid %simage type (%d)\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", ityp);
|
|
ityp = 1; /* avoid out of range array index */
|
|
set_err(kMinorError);
|
|
}
|
|
switch (sampledepth) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
if (ityp == 2 || ityp == 4 || ityp == 6) { /* RGB or GA or RGBA */
|
|
printf("%s invalid %ssample depth (%d) for %s image\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", sampledepth,
|
|
png_type[ityp]);
|
|
set_err(kMinorError);
|
|
}
|
|
break;
|
|
case 8:
|
|
break;
|
|
case 16:
|
|
if (ityp == 3) { /* palette */
|
|
printf("%s invalid %ssample depth (%d) for %s image\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", sampledepth,
|
|
png_type[ityp]);
|
|
set_err(kMinorError);
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s invalid %ssample depth (%d)\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", sampledepth);
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
compr = (uch)buffer[10];
|
|
if (compr > 127) {
|
|
printf("%s private (invalid?) %scompression method (%d) "
|
|
"(warning)\n", verbose? ":":fname, verbose? "":"IHDR ", compr);
|
|
set_err(kWarning);
|
|
} else if (compr > 0) {
|
|
printf("%s invalid %scompression method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", compr);
|
|
set_err(kMinorError);
|
|
}
|
|
filt = (uch)buffer[11];
|
|
if (filt > 127) {
|
|
printf("%s private (invalid?) %sfilter method (%d) "
|
|
"(warning)\n", verbose? ":":fname, verbose? "":"IHDR ", filt);
|
|
set_err(kWarning);
|
|
} else if (filt > 0 && !(mng && (ityp == 2 || ityp == 6) && filt == 64))
|
|
{
|
|
printf("%s invalid %sfilter method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", filt);
|
|
set_err(kMinorError);
|
|
}
|
|
lace = (uch)buffer[12];
|
|
if (lace > 127) {
|
|
printf("%s private (invalid?) %sinterlace method (%d) "
|
|
"(warning)\n", verbose? ":":fname, verbose? "":"IHDR ", lace);
|
|
set_err(kWarning);
|
|
} else if (lace > 1) {
|
|
printf("%s invalid %sinterlace method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"IHDR ", lace);
|
|
set_err(kMinorError);
|
|
}
|
|
switch (ityp) {
|
|
case 2:
|
|
bitdepth = sampledepth * 3; /* RGB */
|
|
break;
|
|
case 4:
|
|
bitdepth = sampledepth * 2; /* gray+alpha */
|
|
break;
|
|
case 6:
|
|
bitdepth = sampledepth * 4; /* RGBA */
|
|
break;
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n %ld x %ld image, %d-bit %s, %sinterlaced\n", w, h,
|
|
bitdepth, png_type[ityp], lace? "":"non-");
|
|
}
|
|
}
|
|
have_IHDR = 1;
|
|
if (mng)
|
|
top_level = 0;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
#ifdef USE_ZLIB
|
|
first_idat = 1; /* flag: next IDAT will be the first in this subimage */
|
|
zlib_error = 0; /* flag: no zlib errors yet in this file */
|
|
/* GRR 20000304: data dump not yet compatible with interlaced images: */
|
|
if (lace && verbose > 3) /* (FIXME eventually...or move to pngcrunch) */
|
|
verbose = 2;
|
|
#endif
|
|
|
|
/*------*
|
|
| JHDR |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "JHDR") == 0) {
|
|
if (png) {
|
|
printf("%s JHDR not defined in PNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (jng && have_JHDR) {
|
|
printf("%s multiple JHDR not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 16) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
w = LG(buffer);
|
|
h = LG(buffer+4);
|
|
if (w == 0 || h == 0) {
|
|
printf("%s invalid %simage dimensions (%ldx%ld)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", w, h);
|
|
set_err(kMinorError);
|
|
}
|
|
jtyp = (uch)buffer[8];
|
|
if (jtyp != 8 && jtyp != 10 && jtyp != 12 && jtyp != 14) {
|
|
printf("%s invalid %scolor type\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ");
|
|
set_err(kMinorError);
|
|
} else {
|
|
jtyp = (jtyp >> 1) - 4; /* now 0,1,2,3: index into jng_type[] */
|
|
bitdepth = (uch)buffer[9];
|
|
if (bitdepth != 8 && bitdepth != 12 && bitdepth != 20) {
|
|
printf("%s invalid %sbit depth (%d)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", bitdepth);
|
|
set_err(kMinorError);
|
|
} else if (buffer[10] != 8) {
|
|
printf("%s invalid %sJPEG compression method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", buffer[10]);
|
|
set_err(kMinorError);
|
|
} else if (buffer[13] != 0) {
|
|
printf("%s invalid %salpha-channel compression method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", buffer[13]);
|
|
set_err(kMinorError);
|
|
} else if (buffer[14] != 0) {
|
|
printf("%s invalid %salpha-channel filter method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", buffer[14]);
|
|
set_err(kMinorError);
|
|
} else if (buffer[15] != 0) {
|
|
printf("%s invalid %salpha-channel interlace method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", buffer[15]);
|
|
set_err(kMinorError);
|
|
} else if ((lace = (uch)buffer[11]) != 0 && lace != 8) {
|
|
printf("%s invalid %sJPEG interlace method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"JHDR ", lace);
|
|
set_err(kMinorError);
|
|
} else {
|
|
int a;
|
|
|
|
if (bitdepth == 20) {
|
|
need_JSEP = 1;
|
|
jbitd = 8;
|
|
and = "and 12-bit ";
|
|
} else
|
|
jbitd = bitdepth;
|
|
a = alphadepth = buffer[12];
|
|
if ((a != 1 && a != 2 && a != 4 && a != 8 && a != 16 &&
|
|
jtyp > 1) || (a != 0 && jtyp < 2))
|
|
{
|
|
printf("%s invalid %salpha-channel bit depth (%d) for %s image\n"
|
|
, verbose? ":":fname, verbose? "":"JHDR ", alphadepth,
|
|
jng_type[jtyp]);
|
|
set_err(kMinorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
if (jtyp < 2)
|
|
printf("\n %ld x %ld image, %d-bit %s%s%s\n",
|
|
w, h, jbitd, and, jng_type[jtyp], lace? ", progressive":"");
|
|
else
|
|
printf("\n %ld x %ld image, %d-bit %s%s + %d-bit alpha%s\n",
|
|
w, h, jbitd, and, jng_type[jtyp-2], alphadepth,
|
|
lace? ", progressive":"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
have_JHDR = 1;
|
|
if (mng)
|
|
top_level = 0;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| MHDR |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "MHDR") == 0) {
|
|
if (png || jng) {
|
|
printf("%s MHDR not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else
|
|
if (have_MHDR) {
|
|
printf("%s multiple MHDR not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 28) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"MHDR ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
ulg tps, playtime, profile;
|
|
/* int validtrans = 0; */
|
|
|
|
mng_width = w = LG(buffer);
|
|
mng_height = h = LG(buffer+4);
|
|
tps = LG(buffer+8);
|
|
layers = LG(buffer+12);
|
|
frames = LG(buffer+16);
|
|
playtime = LG(buffer+20);
|
|
profile = LG(buffer+24);
|
|
if (verbose) {
|
|
printf("\n %lu x %lu frame size, ", w, h);
|
|
if (tps)
|
|
printf("%lu tick%s per second, ", tps, (tps == 1L)? "" : "s");
|
|
else
|
|
printf("infinite tick length, ");
|
|
if (layers && layers < 0x7ffffffL)
|
|
printf("%lu layer%s,\n", layers, (layers == 1L)? "" : "s");
|
|
else
|
|
printf("%s layer count,\n", layers? "infinite" : "unspecified");
|
|
if (frames && frames < 0x7ffffffL)
|
|
printf(" %lu frame%s, ", frames, (frames == 1L)? "" : "s");
|
|
else
|
|
printf(" %s frame count, ", frames? "infinite" : "unspecified");
|
|
if (playtime && playtime < 0x7ffffffL) {
|
|
printf("%lu-tick play time ", playtime);
|
|
if (tps)
|
|
printf("(%lu seconds), ", (playtime + (tps >> 1)) / tps);
|
|
else
|
|
printf(", ");
|
|
} else
|
|
printf("%s play time, ", playtime? "infinite" : "unspecified");
|
|
}
|
|
if (profile & 0x0001) {
|
|
int bits = 0;
|
|
|
|
vlc = lc = 1;
|
|
if (verbose)
|
|
printf("valid profile:\n ");
|
|
if (profile & 0x0002) {
|
|
if (verbose)
|
|
printf("simple MNG features");
|
|
++bits;
|
|
vlc = 0;
|
|
}
|
|
if (profile & 0x0004) {
|
|
if (verbose)
|
|
printf("%scomplex MNG features", bits? ", " : "");
|
|
++bits;
|
|
vlc = 0;
|
|
lc = 0;
|
|
}
|
|
if (profile & 0x0008) {
|
|
if (verbose)
|
|
printf("%scritical transparency", bits? ", " : "");
|
|
++bits;
|
|
}
|
|
if (profile & 0x0010) {
|
|
if (verbose)
|
|
printf("%s%sJNG", bits? ", " : "", (bits == 3)? "\n " : "");
|
|
++bits;
|
|
vlc = 0;
|
|
lc = 0;
|
|
}
|
|
if (profile & 0x0020) {
|
|
if (verbose)
|
|
printf("%s%sdelta-PNG", bits? ", " : "",
|
|
(bits == 3)? "\n " : "");
|
|
++bits;
|
|
vlc = 0;
|
|
lc = 0;
|
|
}
|
|
if (profile & 0x0040) {
|
|
if (verbose)
|
|
printf("%s%svalid transparency info", bits? ", " : "",
|
|
(bits > 0 && (bits % 3) == 0)? "\n " : "");
|
|
++bits;
|
|
/* validtrans = 1; */
|
|
}
|
|
if (/* validtrans && */ profile & 0x0080) {
|
|
if (verbose)
|
|
printf("%s%smay have bkgd transparency", bits? ", " : "",
|
|
(bits > 0 && (bits % 3) == 0)? "\n " : "");
|
|
++bits;
|
|
}
|
|
if (/* validtrans && */ profile & 0x0100) {
|
|
/*
|
|
FIXME: also check bit 3 (0x0008); if not set, this one is meaningless
|
|
*/
|
|
if (verbose)
|
|
printf("%s%smay have semi-transparency", bits? ", " : "",
|
|
(bits > 0 && (bits % 3) == 0)? "\n " : "");
|
|
++bits;
|
|
}
|
|
if (/* validtrans && */ profile & 0x0200) {
|
|
if (verbose)
|
|
printf("%s%sobject buffers must be stored", bits? ", " : "",
|
|
(bits > 0 && (bits % 3) == 0)? "\n " : "");
|
|
++bits;
|
|
vlc = 0;
|
|
lc = 0;
|
|
}
|
|
if (profile & 0xfc00) {
|
|
/*
|
|
FIXME: error/strong warning
|
|
*/
|
|
if (verbose)
|
|
printf("%s%sreserved bits are set", bits? ", " : "",
|
|
(bits > 0 && (bits % 3) == 0)? "\n " : "");
|
|
++bits;
|
|
vlc = 0;
|
|
lc = 0;
|
|
}
|
|
if (profile & 0x7fff0000) {
|
|
if (verbose)
|
|
printf("%s%sprivate/experimental bits are set", bits? ", " : "",
|
|
(bits > 0 && (bits % 3) == 0)? "\n " : "");
|
|
++bits;
|
|
vlc = 0;
|
|
lc = 0;
|
|
}
|
|
/*
|
|
FIXME: make sure bit 31 (0x80000000) is 0
|
|
*/
|
|
if (verbose) {
|
|
if (vlc)
|
|
printf("%s (MNG-VLC)", bits? "":"no feature bits specified");
|
|
else if (lc)
|
|
printf(" (MNG-LC)");
|
|
printf("\n");
|
|
}
|
|
} else {
|
|
vlc = lc = -1;
|
|
if (verbose)
|
|
printf("%s\n simplicity profile\n",
|
|
profile? "invalid" : "unspecified");
|
|
}
|
|
}
|
|
have_MHDR = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*================================================*
|
|
* PNG chunks (with the exception of IHDR, above) *
|
|
*================================================*/
|
|
|
|
/*------*
|
|
| PLTE |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "PLTE") == 0) {
|
|
if (jng) {
|
|
printf("%s PLTE not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_PLTE) {
|
|
printf("%s multiple PLTE not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && ityp != 3 && ityp != 2 && ityp != 6) {
|
|
printf("%s PLTE not allowed in %s image\n", verbose? ":":fname,
|
|
png_type[ityp]);
|
|
set_err(kMinorError);
|
|
} else if (png && have_IDAT) {
|
|
printf("%s %smust precede IDAT\n",
|
|
verbose? ":":fname, verbose? "":"PLTE ");
|
|
set_err(kMinorError);
|
|
} else if (png && have_bKGD) {
|
|
printf("%s %smust precede bKGD\n",
|
|
verbose? ":":fname, verbose? "":"PLTE ");
|
|
set_err(kMinorError);
|
|
} else if ((!(mng && have_PLTE) && sz < 3) || sz > 768 || sz % 3 != 0) {
|
|
printf("%s invalid number of %sentries (%g)\n",
|
|
verbose? ":":fname, verbose? "":"PLTE ", (double)sz / 3);
|
|
set_err(kMinorError); /* was kMajorError, but should be able to cont. */
|
|
} else {
|
|
nplte = sz / 3;
|
|
if (!(mng && have_PLTE) && ((bitdepth == 1 && nplte > 2) ||
|
|
(bitdepth == 2 && nplte > 4) || (bitdepth == 4 && nplte > 16)))
|
|
{
|
|
printf("%s invalid number of %sentries (%d) for %d-bit image\n",
|
|
verbose? ":":fname, verbose? "":"PLTE ", nplte, bitdepth);
|
|
set_err(kMinorError);
|
|
}
|
|
}
|
|
/*
|
|
else if (printpal && !verbose)
|
|
printf("\n");
|
|
*/
|
|
if (no_err(kMinorError)) {
|
|
if (ityp == 1) /* for MNG and tRNS */
|
|
ityp = 3;
|
|
if (verbose || (printpal && !quiet)) {
|
|
if (!verbose && printpal && !quiet)
|
|
printf(" PLTE chunk");
|
|
printf(": %d palette entr%s\n", nplte, nplte == 1? "y":"ies");
|
|
}
|
|
if (printpal) {
|
|
char *spc;
|
|
|
|
if (nplte < 10)
|
|
spc = " ";
|
|
else if (nplte < 100)
|
|
spc = " ";
|
|
else
|
|
spc = " ";
|
|
for (i = j = 0; i < nplte; ++i, j += 3)
|
|
printf("%s%3d: (%3d,%3d,%3d) = (0x%02x,0x%02x,0x%02x)\n", spc, i,
|
|
buffer[j], buffer[j + 1], buffer[j + 2],
|
|
buffer[j], buffer[j + 1], buffer[j + 2]);
|
|
}
|
|
}
|
|
have_PLTE = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| IDAT |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "IDAT") == 0) {
|
|
/* GRR FIXME: need to check for consecutive IDATs within MNG segments */
|
|
if (have_IDAT && !last_is_IDAT) {
|
|
if (mng) { /* reset things (SEMI-HACK; check for segments instead!) */
|
|
have_IDAT = 0;
|
|
#ifdef USE_ZLIB
|
|
zlib_error = 0;
|
|
zlib_windowbits = 15;
|
|
#endif
|
|
zhead = 1;
|
|
if (verbose)
|
|
printf("\n");
|
|
} else {
|
|
printf("%s IDAT chunks must be consecutive\n", verbose? ":":fname);
|
|
set_err(kMajorError);
|
|
}
|
|
} else if (png && ityp == 3 && !have_PLTE) {
|
|
printf("%s %smust follow PLTE in %s image\n",
|
|
verbose? ":":fname, verbose? "":"IDAT ", png_type[ityp]);
|
|
set_err(kMajorError);
|
|
} else if (verbose)
|
|
printf("\n");
|
|
|
|
if (!no_err(kMinorError) && !force)
|
|
return global_error;
|
|
|
|
/* We just want to check that we have read at least the minimum (10)
|
|
* IDAT bytes possible, but avoid any overflow for short ints. We
|
|
* must also take into account that 0-length IDAT chunks are legal.
|
|
*/
|
|
if (have_IDAT <= 0)
|
|
have_IDAT = (sz > 0)? sz : -1; /* -1 as marker for IDAT(s), no data */
|
|
else if (have_IDAT < 10)
|
|
have_IDAT += (sz > 10)? 10 : sz; /* FIXME? could cap at 10 always */
|
|
|
|
/* Dump the zlib header from the first two bytes. */
|
|
if (zhead < 0x10000 && sz > 0) {
|
|
zhead = (zhead << 8) + buffer[0];
|
|
if (sz > 1 && zhead < 0x10000)
|
|
zhead = (zhead << 8) + buffer[1];
|
|
if (zhead >= 0x10000) {
|
|
/* formerly print_zlibheader(zhead & 0xffff); */
|
|
/* See the code in zlib deflate.c that writes out the header when
|
|
s->status is INIT_STATE. In fact this code is based on the zlib
|
|
specification in RFC 1950 (ftp://ds.internic.net/rfc/rfc1950.txt),
|
|
with the implicit assumption that the zlib header *is* written (it
|
|
always should be inside a valid PNG file). The variable names are
|
|
taken, verbatim, from the RFC. */
|
|
unsigned int CINFO = (zhead & 0xf000) >> 12;
|
|
|
|
#ifdef USE_ZLIB
|
|
if (check_windowbits) /* check for libpng 1.2.6 windowBits bug */
|
|
zlib_windowbits = CINFO + 8;
|
|
#endif
|
|
if (verbose) {
|
|
unsigned int CM = (zhead & 0xf00) >> 8;
|
|
unsigned int FDICT = (zhead & 0x20) >> 5;
|
|
unsigned int FLEVEL = (zhead & 0xc0) >> 6;
|
|
|
|
printf(" zlib: ");
|
|
if ((zhead & 0xffff) % 31) {
|
|
printf("compression header fails checksum\n");
|
|
set_err(kMajorError);
|
|
} else if (CM == 8) {
|
|
if (CINFO > 1) {
|
|
printf("deflated, %dK window, %s compression%s\n",
|
|
(1 << (CINFO-2)), deflate_type[FLEVEL],
|
|
FDICT? ", preset dictionary":"");
|
|
} else {
|
|
printf("deflated, %d-byte window, %s compression%s\n",
|
|
(1 << (CINFO+8)), deflate_type[FLEVEL],
|
|
FDICT? ", preset dictionary":"");
|
|
}
|
|
} else {
|
|
printf("non-deflate compression method (%d)\n", CM);
|
|
set_err(kMajorError);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#ifdef USE_ZLIB
|
|
if (check_zlib && !zlib_error) {
|
|
static uch *p; /* always points to next filter byte */
|
|
static int cur_y, cur_pass, cur_xoff, cur_yoff, cur_xskip, cur_yskip;
|
|
static long cur_width, cur_linebytes;
|
|
static long numfilt, numfilt_this_block, numfilt_total, numfilt_pass[7];
|
|
uch *eod;
|
|
int err=Z_OK;
|
|
|
|
zstrm.next_in = buffer;
|
|
zstrm.avail_in = toread;
|
|
|
|
// FIXME! when inflate error and force, need to skip over rest of IDAT
|
|
/* initialize zlib and bit/byte/line variables if not already done */
|
|
if (first_idat) {
|
|
zstrm.next_out = p = outbuf;
|
|
zstrm.avail_out = BS;
|
|
zstrm.zalloc = (alloc_func)Z_NULL;
|
|
zstrm.zfree = (free_func)Z_NULL;
|
|
zstrm.opaque = (voidpf)Z_NULL;
|
|
if ((err = inflateInit2(&zstrm, zlib_windowbits)) != Z_OK) {
|
|
printf("\n zlib: oops! can't initialize (error = %d)\n", err);
|
|
zlib_error = 1;
|
|
/* actually, fatal error for subsequent PNGs, too; could (should?)
|
|
return here... */
|
|
}
|
|
cur_y = 0;
|
|
cur_pass = 1; /* interlace pass: 1 through 7 */
|
|
cur_xoff = cur_yoff = 0;
|
|
cur_xskip = cur_yskip = lace? 8 : 1;
|
|
cur_width = (w - cur_xoff + cur_xskip - 1) / cur_xskip; /* round up */
|
|
cur_linebytes = ((cur_width*bitdepth + 7) >> 3) + 1; /* round, fltr */
|
|
numfilt = 0L;
|
|
first_idat = 0;
|
|
if (lace) { /* loop through passes to calculate total filters */
|
|
int passm1, yskip=0, yoff=0, xoff=0;
|
|
|
|
if (verbose) /* GRR FIXME? could move this calc outside USE_ZLIB */
|
|
printf(" rows per pass%s: ",
|
|
(lace > 1)? " (assuming Adam7-like interlacing)":"");
|
|
for (passm1 = 0; passm1 < 7; ++passm1) {
|
|
switch (passm1) { /* (see table below for full summary) */
|
|
case 0: yskip = 8; yoff = 0; xoff = 0; break;
|
|
case 1: yskip = 8; yoff = 0; xoff = 4; break;
|
|
case 2: yskip = 8; yoff = 4; xoff = 0; break;
|
|
case 3: yskip = 4; yoff = 0; xoff = 2; break;
|
|
case 4: yskip = 4; yoff = 2; xoff = 0; break;
|
|
case 5: yskip = 2; yoff = 0; xoff = 1; break;
|
|
case 6: yskip = 2; yoff = 1; xoff = 0; break;
|
|
}
|
|
/* effective height is reduced if odd pass: subtract yoff (but
|
|
* if effective width of pass is 0 => no rows and no filters) */
|
|
numfilt_pass[passm1] =
|
|
(w <= xoff)? 0 : (h - yoff + yskip - 1) / yskip;
|
|
if (verbose) {
|
|
/* colors here are handy as a key if row-filters are being
|
|
* printed, but otherwise they're a bit too busy */
|
|
printf("%s%s%ld%s", passm1? ", ":"",
|
|
(verbose > 1)? pass_color[passm1+1]:"",
|
|
numfilt_pass[passm1], (verbose > 1)? color_off:"");
|
|
}
|
|
if (passm1 > 0) /* now make it cumulative */
|
|
numfilt_pass[passm1] += numfilt_pass[passm1 - 1];
|
|
}
|
|
if (verbose)
|
|
printf("\n");
|
|
} else {
|
|
numfilt_pass[0] = h; /* if non-interlaced */
|
|
numfilt_pass[1] = numfilt_pass[2] = numfilt_pass[3] = h;
|
|
numfilt_pass[4] = numfilt_pass[5] = numfilt_pass[6] = h;
|
|
}
|
|
numfilt_total = numfilt_pass[6];
|
|
}
|
|
|
|
if (verbose > 1) {
|
|
printf(" row filters (0 none, 1 sub, 2 up, 3 avg, "
|
|
"4 paeth)%s:\n %s", verbose > 3? " and data" : "",
|
|
pass_color[cur_pass]);
|
|
}
|
|
numfilt_this_block = 0L;
|
|
|
|
while (err != Z_STREAM_END && zstrm.avail_in > 0) {
|
|
/* know zstrm.avail_out > 0: get some image/filter data */
|
|
err = inflate(&zstrm, Z_SYNC_FLUSH);
|
|
if (err != Z_OK && err != Z_STREAM_END) {
|
|
printf("%s zlib: inflate error = %d (%s)\n",
|
|
verbose > 1? "\n " : (verbose == 1? " ":fname), err,
|
|
(-err < 1 || -err > 6)? "unknown":zlib_error_type[-err-1]);
|
|
zlib_error = 1; /* fatal error only for this PNG */
|
|
break; /* kill inner loop */
|
|
}
|
|
|
|
/* now have uncompressed, filtered image data in outbuf */
|
|
eod = outbuf + BS - zstrm.avail_out;
|
|
while (p < eod) {
|
|
|
|
if (cur_linebytes) { /* GRP 20000727: bugfix */
|
|
int filttype = p[0];
|
|
if (filttype <= 4) {
|
|
filter_counts[last_filter][filttype]++;
|
|
last_filter = filttype;
|
|
} else {
|
|
/* Handle all user defined filters in one block. */
|
|
filter_counts[last_filter][6]++;
|
|
last_filter = 6;
|
|
}
|
|
if (filttype > 127) {
|
|
if (lace > 1)
|
|
break; /* assume it's due to unknown interlace method */
|
|
if (numfilt_this_block == 0) {
|
|
/* warn only on first one per block; don't break */
|
|
printf("%s private (invalid?) %srow-filter type (%d) "
|
|
"(warning)\n", verbose? "\n ":fname, verbose? "":"IDAT ",
|
|
filttype);
|
|
set_err(kWarning);
|
|
}
|
|
} else if (filttype > 4) {
|
|
if (lace <= 1) {
|
|
printf("%s invalid %srow-filter type (%d)\n",
|
|
verbose? " ":fname, verbose? "":"IDAT ", filttype);
|
|
set_err(kMinorError);
|
|
} /* else assume it's due to unknown interlace method */
|
|
break;
|
|
}
|
|
if (verbose > 3) { /* GRR 20000304 */
|
|
printf(" [%1d]", filttype);
|
|
fflush(stdout);
|
|
++numfilt;
|
|
for (i = 1; i < cur_linebytes; ++i, ++p) {
|
|
printf(" %d", (int)p[1]);
|
|
fflush(stdout);
|
|
}
|
|
++p;
|
|
printf("\n ");
|
|
fflush(stdout);
|
|
} else {
|
|
if (verbose > 1) {
|
|
printf(" %1d", filttype);
|
|
if (++numfilt_this_block % 25 == 0)
|
|
printf("\n ");
|
|
}
|
|
++numfilt;
|
|
if (lace && verbose > 1) {
|
|
int passm1, cur_pass_delta=0;
|
|
|
|
for (passm1 = 0; passm1 < 6; ++passm1) { /* omit pass 7 */
|
|
if (numfilt == numfilt_pass[passm1]) {
|
|
++cur_pass_delta;
|
|
if (color) {
|
|
printf("%s %s|", COLOR_NORMAL, COLOR_WHITE_BOLD);
|
|
} else {
|
|
printf(" |");
|
|
}
|
|
if (++numfilt_this_block % 25 == 0) /* pretend | is one */
|
|
printf("%s\n %s", color_off,
|
|
pass_color[cur_pass + cur_pass_delta]);
|
|
}
|
|
}
|
|
if (numfilt_this_block % 25) /* else already did this */
|
|
printf("%s%s", color_off,
|
|
pass_color[cur_pass + cur_pass_delta]);
|
|
}
|
|
p += cur_linebytes;
|
|
}
|
|
}
|
|
cur_y += cur_yskip;
|
|
|
|
if (lace) {
|
|
while (cur_y >= h) { /* may loop if very short image */
|
|
/*
|
|
pass xskip yskip xoff yoff
|
|
1 8 8 0 0
|
|
2 8 8 4 0
|
|
3 4 8 0 4
|
|
4 4 4 2 0
|
|
5 2 4 0 2
|
|
6 2 2 1 0
|
|
7 1 2 0 1
|
|
*/
|
|
++cur_pass;
|
|
if (cur_pass & 1) { /* beginning an odd pass */
|
|
cur_yoff = cur_xoff;
|
|
cur_xoff = 0;
|
|
cur_xskip >>= 1;
|
|
} else { /* beginning an even pass */
|
|
if (cur_pass == 2)
|
|
cur_xoff = 4;
|
|
else {
|
|
cur_xoff = cur_yoff >> 1;
|
|
cur_yskip >>= 1;
|
|
}
|
|
cur_yoff = 0;
|
|
}
|
|
cur_y = cur_yoff;
|
|
/* effective width is reduced if even pass: subtract cur_xoff */
|
|
cur_width = (w - cur_xoff + cur_xskip - 1) / cur_xskip;
|
|
cur_linebytes = ((cur_width*bitdepth + 7) >> 3) + 1;
|
|
if (cur_linebytes == 1) /* just the filter byte? no can do */
|
|
cur_linebytes = 0; /* GRP 20000727: added fix */
|
|
}
|
|
} else if (cur_y >= h) {
|
|
if (verbose > 3) { /* GRR 20000304: bad code */
|
|
printf(" %td bytes remaining in buffer before inflateEnd()",
|
|
eod-p); // ptrdiff_t
|
|
printf("\n ");
|
|
fflush(stdout);
|
|
i = inflateEnd(&zstrm); /* we're all done */
|
|
if (i == Z_OK || i == Z_STREAM_ERROR)
|
|
printf(" inflateEnd() returns %s\n ",
|
|
i == Z_OK? "Z_OK" : "Z_STREAM_ERROR");
|
|
else
|
|
printf(" inflateEnd() returns %d\n ", i);
|
|
fflush(stdout);
|
|
} else
|
|
inflateEnd(&zstrm); /* we're all done */
|
|
zlib_error = -1; /* kill outermost loop (over chunks) */
|
|
err = Z_STREAM_END; /* kill middle loop */
|
|
break; /* kill innermost loop */
|
|
}
|
|
}
|
|
p -= (eod - outbuf); /* wrap p back into outbuf region */
|
|
zstrm.next_out = outbuf;
|
|
zstrm.avail_out = BS;
|
|
|
|
/* get more input (waiting until buffer empties is not necessary best
|
|
* zlib strategy, but simpler than shifting leftover data around) */
|
|
if (zstrm.avail_in == 0 && sz > toread) {
|
|
int data_read;
|
|
|
|
sz -= toread;
|
|
toread = (sz > BS)? BS:sz;
|
|
if ((data_read = fread(buffer, 1, toread, fp)) != toread) {
|
|
printf("\n%s EOF while reading %s data\n", verbose? ":":fname,
|
|
chunkid);
|
|
set_err(kCriticalError);
|
|
return global_error;
|
|
}
|
|
crc = update_crc(crc, buffer, toread);
|
|
zstrm.next_in = buffer;
|
|
zstrm.avail_in = toread;
|
|
}
|
|
}
|
|
if (verbose > 1 && no_err(kMinorError))
|
|
printf("%s (%ld out of %ld)\n", color_off, numfilt, numfilt_total);
|
|
}
|
|
if (zlib_error > 0) /* our flag, not zlib's (-1 means normal exit) */
|
|
set_err(kMajorError);
|
|
#endif /* USE_ZLIB */
|
|
last_is_IDAT = 1;
|
|
last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| IEND |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "IEND") == 0) {
|
|
if (!mng && have_IEND) {
|
|
printf("%s multiple IEND not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 0) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"IEND ");
|
|
set_err(kMinorError);
|
|
} else if (jng && need_JSEP && !have_JSEP) {
|
|
printf("%s missing JSEP in 20-bit JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (jng && have_JDAT <= 0) {
|
|
printf("%s no JDAT chunks\n", verbose? ":":fname);
|
|
set_err(kMajorError);
|
|
/*
|
|
* FIXME: what's minimum valid JPEG/JFIF length?
|
|
* } else if (jng && have_JDAT < 10) {
|
|
* printf("%s not enough JDAT data\n", verbose? ":":fname);
|
|
* set_err(kMajorError);
|
|
*/
|
|
} else if (png && have_IDAT <= 0) {
|
|
printf("%s no IDAT chunks\n", verbose? ":":fname);
|
|
set_err(kMajorError);
|
|
} else if (png && have_IDAT < 10) {
|
|
printf("%s not enough IDAT data\n", verbose? ":":fname);
|
|
set_err(kMajorError);
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
have_IEND = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| bKGD |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "bKGD") == 0) {
|
|
if (!mng && have_bKGD) {
|
|
printf("%s multiple bKGD not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"bKGD ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
}
|
|
switch (ityp) {
|
|
case 0:
|
|
case 4:
|
|
if (sz != 2) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"bKGD ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n gray = 0x%04x\n", SH(buffer));
|
|
}
|
|
break;
|
|
case 1: /* MNG top-level chunk (default values): "as if 16-bit RGBA" */
|
|
case 2:
|
|
case 6:
|
|
if (sz != 6) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"bKGD ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n red = 0x%04x, green = 0x%04x, blue = 0x%04x\n",
|
|
SH(buffer), SH(buffer+2), SH(buffer+4));
|
|
}
|
|
break;
|
|
case 3:
|
|
if (sz != 1) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"bKGD ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[0] >= nplte) {
|
|
printf("%s %sindex (%u) falls outside PLTE (%u)\n",
|
|
verbose? ":":fname, verbose? "":"bKGD ", buffer[0], nplte);
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n index = %u\n", buffer[0]);
|
|
}
|
|
break;
|
|
}
|
|
have_bKGD = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| cHRM |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "cHRM") == 0) {
|
|
if (!mng && have_cHRM) {
|
|
printf("%s multiple cHRM not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_PLTE) {
|
|
printf("%s %smust precede PLTE\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ");
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 32) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
double wx, wy, rx, ry, gx, gy, bx, by;
|
|
|
|
wx = (double)LG(buffer)/100000;
|
|
wy = (double)LG(buffer+4)/100000;
|
|
rx = (double)LG(buffer+8)/100000;
|
|
ry = (double)LG(buffer+12)/100000;
|
|
gx = (double)LG(buffer+16)/100000;
|
|
gy = (double)LG(buffer+20)/100000;
|
|
bx = (double)LG(buffer+24)/100000;
|
|
by = (double)LG(buffer+28)/100000;
|
|
|
|
if (wx < 0 || wx > 0.8 || wy <= 0 || wy > 1.0 || wx + wy > 1.0) {
|
|
printf("%s invalid %swhite point %0g %0g\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ", wx, wy);
|
|
set_err(kMinorError);
|
|
} else if (rx < 0 || rx > 1.0 || ry < 0 || ry > 1.0 || rx + ry > 1.0) {
|
|
printf("%s invalid %sred point %0g %0g\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ", rx, ry);
|
|
set_err(kMinorError);
|
|
} else if (gx < 0 || gx > 1.0 || gy < 0 || gy > 1.0 || gx + gy > 1.0) {
|
|
printf("%s invalid %sgreen point %0g %0g\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ", gx, gy);
|
|
set_err(kMinorError);
|
|
} else if (bx < 0 || bx > 1.0 || by < 0 || by > 1.0 || bx + by > 1.0) {
|
|
printf("%s invalid %sblue point %0g %0g\n",
|
|
verbose? ":":fname, verbose? "":"cHRM ", bx, by);
|
|
set_err(kMinorError);
|
|
}
|
|
else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(" White x = %0g y = %0g, Red x = %0g y = %0g\n",
|
|
wx, wy, rx, ry);
|
|
printf(" Green x = %0g y = %0g, Blue x = %0g y = %0g\n",
|
|
gx, gy, bx, by);
|
|
}
|
|
}
|
|
have_cHRM = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| fRAc |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "fRAc") == 0) {
|
|
if (verbose)
|
|
printf("\n undefined fractal parameters (ancillary, safe to copy)\n"
|
|
" [contact Tim Wegner, twegner@phoenix.net, for specification]\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| gAMA |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "gAMA") == 0) {
|
|
if (!mng && have_gAMA) {
|
|
printf("%s multiple gAMA not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"gAMA ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_PLTE) {
|
|
printf("%s %smust precede PLTE\n",
|
|
verbose? ":":fname, verbose? "":"gAMA ");
|
|
set_err(kMinorError);
|
|
} else if (sz != 4) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"gAMA ");
|
|
set_err(kMajorError);
|
|
} else if (LG(buffer) == 0) {
|
|
printf("%s invalid %svalue (0.0000)\n",
|
|
verbose? ":":fname, verbose? "":"gAMA ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(": %#0.5g\n", (double)LG(buffer)/100000);
|
|
}
|
|
have_gAMA = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| gIFg |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "gIFg") == 0) {
|
|
if (jng) {
|
|
printf("%s gIFg not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 4) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"gIFg ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
double dtime = .01 * SH(buffer+2);
|
|
|
|
printf("\n disposal method = %d, user input flag = %d, display time = %lf seconds\n",
|
|
buffer[0], buffer[1], dtime);
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| gIFt |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "gIFt") == 0) {
|
|
printf("%s %sDEPRECATED CHUNK\n",
|
|
verbose? ":":fname, verbose? "":"gIFt ");
|
|
set_err(kMinorError);
|
|
if (jng) {
|
|
printf("%s gIFt not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz < 24) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"gIFt ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(" %ldx%ld-pixel text grid at (%ld,%ld) pixels from upper "
|
|
"left\n", LG(buffer+8), LG(buffer+12), LG(buffer), LG(buffer+4));
|
|
printf(" character cell = %dx%d pixels\n", buffer[16], buffer[17]);
|
|
printf(" foreground color = 0x%02d%02d%02d, background color = "
|
|
"0x%02d%02d%02d\n", buffer[18], buffer[19], buffer[20], buffer[21],
|
|
buffer[22], buffer[23]);
|
|
printf(" %ld bytes of text data\n", sz-24);
|
|
/* GRR: print text according to grid size/cell size? */
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| gIFx |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "gIFx") == 0) {
|
|
if (jng) {
|
|
printf("%s gIFx not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz < 11) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"gIFx ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(
|
|
"\n application ID = %.*s, authentication code = 0x%02x%02x%02x\n",
|
|
8, buffer, buffer[8], buffer[9], buffer[10]);
|
|
printf(" %ld bytes of application data\n", sz-11);
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| hIST |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "hIST") == 0) {
|
|
if (jng) {
|
|
printf("%s hIST not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_hIST) {
|
|
printf("%s multiple hIST not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!have_PLTE) {
|
|
printf("%s %smust follow PLTE\n",
|
|
verbose? ":":fname, verbose? "":"hIST ");
|
|
set_err(kMinorError);
|
|
} else if (png && have_IDAT) {
|
|
printf("%s %smust precede IDAT\n",
|
|
verbose? ":":fname, verbose? "":"hIST ");
|
|
set_err(kMinorError);
|
|
} else if (sz != nplte * 2) {
|
|
printf("%s invalid number of %sentries (%g)\n",
|
|
verbose? ":":fname, verbose? "":"hIST ", (double)sz / 2);
|
|
set_err(kMajorError);
|
|
}
|
|
if ((verbose || (printpal && !quiet)) && no_err(kMinorError)) {
|
|
if (!verbose && printpal && !quiet)
|
|
printf(" hIST chunk");
|
|
printf(": %ld histogram entr%s\n", sz / 2, sz/2 == 1? "y":"ies");
|
|
}
|
|
if (printpal && no_err(kMinorError)) {
|
|
char *spc;
|
|
|
|
if (sz < 10)
|
|
spc = " ";
|
|
else if (sz < 100)
|
|
spc = " ";
|
|
else
|
|
spc = " ";
|
|
for (i = j = 0; j < sz; ++i, j += 2)
|
|
printf("%s%3d: %5u\n", spc, i, SH(buffer+j));
|
|
}
|
|
have_hIST = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| iCCP |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "iCCP") == 0) {
|
|
int name_len;
|
|
|
|
if (!mng && have_iCCP) {
|
|
printf("%s multiple iCCP not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_sRGB && 0) {
|
|
printf("%s %snot allowed with sRGB\n",
|
|
verbose? ":":fname, verbose? "":"iCCP ");
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_PLTE) {
|
|
printf("%s %smust precede PLTE\n",
|
|
verbose? ":":fname, verbose? "":"iCCP ");
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"iCCP ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (check_keyword(buffer, toread, &name_len, "profile name",
|
|
chunkid, fname)) {
|
|
set_err(kMinorError);
|
|
} else {
|
|
int remainder = toread - name_len - 3;
|
|
uch compr = buffer[name_len+1];
|
|
|
|
if (remainder < 0) {
|
|
printf("%s invalid %slength\n", /* or input buffer too small */
|
|
verbose? ":":fname, verbose? "":"iCCP ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[name_len] != 0) {
|
|
printf("%s missing NULL after %sprofile name\n",
|
|
verbose? ":":fname, verbose? "":"iCCP ");
|
|
set_err(kMajorError);
|
|
} else if (compr > 0 && compr < 128) {
|
|
printf("%s invalid %scompression method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"iCCP ", compr);
|
|
set_err(kMinorError);
|
|
} else if (compr >= 128) {
|
|
set_err(kWarning);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n profile name = ");
|
|
init_printbuf_state(&prbuf_state);
|
|
print_buffer(&prbuf_state, buffer, name_len, 0);
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
printf("%scompression method = %d (%s)%scompressed profile = "
|
|
"%ld bytes\n", (name_len > 24)? "\n ":", ", compr,
|
|
(compr == 0)? "deflate":"private: warning",
|
|
(name_len > 24)? ", ":"\n ", sz-name_len-2);
|
|
/* FIXME: should use remainder instead? */
|
|
}
|
|
}
|
|
have_iCCP = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| iTXt |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "iTXt") == 0) {
|
|
int keylen;
|
|
|
|
if (check_keyword(buffer, toread, &keylen, "keyword", chunkid, fname))
|
|
set_err(kMinorError);
|
|
else {
|
|
int compressed = 0, compr = 0;
|
|
#ifdef USE_ZLIB
|
|
int can_inflate = 0;
|
|
#endif
|
|
|
|
init_printbuf_state(&prbuf_state);
|
|
if (verbose) {
|
|
printf(", keyword: ");
|
|
}
|
|
if (verbose || printtext) {
|
|
print_buffer(&prbuf_state, buffer, keylen, 0);
|
|
}
|
|
if (verbose)
|
|
printf("\n");
|
|
else if (printtext)
|
|
printf(":\n");
|
|
/* FIXME: need some size checks here? */
|
|
compressed = buffer[keylen+1];
|
|
if (compressed < 0 || compressed > 1) {
|
|
printf("%s invalid %scompression flag (%d)\n",
|
|
verbose? ":":fname, verbose? "":"iTXt ", compressed);
|
|
set_err(kMinorError);
|
|
} else if ((compr = (uch)buffer[keylen+2]) > 127) {
|
|
printf("%s private (invalid?) %scompression method (%d) "
|
|
"(warning)\n", verbose? ":":fname, verbose? "":"iTXt ", compr);
|
|
set_err(kWarning);
|
|
} else if (compr > 0) {
|
|
printf("%s invalid %scompression method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"iTXt ", compr);
|
|
set_err(kMinorError);
|
|
}
|
|
#ifdef USE_ZLIB
|
|
else
|
|
can_inflate = 1;
|
|
#endif
|
|
if (no_err(kMinorError)) {
|
|
int taglen = keywordlen(buffer+keylen+3, toread-keylen-3);
|
|
int tranlen = keywordlen(buffer+keylen+3+taglen+1,
|
|
toread-(keylen+3+taglen+1));
|
|
if (verbose) {
|
|
if (taglen > 0) {
|
|
printf(" %scompressed, language tag = ", compressed? "":"un");
|
|
print_buffer(&prbuf_state, buffer+keylen+3, taglen, 0);
|
|
} else {
|
|
printf(" %scompressed, no language tag",
|
|
compressed? "":"un");
|
|
}
|
|
if (buffer[keylen+3+taglen+1] == 0)
|
|
printf("\n no translated keyword, %ld bytes of compressed UTF-8 text\n",
|
|
sz - (keylen+3+taglen+1));
|
|
else {
|
|
printf("\n %ld bytes of translated keyword and compressed UTF-8 text\n",
|
|
sz - (keylen+3+taglen));
|
|
print_buffer(&prbuf_state, buffer+keylen+3+taglen+1, tranlen, 0);
|
|
}
|
|
} else if (printtext) {
|
|
int text_offset = keylen+3+taglen+1+tranlen+1;
|
|
if (!compressed) {
|
|
print_buffer(&prbuf_state, buffer+text_offset,
|
|
toread-text_offset, 1);
|
|
printf("\n");
|
|
}
|
|
#ifdef USE_ZLIB
|
|
else if (can_inflate) {
|
|
inflate_engine e;
|
|
|
|
if (inflate_engine_init(&e) == Z_OK) {
|
|
int rc = inflate_engine_process(&e, buffer+text_offset,
|
|
toread-text_offset, chunkid, fname, toread == sz);
|
|
const char *tag = "";
|
|
const char *type = "UTF-8 text";
|
|
|
|
if (rc == Z_OK) {
|
|
if (toread == sz) {
|
|
set_err(kWarning);
|
|
tag = "UNTERMINATED ";
|
|
fprintf(stderr, "%s: %s: %s: unterminated deflate data\n",
|
|
fname, chunkid, buffer);
|
|
}
|
|
} else if (rc != Z_STREAM_END) {
|
|
tag = "DAMAGED ";
|
|
fprintf(stderr, "%s: %s: %s: damaged deflate data\n", fname,
|
|
chunkid, buffer);
|
|
}
|
|
|
|
if (inflate_engine_check_text(&e, chunkid, fname)) {
|
|
set_err(kWarning); /* text contains '\0' */
|
|
type = "binary";
|
|
}
|
|
|
|
printf(" (%ld characters of %scompressed %s %s)\n",
|
|
inflate_engine_length(&e), tag, chunkid, type);
|
|
inflate_engine_print(&prbuf_state, &e, 1);
|
|
inflate_engine_destroy(&e);
|
|
printf("\n");
|
|
} else
|
|
fprintf(stderr, "%s: %s: failed to init zlib (ignored)\n",
|
|
fname, chunkid);
|
|
}
|
|
#endif /* USE_ZLIB */
|
|
else {
|
|
printf(" (compressed %s UTF-8 text)\n", chunkid);
|
|
}
|
|
|
|
if (toread != sz)
|
|
printf(" {%ld bytes not read}\n", sz-toread);
|
|
}
|
|
}
|
|
report_printbuf(&prbuf_state, fname, chunkid); /* print CR/LF & NULLs info */
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| oFFs |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "oFFs") == 0) {
|
|
if (!mng && have_oFFs) {
|
|
printf("%s multiple oFFs not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"oFFs ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 9) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"oFFs ");
|
|
set_err(kMinorError);
|
|
} else if (buffer[8] > 1) {
|
|
printf("%s invalid %sunit specifier (%u)\n",
|
|
verbose? ":":fname, verbose? "":"oFFs ", buffer[8]);
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(": %ldx%ld %s offset\n", LG(buffer), LG(buffer+4),
|
|
(buffer[8] == 0)? "pixels":"micrometers");
|
|
}
|
|
have_oFFs = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| pCAL |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "pCAL") == 0) {
|
|
if (jng) {
|
|
printf("%s pCAL not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_pCAL) {
|
|
printf("%s multiple pCAL not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_IDAT) {
|
|
printf("%s %smust precede IDAT\n",
|
|
verbose? ":":fname, verbose? "":"pCAL ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
int name_len;
|
|
|
|
if (check_keyword(buffer, toread, &name_len, "calibration name",
|
|
chunkid, fname))
|
|
set_err(kMinorError);
|
|
else if (sz < name_len + 15) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"pCAL ");
|
|
set_err(kMajorError);
|
|
} else {
|
|
long x0 = LG(buffer+name_len+1); /* already checked sz */
|
|
long x1 = LG(buffer+name_len+5);
|
|
int eqn_num = buffer[name_len+9];
|
|
int num_params = buffer[name_len+10];
|
|
|
|
if (eqn_num < 0 || eqn_num > 3) {
|
|
printf("%s invalid %s equation type (%d)\n",
|
|
verbose? ":":fname, verbose? "":chunkid, eqn_num);
|
|
set_err(kMinorError);
|
|
} else if (num_params != eqn_params[eqn_num]) {
|
|
printf(
|
|
"%s invalid number of parameters (%d) for %s equation type %d\n",
|
|
verbose? ":":fname, num_params, verbose? "":chunkid, eqn_num);
|
|
set_err(kMinorError);
|
|
} else if (verbose) {
|
|
int remainder = 0;
|
|
uch *pbuf;
|
|
|
|
printf(": equation type %d\n", eqn_num);
|
|
printf(" %s\n", eqn_type[eqn_num]);
|
|
printf(" calibration name = ");
|
|
init_printbuf_state(&prbuf_state);
|
|
print_buffer(&prbuf_state, buffer, name_len, 0);
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
if (toread != sz) {
|
|
printf(
|
|
"\n pngcheck INTERNAL LOGIC ERROR: toread (%d) != sz (%ld)",
|
|
toread, sz);
|
|
} else
|
|
remainder = toread - name_len - 11;
|
|
pbuf = buffer + name_len + 11;
|
|
if (*pbuf == 0)
|
|
printf("\n no physical_value unit name\n");
|
|
else {
|
|
int unit_len = keywordlen(pbuf, remainder);
|
|
|
|
printf("\n physical_value unit name = ");
|
|
init_printbuf_state(&prbuf_state);
|
|
print_buffer(&prbuf_state, pbuf, unit_len, 0);
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
printf("\n");
|
|
pbuf += unit_len;
|
|
remainder -= unit_len;
|
|
}
|
|
printf(" x0 = %ld\n", x0);
|
|
printf(" x1 = %ld\n", x1);
|
|
for (i = 0; i < num_params; ++i) {
|
|
int len;
|
|
|
|
if (remainder < 2) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"pCAL ");
|
|
set_err(kMajorError);
|
|
break;
|
|
}
|
|
if (*pbuf != 0) {
|
|
printf("%s %smissing NULL separator\n",
|
|
verbose? ":":fname, verbose? "":"pCAL ");
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
++pbuf;
|
|
--remainder;
|
|
len = keywordlen(pbuf, remainder);
|
|
printf(" p%d = ", i);
|
|
init_printbuf_state(&prbuf_state);
|
|
print_buffer(&prbuf_state, pbuf, len, 0);
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
printf("\n");
|
|
pbuf += len;
|
|
remainder -= len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
have_pCAL = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| pHYs |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "pHYs") == 0) {
|
|
if (!mng && have_pHYs) {
|
|
printf("%s multiple pHYs not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"pHYs ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 9) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"pHYs ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[8] > 1) {
|
|
printf("%s invalid %sunit specifier (%u)\n",
|
|
verbose? ":":fname, verbose? "":"pHYs ", buffer[8]);
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
ulg xres = LG(buffer);
|
|
ulg yres = LG(buffer+4);
|
|
unsigned units = buffer[8];
|
|
|
|
printf(": %lux%lu pixels/%s", xres, yres, units? "meter":"unit");
|
|
if (units && xres == yres)
|
|
printf(" (%lu dpi)", (ulg)(xres*0.0254 + 0.5));
|
|
else if (!units) {
|
|
ulg gcf_xres_yres = gcf(xres, yres);
|
|
|
|
printf(" (%lu:%lu)", xres/gcf_xres_yres, yres/gcf_xres_yres);
|
|
}
|
|
printf("\n");
|
|
}
|
|
have_pHYs = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| sBIT |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "sBIT") == 0) {
|
|
int maxbits = (ityp == 3)? 8 : sampledepth;
|
|
|
|
if (jng) {
|
|
printf("%s sBIT not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_sBIT) {
|
|
printf("%s multiple sBIT not allowed\n", verbose? ":" : fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_PLTE) {
|
|
printf("%s %smust precede PLTE\n",
|
|
verbose? ":" : fname, verbose? "" : "sBIT ");
|
|
set_err(kMinorError);
|
|
} else if (png && have_IDAT) {
|
|
printf("%s %smust precede IDAT\n",
|
|
verbose? ":" : fname, verbose? "" : "sBIT ");
|
|
set_err(kMinorError);
|
|
}
|
|
switch (ityp) {
|
|
case 0:
|
|
if (sz != 1) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":" : fname, verbose? "" : "sBIT ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
|
|
printf("%s %d %sgrey bits invalid for %d-bit/sample image\n",
|
|
verbose? ":" : fname, buffer[0], verbose? "" : "sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
printf("\n gray = %u = 0x%02x\n", buffer[0], buffer[0]);
|
|
}
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
if (sz != 3) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"sBIT ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
|
|
printf("%s %d %sred bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[0], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (buffer[1] == 0 || buffer[1] > maxbits) {
|
|
printf("%s %d %sgreen bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[1], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (buffer[2] == 0 || buffer[2] > maxbits) {
|
|
printf("%s %d %sblue bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[2], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
printf("\n red = %u = 0x%02x, green = %u = 0x%02x, "
|
|
"blue = %u = 0x%02x\n", buffer[0], buffer[0],
|
|
buffer[1], buffer[1], buffer[2], buffer[2]);
|
|
}
|
|
break;
|
|
case 4:
|
|
if (sz != 2) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"sBIT ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
|
|
printf("%s %d %sgrey bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[0], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMajorError);
|
|
} else if (buffer[1] == 0 || buffer[1] > maxbits) {
|
|
printf("%s %d %salpha bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[1], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMajorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
printf("\n gray = %u = 0x%02x, alpha = %u = 0x%02x\n",
|
|
buffer[0], buffer[0], buffer[1], buffer[1]);
|
|
}
|
|
break;
|
|
case 6:
|
|
if (sz != 4) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"sBIT ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[0] == 0 || buffer[0] > maxbits) {
|
|
printf("%s %d %sred bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[0], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (buffer[1] == 0 || buffer[1] > maxbits) {
|
|
printf("%s %d %sgreen bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[1], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (buffer[2] == 0 || buffer[2] > maxbits) {
|
|
printf("%s %d %sblue bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[2], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (buffer[3] == 0 || buffer[3] > maxbits) {
|
|
printf("%s %d %salpha bits invalid for %d-bit/sample image\n",
|
|
verbose? ":":fname, buffer[3], verbose? "":"sBIT ", maxbits);
|
|
set_err(kMinorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
printf("\n red = %u = 0x%02x, green = %u = 0x%02x, "
|
|
"blue = %u = 0x%02x, alpha = %u = 0x%02x\n", buffer[0], buffer[0],
|
|
buffer[1], buffer[1], buffer[2], buffer[2], buffer[3], buffer[3]);
|
|
}
|
|
break;
|
|
}
|
|
have_sBIT = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| sCAL |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "sCAL") == 0) {
|
|
int unittype = buffer[0];
|
|
uch *pPixwidth = buffer+1, *pPixheight=NULL;
|
|
|
|
if (!mng && have_sCAL) {
|
|
printf("%s multiple sCAL not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz < 4) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ");
|
|
set_err(kMinorError);
|
|
} else if (unittype < 1 || unittype > 2) {
|
|
printf("%s invalid %sunit specifier (%d)\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ", unittype);
|
|
set_err(kMinorError);
|
|
} else {
|
|
uch *qq;
|
|
for (qq = pPixwidth; qq < buffer+sz; ++qq) {
|
|
if (*qq == 0)
|
|
break;
|
|
}
|
|
if (qq == buffer+sz) {
|
|
printf("%s missing %snull separator\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ");
|
|
set_err(kMinorError);
|
|
} else {
|
|
pPixheight = qq + 1;
|
|
if (pPixheight == buffer+sz || *pPixheight == 0) {
|
|
printf("%s missing %spixel height\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ");
|
|
set_err(kMinorError);
|
|
}
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
for (qq = pPixheight; qq < buffer+sz; ++qq) {
|
|
if (*qq == 0)
|
|
break;
|
|
}
|
|
if (qq != buffer+sz) {
|
|
printf("%s extra %snull separator (warning)\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ");
|
|
set_err(kWarning);
|
|
}
|
|
if (*pPixwidth == '-' || *pPixheight == '-') {
|
|
printf("%s invalid negative %svalue(s)\n",
|
|
verbose? ":":fname, verbose? "":"sCAL ");
|
|
set_err(kMinorError);
|
|
} else if (check_ascii_float(pPixwidth, pPixheight-pPixwidth-1,
|
|
chunkid, fname) ||
|
|
check_ascii_float(pPixheight, buffer+sz-pPixheight,
|
|
chunkid, fname))
|
|
{
|
|
set_err(kMinorError);
|
|
}
|
|
}
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
if (sz >= BS)
|
|
sz = BS-1;
|
|
buffer[sz] = '\0';
|
|
printf(": image size %s x %s %s\n", pPixwidth, pPixheight,
|
|
(unittype == 1)? "meters":"radians");
|
|
}
|
|
have_sCAL = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| sPLT |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "sPLT") == 0) {
|
|
int name_len;
|
|
|
|
if (jng) {
|
|
printf("%s sPLT not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_IDAT) {
|
|
printf("%s %smust precede IDAT\n",
|
|
verbose? ":":fname, verbose? "":"sPLT ");
|
|
set_err(kMinorError);
|
|
} else if (check_keyword(buffer, toread, &name_len, "palette name",
|
|
chunkid, fname)) {
|
|
set_err(kMinorError);
|
|
} else {
|
|
uch bps = buffer[name_len+1];
|
|
int remainder = toread - name_len - 2;
|
|
int bytes = (bps >> 3);
|
|
int entry_sz = 4*bytes + 2;
|
|
int nsplt = remainder / entry_sz;
|
|
|
|
if (remainder < 0) {
|
|
printf("%s invalid %slength\n", /* or input buffer too small */
|
|
verbose? ":":fname, verbose? "":"sPLT ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[name_len] != 0) {
|
|
printf("%s missing NULL after %spalette name\n",
|
|
verbose? ":":fname, verbose? "":"sPLT ");
|
|
set_err(kMinorError);
|
|
} else if (bps != 8 && bps != 16) {
|
|
printf("%s invalid %ssample depth (%u bits)\n",
|
|
verbose? ":":fname, verbose? "":"sPLT ", bps);
|
|
set_err(kMinorError);
|
|
} else if (remainder % entry_sz != 0) {
|
|
printf("%s invalid number of %sentries (%g)\n",
|
|
verbose? ":":fname, verbose? "":"sPLT ",
|
|
(double)remainder / entry_sz);
|
|
set_err(kMajorError);
|
|
} else if (verbose || (printpal && !quiet)) {
|
|
if (!verbose && printpal && !quiet)
|
|
printf(" sPLT chunk");
|
|
printf(": %d palette/histogram entr%s\n", nsplt,
|
|
nsplt == 1? "y":"ies");
|
|
printf(" sample depth = %u bits, palette name = ", bps);
|
|
init_printbuf_state(&prbuf_state);
|
|
print_buffer(&prbuf_state, buffer, name_len, 0);
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
printf("\n");
|
|
}
|
|
if (printpal && no_err(kMinorError)) {
|
|
char *spc;
|
|
int i, j = name_len+2;
|
|
|
|
if (nsplt < 10)
|
|
spc = " ";
|
|
else if (nsplt < 100)
|
|
spc = " ";
|
|
else if (nsplt < 1000)
|
|
spc = " ";
|
|
else if (nsplt < 10000)
|
|
spc = " ";
|
|
else
|
|
spc = " ";
|
|
/* GRR: could check for (required) non-increasing freq order */
|
|
/* GRR: could also check for all zero freqs: undefined hist */
|
|
if (bytes == 1) {
|
|
for (i = 0; i < nsplt; ++i, j += 6)
|
|
printf("%s%3d: (%3u,%3u,%3u,%3u) = "
|
|
"(0x%02x,0x%02x,0x%02x,0x%02x) freq = %u\n", spc, i,
|
|
buffer[j], buffer[j+1], buffer[j+2], buffer[j+3],
|
|
buffer[j], buffer[j+1], buffer[j+2], buffer[j+3],
|
|
SH(buffer+j+4));
|
|
} else {
|
|
for (i = 0; i < nsplt; ++i, j += 10)
|
|
printf("%s%5d: (%5u,%5u,%5u,%5u) = (%04x,%04x,%04x,%04x) "
|
|
"freq = %u\n", spc, i, SH(buffer+j), SH(buffer+j+2),
|
|
SH(buffer+j+4), SH(buffer+j+6), SH(buffer+j), SH(buffer+j+2),
|
|
SH(buffer+j+4), SH(buffer+j+6), SH(buffer+j+8));
|
|
}
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| sRGB |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "sRGB") == 0) {
|
|
if (!mng && have_sRGB) {
|
|
printf("%s multiple sRGB not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_iCCP) {
|
|
printf("%s %snot allowed with iCCP\n",
|
|
verbose? ":":fname, verbose? "":"sRGB ");
|
|
set_err(kMinorError);
|
|
} else if (!mng && have_PLTE) {
|
|
printf("%s %smust precede PLTE\n",
|
|
verbose? ":":fname, verbose? "":"sRGB ");
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"sRGB ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 1) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"sRGB ");
|
|
set_err(kMinorError);
|
|
} else if (buffer[0] > 3) {
|
|
printf("%s %sinvalid rendering intent\n",
|
|
verbose? ":":fname, verbose? "":"sRGB ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n rendering intent = %s\n", rendering_intent[buffer[0]]);
|
|
}
|
|
have_sRGB = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| sTER |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "sTER") == 0) {
|
|
if (!mng && have_sTER) {
|
|
printf("%s multiple sTER not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (!mng && (have_IDAT || have_JDAT)) {
|
|
printf("%s %smust precede %cDAT\n",
|
|
verbose? ":":fname, verbose? "":"sTER ", have_IDAT? 'I':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 1) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"sTER ");
|
|
set_err(kMinorError);
|
|
} else if (buffer[0] > 1) {
|
|
printf("%s invalid %slayout mode\n",
|
|
verbose? ":":fname, verbose? "":"sTER ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n stereo subimage layout = %s\n",
|
|
buffer[0]? "divergent (parallel)":"cross-eyed");
|
|
}
|
|
have_sTER = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------* *------*
|
|
| tEXt | | zTXt |
|
|
*------* *------*/
|
|
} else if (strcmp(chunkid, "tEXt") == 0 || strcmp(chunkid, "zTXt") == 0) {
|
|
int ztxt = (chunkid[0] == 'z');
|
|
int keylen;
|
|
#ifdef USE_ZLIB
|
|
int can_inflate = 0;
|
|
#endif
|
|
|
|
if (check_keyword(buffer, toread, &keylen, "keyword", chunkid, fname))
|
|
set_err(kMinorError);
|
|
else if (ztxt) {
|
|
int compr = (uch)buffer[keylen+1];
|
|
if (compr > 127) {
|
|
printf("%s private (possibly invalid) %scompression method (%d) "
|
|
"(warning)\n", verbose? ":":fname, verbose? "":"zTXt ", compr);
|
|
set_err(kWarning);
|
|
} else if (compr > 0) {
|
|
printf("%s invalid %scompression method (%d)\n",
|
|
verbose? ":":fname, verbose? "":"zTXt ", compr);
|
|
set_err(kMinorError);
|
|
}
|
|
#ifdef USE_ZLIB
|
|
else
|
|
can_inflate = 1;
|
|
#endif
|
|
}
|
|
else if (check_text(buffer + keylen + 1, toread - keylen - 1, chunkid,
|
|
fname)) {
|
|
set_err(kWarning);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
init_printbuf_state(&prbuf_state);
|
|
if (verbose || printtext) {
|
|
if (verbose)
|
|
printf(", keyword: ");
|
|
print_buffer(&prbuf_state, buffer, keylen, 0);
|
|
}
|
|
if (printtext) {
|
|
printf(verbose? "\n" : ":\n");
|
|
if (strcmp(chunkid, "tEXt") == 0)
|
|
print_buffer(&prbuf_state, buffer + keylen + 1,
|
|
toread - keylen - 1, 1);
|
|
#ifdef USE_ZLIB
|
|
else if (can_inflate) {
|
|
inflate_engine e;
|
|
|
|
if (inflate_engine_init(&e) == Z_OK) {
|
|
int rc = inflate_engine_process(&e, buffer+keylen+2,
|
|
toread-keylen-2, chunkid, fname, toread == sz);
|
|
const char *tag = "";
|
|
const char *type = "text";
|
|
|
|
if (rc == Z_OK) {
|
|
if (toread == sz) {
|
|
set_err(kWarning);
|
|
tag = "UNTERMINATED ";
|
|
fprintf(stderr, "%s: %s: %s: unterminated deflate data\n",
|
|
fname, chunkid, buffer);
|
|
}
|
|
} else if (rc != Z_STREAM_END) {
|
|
tag = "DAMAGED ";
|
|
fprintf(stderr, "%s: %s: %s: damaged deflate data\n", fname,
|
|
chunkid, buffer);
|
|
}
|
|
|
|
if (inflate_engine_check_text(&e, chunkid, fname)) {
|
|
set_err(kWarning); /* text contains '\0' */
|
|
type = "binary";
|
|
}
|
|
|
|
printf(" (%ld characters of %scompressed %s %s)\n",
|
|
inflate_engine_length(&e), tag, chunkid, type);
|
|
inflate_engine_print(&prbuf_state, &e, 1);
|
|
inflate_engine_destroy(&e);
|
|
} else
|
|
fprintf(stderr, "%s: %s: failed to init zlib (ignored)\n", fname,
|
|
chunkid);
|
|
}
|
|
#endif /* USE_ZLIB */
|
|
else {
|
|
printf("%s(compressed %s text)", verbose? " " : "", chunkid);
|
|
}
|
|
|
|
/* For the sake of simplifying this program, we will not print
|
|
* the contents of a tEXt chunk whose size is larger than the
|
|
* buffer size (currently 32K). People should use zTXt for
|
|
* such large amounts of text, anyway! Note that this does not
|
|
* mean that the tEXt/zTXt contents will be lost if extracting.
|
|
*/
|
|
printf("\n");
|
|
if (toread != sz)
|
|
printf(" {%ld bytes not read}\n", sz-toread);
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| tIME |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "tIME") == 0) {
|
|
if (!mng && have_tIME) {
|
|
printf("%s multiple tIME not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 7) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"tIME ");
|
|
set_err(kMinorError);
|
|
} else {
|
|
int yr = SH(buffer);
|
|
int mo = buffer[2];
|
|
int dy = buffer[3];
|
|
int hh = buffer[4];
|
|
int mm = buffer[5];
|
|
int ss = buffer[6];
|
|
|
|
if (yr < 1995) {
|
|
/* conversion to PNG format counts as modification... */
|
|
/* FIXME: also test for future dates? (may allow current year + 1) */
|
|
printf("%s invalid %syear (%d)\n",
|
|
verbose? ":":fname, verbose? "":"tIME ", yr);
|
|
set_err(kMinorError);
|
|
} else if (mo < 1 || mo > 12) {
|
|
printf("%s invalid %smonth (%d)\n",
|
|
verbose? ":":fname, verbose? "":"tIME ", mo);
|
|
set_err(kMinorError);
|
|
} else if (dy < 1 || dy > 31) {
|
|
/* FIXME: also validate day given specified month? */
|
|
printf("%s invalid %sday (%d)\n",
|
|
verbose? ":":fname, verbose? "":"tIME ", dy);
|
|
set_err(kMinorError);
|
|
} else if (hh < 0 || hh > 23) {
|
|
printf("%s invalid %shour (%d)\n",
|
|
verbose? ":":fname, verbose? "":"tIME ", hh);
|
|
set_err(kMinorError);
|
|
} else if (mm < 0 || mm > 59) {
|
|
printf("%s invalid %sminute (%d)\n",
|
|
verbose? ":":fname, verbose? "":"tIME ", mm);
|
|
set_err(kMinorError);
|
|
} else if (ss < 0 || ss > 60) {
|
|
printf("%s invalid %ssecond (%d)\n",
|
|
verbose? ":":fname, verbose? "":"tIME ", ss);
|
|
set_err(kMinorError);
|
|
}
|
|
/* print the date in RFC 1123 format, rather than stored order */
|
|
/* FIXME: change to ISO-whatever format, i.e., yyyy-mm-dd hh:mm:ss? */
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(": %2d %s %4d %02d:%02d:%02d UTC\n", dy, getmonth(mo), yr,
|
|
hh, mm, ss);
|
|
}
|
|
}
|
|
have_tIME = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| tRNS |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "tRNS") == 0) {
|
|
if (jng) {
|
|
printf("%s tRNS not defined in JNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (png && have_tRNS) {
|
|
printf("%s multiple tRNS not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (ityp == 3 && !have_PLTE) {
|
|
printf("%s %smust follow PLTE\n",
|
|
verbose? ":":fname, verbose? "":"tRNS ");
|
|
set_err(kMinorError);
|
|
} else if (png && have_IDAT) {
|
|
printf("%s %smust precede IDAT\n",
|
|
verbose? ":":fname, verbose? "":"tRNS ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
switch (ityp) {
|
|
case 0:
|
|
if (sz != 2) {
|
|
printf("%s invalid %slength for %s image\n",
|
|
verbose? ":":fname, verbose? "":"tRNS ", png_type[ityp]);
|
|
set_err(kMajorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
printf("\n gray = 0x%04x\n", SH(buffer));
|
|
}
|
|
break;
|
|
case 2:
|
|
if (sz != 6) {
|
|
printf("%s invalid %slength for %s image\n",
|
|
verbose? ":":fname, verbose? "":"tRNS ", png_type[ityp]);
|
|
set_err(kMajorError);
|
|
} else if (verbose && no_err(kMinorError)) {
|
|
printf("\n red = 0x%04x, green = 0x%04x, blue = 0x%04x\n",
|
|
SH(buffer), SH(buffer+2), SH(buffer+4));
|
|
}
|
|
break;
|
|
case 3:
|
|
if (sz > nplte) {
|
|
printf("%s invalid %slength for %s image\n",
|
|
verbose? ":":fname, verbose? "":"tRNS ", png_type[ityp]);
|
|
set_err(kMajorError);
|
|
} else if ((verbose || (printpal && !quiet)) && no_err(kMinorError))
|
|
{
|
|
if (!verbose && printpal && !quiet)
|
|
printf(" tRNS chunk");
|
|
printf(": %ld transparency entr%s\n", sz, sz == 1? "y":"ies");
|
|
}
|
|
if (printpal && no_err(kMinorError)) {
|
|
char *spc;
|
|
|
|
if (sz < 10)
|
|
spc = " ";
|
|
else if (sz < 100)
|
|
spc = " ";
|
|
else
|
|
spc = " ";
|
|
for (i = 0; i < sz; ++i)
|
|
printf("%s%3d: %3d = 0x%02x\n", spc, i, buffer[i], buffer[i]);
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s %snot allowed in %s image\n",
|
|
verbose? ":":fname, verbose? "":"tRNS ", png_type[ityp]);
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
}
|
|
have_tRNS = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*===========================================*/
|
|
/* identifiable private chunks; guts unknown */
|
|
|
|
/*------*
|
|
| cmOD |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "cmOD") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Microsoft Picture It private, ancillary, unsafe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| cmPP | (guessing MS)
|
|
*------*/
|
|
} else if (strcmp(chunkid, "cmPP") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Microsoft Picture It(?) private, ancillary, unsafe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| cpIp |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "cpIp") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Microsoft Picture It private, ancillary, safe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| mkBF |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "mkBF") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Macromedia Fireworks private, ancillary, unsafe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| mkBS |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "mkBS") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Macromedia Fireworks private, ancillary, unsafe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| mkBT |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "mkBT") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Macromedia Fireworks private, ancillary, unsafe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| mkTS |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "mkTS") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Macromedia Fireworks(?) private, ancillary, unsafe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/* msOG - Microsoft? Macromedia? */
|
|
|
|
/*------*
|
|
| pcLb |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "pcLb") == 0) {
|
|
if (verbose)
|
|
printf("\n "
|
|
"Piclab(?) private, ancillary, safe-to-copy chunk\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| prVW |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "prVW") == 0) {
|
|
if (verbose)
|
|
printf("\n Macromedia Fireworks preview chunk"
|
|
" (private, ancillary, unsafe to copy)\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| spAL | intermediate sPLT test version (still had gamma field)
|
|
*------*/
|
|
} else if (strcmp(chunkid, "spAL") == 0) {
|
|
/* png-group/documents/history/png-proposed-sPLT-19961015.html */
|
|
if (verbose)
|
|
printf("\n preliminary/test version of sPLT "
|
|
"(private, ancillary, unsafe to copy)\n");
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*================================================*
|
|
* JNG chunks (with the exception of JHDR, above) *
|
|
*================================================*/
|
|
|
|
/*------*
|
|
| JDAT |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "JDAT") == 0) {
|
|
if (png) {
|
|
printf("%s JDAT not defined in PNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (have_JDAT && !(last_is_JDAT || last_is_IDAT)) {
|
|
/* GRR: need to check for consecutive IDATs within MNG segments */
|
|
if (mng) { /* reset things (FIXME: SEMI-HACK--check for segments!) */
|
|
have_JDAT = 0;
|
|
if (verbose)
|
|
printf("\n");
|
|
} else {
|
|
printf(
|
|
"%s JDAT chunks must be consecutive or interleaved with IDATs\n",
|
|
verbose? ":":fname);
|
|
set_err(kMajorError);
|
|
if (!force)
|
|
return global_error;
|
|
}
|
|
} else if (verbose)
|
|
printf("\n");
|
|
have_JDAT = 1;
|
|
last_is_IDAT = 0;
|
|
last_is_JDAT = 1; /* also true if last was JSEP (see below) */
|
|
|
|
/*------*
|
|
| JSEP |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "JSEP") == 0) {
|
|
if (png) {
|
|
printf("%s JSEP not defined in PNG\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (jng && bitdepth != 20) {
|
|
printf("%s JSEP allowed only if 8-bit and 12-bit JDATs present\n",
|
|
verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (jng && have_JSEP) {
|
|
printf("%s multiple JSEP not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (jng && !(last_is_JDAT || last_is_IDAT)) {
|
|
printf("%s JSEP must appear between JDAT or IDAT chunks\n",
|
|
verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 0) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"JSEP ");
|
|
set_err(kMinorError);
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
have_JSEP = 1;
|
|
last_is_IDAT = 0;
|
|
last_is_JDAT = 1; /* effectively... (GRR HACK) */
|
|
|
|
/*===============================================================*
|
|
* MNG chunks (with the exception of MHDR and JNG chunks, above) *
|
|
*===============================================================*/
|
|
|
|
/*------*
|
|
| DHDR | DELTA-PNG
|
|
*------*/
|
|
} else if (strcmp(chunkid, "DHDR") == 0) {
|
|
if (png || jng) {
|
|
printf("%s DHDR not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 4 && sz != 12 && sz != 20) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"DHDR ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
uch dtype = buffer[3];
|
|
|
|
printf("\n object ID = %u, image type = %s, delta type = %s\n",
|
|
SH(buffer), buffer[2]? "PNG":"unspecified",
|
|
(dtype < sizeof(delta_type)/sizeof(char *))?
|
|
delta_type[dtype] : inv);
|
|
if (sz > 4) {
|
|
if (dtype == 7) {
|
|
printf("%s invalid %slength for delta type %d\n",
|
|
verbose? ":":fname, verbose? "":"DHDR ", dtype);
|
|
set_err(kMinorError);
|
|
} else {
|
|
printf(" block width = %lu, block height = %lu\n", LG(buffer+4),
|
|
LG(buffer+8));
|
|
if (sz > 12) {
|
|
if (dtype == 0) {
|
|
printf("%s invalid %slength for delta type %d\n",
|
|
verbose? ":":fname, verbose? "":"DHDR ", dtype);
|
|
set_err(kMinorError);
|
|
} else
|
|
printf(" x offset = %lu, y offset = %lu\n", LG(buffer+12),
|
|
LG(buffer+16));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
#ifdef USE_ZLIB
|
|
first_idat = 1; /* flag: next IDAT will be the first in this subimage */
|
|
zlib_error = 0; /* flag: no zlib errors yet in this file */
|
|
/* GRR 20000304: data dump not yet compatible with interlaced images: */
|
|
if (lace && verbose > 3) /* (FIXME eventually...or move to pngcrunch) */
|
|
verbose = 2;
|
|
#endif
|
|
|
|
/*------*
|
|
| FRAM |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "FRAM") == 0) {
|
|
if (png || jng) {
|
|
printf("%s FRAM not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz == 0 && verbose) {
|
|
printf(": empty\n");
|
|
} else if (verbose) {
|
|
uch fmode = buffer[0];
|
|
|
|
printf(": mode %d\n %s\n", fmode,
|
|
(fmode < sizeof(framing_mode)/sizeof(char *))?
|
|
framing_mode[fmode] : inv);
|
|
if (sz > 1) {
|
|
uch *p = buffer+1;
|
|
int bytes_left, found_null=0;
|
|
|
|
if (*p) {
|
|
printf(" frame name = ");
|
|
do {
|
|
if (*p)
|
|
putchar(*p); /* GRR EBCDIC WARNING */
|
|
else {
|
|
putchar('\n');
|
|
++p;
|
|
break;
|
|
}
|
|
} while (++p < buffer + sz);
|
|
} else {
|
|
++p; /* skip over null */
|
|
++found_null;
|
|
}
|
|
bytes_left = sz - (p-buffer); /* FIXME: is sz big enough? */
|
|
if (bytes_left == 0 && found_null) {
|
|
printf(" invalid trailing NULL byte\n");
|
|
set_err(kMinorError);
|
|
} else if (bytes_left < 4) {
|
|
printf(" invalid length\n");
|
|
set_err(kMajorError);
|
|
} else {
|
|
uch cid = *p++; /* change_interframe_delay */
|
|
uch ctt = *p++; /* change_timeout_and_termination */
|
|
uch cscb = *p++; /* change_subframe_clipping_boundaries */
|
|
uch csil = *p++; /* change_sync_id_list */
|
|
|
|
if (cid > 2 || ctt > 8 || cscb > 2 || csil > 2) {
|
|
printf(" invalid change flags\n");
|
|
set_err(kMinorError);
|
|
} else {
|
|
bytes_left -= 4;
|
|
printf(" %s\n", change_interframe_delay[cid]);
|
|
/* GRR: need real error-checking here: */
|
|
if (cid && bytes_left >= 4) {
|
|
ulg delay = LG(p);
|
|
|
|
printf(" new delay = %lu tick%s\n", delay, (delay == 1L)?
|
|
"" : "s");
|
|
p += 4;
|
|
bytes_left -= 4;
|
|
}
|
|
printf(" %s\n", change_timeout_and_termination[ctt]);
|
|
/* GRR: need real error-checking here: */
|
|
if (ctt && bytes_left >= 4) {
|
|
ulg val = LG(p);
|
|
|
|
if (val == 0x7fffffffL)
|
|
printf(" new timeout = infinite\n");
|
|
else
|
|
printf(" new timeout = %lu tick%s\n", val, (val == 1L)?
|
|
"" : "s");
|
|
p += 4;
|
|
bytes_left -= 4;
|
|
}
|
|
printf(" %s\n", change_subframe_clipping_boundaries[cscb]);
|
|
/* GRR: need real error-checking here: */
|
|
if (cscb && bytes_left >= 17) {
|
|
printf(" new frame clipping boundaries (%s):\n", (*p++)?
|
|
"differences from previous values":"absolute pixel values");
|
|
printf(
|
|
" left = %ld, right = %ld, top = %ld, bottom = %ld\n",
|
|
LG(p), LG(p+4), LG(p+8), LG(p+12));
|
|
p += 16;
|
|
bytes_left -= 17;
|
|
}
|
|
printf(" %s\n", change_sync_id_list[csil]);
|
|
if (csil) {
|
|
if (bytes_left) {
|
|
while (bytes_left >= 4) {
|
|
printf(" %lu\n", LG(p));
|
|
p += 4;
|
|
bytes_left -= 4;
|
|
}
|
|
} else
|
|
printf(" [empty list]\n");
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
if (p < buffer + sz)
|
|
printf(" (bytes left = %d)\n", sz - (p-buffer));
|
|
else
|
|
printf(" (no bytes left)\n");
|
|
*/
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| SAVE |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "SAVE") == 0) {
|
|
if (png || jng) {
|
|
printf("%s SAVE not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (have_SAVE) {
|
|
printf("%s multiple SAVE not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz > 0 && verbose) {
|
|
uch offsize = buffer[0];
|
|
|
|
if (offsize != 4 && offsize != 8) {
|
|
printf("%s invalid %soffset size (%u bytes)\n",
|
|
verbose? ":":fname, verbose? "":"SAVE ", (unsigned)offsize);
|
|
set_err(kMinorError);
|
|
} else if (sz > 1) {
|
|
uch *p = buffer+1;
|
|
int bytes_left = sz-1;
|
|
|
|
printf("\n offset size = %u bytes\n", (unsigned)offsize);
|
|
while (bytes_left > 0) {
|
|
uch type = *p;
|
|
|
|
if ((type == 0 && bytes_left < 5+2*offsize) ||
|
|
(type == 1 && bytes_left < 1+offsize)) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"SAVE ");
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
printf(" entry type = %s", (type <
|
|
sizeof(entry_type)/sizeof(char *))? entry_type[type] : inv);
|
|
++p;
|
|
if (type <= 1) {
|
|
ulg first4 = LG(p);
|
|
|
|
printf(", offset = ");
|
|
if ((offsize == 4 && first4 == 0L) ||
|
|
(offsize == 8 && first4 == 0L && LG(p+4) == 0L))
|
|
printf("unknown\n");
|
|
else if (offsize == 4)
|
|
printf("0x%08lx\n", first4);
|
|
else
|
|
printf("0x%08lx%08lx\n", first4, LG(p+4)); /* big-endian */
|
|
p += offsize;
|
|
if (type == 0) {
|
|
printf(" nominal start time = 0x%08lx", LG(p));
|
|
if (offsize == 8)
|
|
printf("%08lx", LG(p+4));
|
|
p += offsize;
|
|
printf(", nominal layer number = %lu,\n", LG(p));
|
|
p += 4;
|
|
printf(" nominal frame number = %lu\n", LG(p));
|
|
p += 4;
|
|
}
|
|
} else
|
|
printf("\n");
|
|
bytes_left = sz - (p-buffer); /* FIXME: is sz big enough? */
|
|
// name must match that in corresponding SEEK/FRAM/eXPI chunk, or
|
|
// be omitted if unnamed segment (not checked!)
|
|
if (bytes_left) {
|
|
int have_name = 0;
|
|
if (*p) {
|
|
have_name = 1;
|
|
printf(" name = ");
|
|
}
|
|
do {
|
|
if (*p)
|
|
putchar(*p); /* GRR EBCDIC WARNING */
|
|
else {
|
|
++p; // skip over separator byte (but omitted for final one)
|
|
break;
|
|
}
|
|
} while (++p < buffer + sz);
|
|
if (have_name)
|
|
printf("\n");
|
|
bytes_left = sz - (p-buffer); /* FIXME: is sz big enough? */
|
|
}
|
|
} /* end while (bytes_left > 0) */
|
|
}
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
have_SAVE = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| SEEK |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "SEEK") == 0) {
|
|
if (png || jng) {
|
|
printf("%s SEEK not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (!have_SAVE) {
|
|
printf("%s %snot allowed without preceding SAVE chunk\n",
|
|
verbose? ":":fname, verbose? "":"SEEK ");
|
|
set_err(kMinorError);
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
if (sz > 0) {
|
|
init_printbuf_state(&prbuf_state);
|
|
print_buffer(&prbuf_state, buffer, sz, 1);
|
|
report_printbuf(&prbuf_state, fname, chunkid);
|
|
printf("\n");
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| nEED |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "nEED") == 0) {
|
|
if (png || jng) {
|
|
printf("%s nEED not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz > 0 && verbose) {
|
|
uch *p = buffer;
|
|
uch *lastbreak = buffer;
|
|
|
|
if (sz < 32)
|
|
printf(": ");
|
|
else
|
|
printf("\n ");
|
|
do {
|
|
if (*p)
|
|
putchar(*p); /* GRR EBCDIC WARNING */
|
|
else if (p - lastbreak > 40)
|
|
printf("\n ");
|
|
else {
|
|
putchar(';');
|
|
putchar(' ');
|
|
}
|
|
} while (++p < buffer + sz);
|
|
printf("\n");
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| DEFI |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "DEFI") == 0) {
|
|
if (png || jng) {
|
|
printf("%s DEFI not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 2 && sz != 3 && sz != 4 && sz != 12 && sz != 28) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"DEFI ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
const char *noshow = do_not_show[0];
|
|
uch concrete = 0;
|
|
long x = 0L;
|
|
long y = 0L;
|
|
|
|
if (sz > 2) {
|
|
if (buffer[2] == 1)
|
|
noshow = do_not_show[1];
|
|
else if (buffer[2] > 1)
|
|
noshow = inv;
|
|
}
|
|
if (sz > 3)
|
|
concrete = buffer[3];
|
|
if (sz > 4) {
|
|
x = LG(buffer+4);
|
|
y = LG(buffer+8);
|
|
}
|
|
printf("\n object ID = %u, %s, %s, x = %ld, y = %ld\n", SH(buffer),
|
|
noshow, concrete? "concrete":"abstract", x, y);
|
|
if (sz > 12) {
|
|
printf(
|
|
" clipping: left = %ld, right = %ld, top = %ld, bottom = %ld\n",
|
|
LG(buffer+12), LG(buffer+16), LG(buffer+20), LG(buffer+24));
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| BACK |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "BACK") == 0) {
|
|
if (png || jng) {
|
|
printf("%s BACK not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz < 6 || sz == 8 || sz > 10) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"BACK ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n red = 0x%04x, green = 0x%04x, blue = 0x%04x (%s)\n",
|
|
SH(buffer), SH(buffer+2), SH(buffer+4), (sz > 6 && (buffer[6] & 1))?
|
|
"mandatory":"advisory");
|
|
if (sz >= 9) {
|
|
printf(" background image ID = %u (%s, %stile)\n",
|
|
SH(buffer+7), (buffer[6] & 1)? "mandatory":"advisory",
|
|
(sz > 9 && (buffer[9] & 1))? "":"do not ");
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| MOVE |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "MOVE") == 0) {
|
|
if (png || jng) {
|
|
printf("%s MOVE not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 13) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"MOVE ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n first object ID = %u, last object ID = %u\n",
|
|
SH(buffer), SH(buffer+2));
|
|
if (buffer[4])
|
|
printf(
|
|
" relative change in position: delta-x = %ld, delta-y = %ld\n",
|
|
LG(buffer+5), LG(buffer+9));
|
|
else
|
|
printf(" new position: x = %ld, y = %ld\n",
|
|
LG(buffer+5), LG(buffer+9));
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| CLON |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "CLON") == 0) {
|
|
if (png || jng) {
|
|
printf("%s CLON not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 4 && sz != 5 && sz != 6 && sz != 7 && sz != 16) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"CLON ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
uch ct = 0; /* full clone */
|
|
uch dns = 2; /* same as parent's */
|
|
uch cf = 0; /* same as parent's */
|
|
uch ldt = 1; /* delta from parent */
|
|
long x = 0L;
|
|
long y = 0L;
|
|
|
|
if (sz > 4)
|
|
ct = buffer[4];
|
|
if (sz > 5)
|
|
dns = buffer[5];
|
|
if (sz > 6)
|
|
cf = buffer[6];
|
|
if (sz > 7) {
|
|
ldt = buffer[7];
|
|
x = LG(buffer+8);
|
|
y = LG(buffer+12);
|
|
}
|
|
printf("\n parent object ID = %u, clone object ID = %u\n",
|
|
SH(buffer), SH(buffer+2));
|
|
printf(" clone type = %s, %s, %s\n",
|
|
(ct < sizeof(clone_type)/sizeof(char *))? clone_type[ct] : inv,
|
|
(dns < sizeof(do_not_show)/sizeof(char *))? do_not_show[dns] : inv,
|
|
cf? "same concreteness as parent":"abstract");
|
|
if (ldt)
|
|
printf(" difference from parent's position: delta-x = %ld,"
|
|
" delta-y = %ld\n", x, y);
|
|
else
|
|
printf(" absolute position: x = %ld, y = %ld\n", x, y);
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| SHOW |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "SHOW") == 0) {
|
|
if (png || jng) {
|
|
printf("%s SHOW not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 0 && sz != 2 && sz != 4 && sz != 5) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"SHOW ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
ush first = 0;
|
|
ush last = 65535;
|
|
uch smode = 2;
|
|
|
|
if (sz > 0) {
|
|
first = last = SH(buffer);
|
|
smode = 0;
|
|
}
|
|
if (sz > 2)
|
|
last = SH(buffer+2);
|
|
if (sz > 4)
|
|
smode = buffer[4];
|
|
printf("\n first object = %u, last object = %u\n", first, last);
|
|
printf(" %s\n",
|
|
(smode < sizeof(show_mode)/sizeof(char *))? show_mode[smode] : inv);
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| CLIP |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "CLIP") == 0) {
|
|
if (png || jng) {
|
|
printf("%s CLIP not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 21) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"CLIP ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(
|
|
"\n first object = %u, last object = %u; %s clip boundaries:\n",
|
|
SH(buffer), SH(buffer+2), buffer[4]? "relative change in":"absolute");
|
|
printf(" left = %ld, right = %ld, top = %ld, bottom = %ld\n",
|
|
LG(buffer+5), LG(buffer+9), LG(buffer+13), LG(buffer+17));
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| LOOP |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "LOOP") == 0) {
|
|
if (png || jng) {
|
|
printf("%s LOOP not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz < 5 || (sz > 6 && ((sz-6) % 4) != 0)) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"LOOP ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf(": nest level = %u\n count = %lu, termination = %s\n",
|
|
(unsigned)(buffer[0]), LG(buffer+1), sz == 5?
|
|
termination_condition[0] : termination_condition[buffer[5] & 0x3]);
|
|
/* GRR: not checking for valid buffer[1] values */
|
|
if (sz > 6) {
|
|
printf(" iteration min = %lu", LG(buffer+6));
|
|
if (sz > 10) {
|
|
printf(", max = %lu", LG(buffer+10));
|
|
if (sz > 14) {
|
|
long i, count = (sz-14) >> 2;
|
|
|
|
printf(", signal number%s = %lu", (count > 1)? "s" : "",
|
|
LG(buffer+14));
|
|
for (i = 1; i < count; ++i)
|
|
printf(", %lu", LG(buffer+14+(i<<2)));
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| ENDL |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "ENDL") == 0) {
|
|
if (png || jng) {
|
|
printf("%s ENDL not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 1) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"ENDL ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError))
|
|
printf(": nest level = %u\n", (unsigned)(buffer[0]));
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| PROM |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "PROM") == 0) {
|
|
if (png || jng) {
|
|
printf("%s PROM not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 3) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"PROM ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
const char *ctype;
|
|
|
|
switch (buffer[0]) {
|
|
case 2:
|
|
ctype = "gray+alpha";
|
|
break;
|
|
case 4:
|
|
ctype = "RGB";
|
|
break;
|
|
case 6:
|
|
ctype = "RGBA";
|
|
break;
|
|
default:
|
|
ctype = inv;
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
printf("\n new color type = %s, new bit depth = %u\n",
|
|
ctype, (unsigned)(buffer[1]));
|
|
/* GRR: not checking for valid buffer[1] values */
|
|
printf(" fill method (if bit depth increased) = %s\n",
|
|
buffer[2]? "zero fill" : "left bit replication");
|
|
/* GRR: not checking for valid buffer[2] values */
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| fPRI |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "fPRI") == 0) {
|
|
if (png || jng) {
|
|
printf("%s fPRI not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 2) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"fPRI ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError))
|
|
printf(": %spriority = %u\n", buffer[0]? "delta " : "",
|
|
(unsigned)(buffer[1]));
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| eXPI |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "eXPI") == 0) {
|
|
if (png || jng) {
|
|
printf("%s eXPI not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz <= 2) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"eXPI ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n snapshot ID = %u, snapshot name = %.*s\n", SH(buffer),
|
|
(int)(sz-2), buffer+2); /* GRR EBCDIC WARNING */
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| BASI |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "BASI") == 0) {
|
|
if (png || jng) {
|
|
printf("%s BASI not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 13 && sz != 19 && sz != 22) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"BASI ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
w = LG(buffer);
|
|
h = LG(buffer+4);
|
|
if (w == 0 || h == 0) {
|
|
printf("%s invalid %simage dimensions (%ldx%ld)\n",
|
|
verbose? ":":fname, verbose? "":"BASI ", w, h);
|
|
set_err(kMinorError);
|
|
}
|
|
bitdepth = (uch)buffer[8];
|
|
ityp = (uch)buffer[9];
|
|
if (ityp > sizeof(png_type)/sizeof(char*)) {
|
|
ityp = 1; /* avoid out of range array index */
|
|
}
|
|
switch (bitdepth) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
if (ityp == 2 || ityp == 4 || ityp == 6) { /* RGB or GA or RGBA */
|
|
printf("%s invalid %sbit depth (%d) for %s image\n",
|
|
verbose? ":":fname, verbose? "":"BASI ", bitdepth,
|
|
png_type[ityp]);
|
|
set_err(kMinorError);
|
|
}
|
|
break;
|
|
case 8:
|
|
break;
|
|
case 16:
|
|
if (ityp == 3) { /* palette */
|
|
printf("%s invalid %sbit depth (%d) for %s image\n",
|
|
verbose? ":":fname, verbose? "":"BASI ", bitdepth,
|
|
png_type[ityp]);
|
|
set_err(kMinorError);
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s invalid %sbit depth (%d)\n",
|
|
verbose? ":":fname, verbose? "":"BASI ", bitdepth);
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
lace = (uch)buffer[12];
|
|
switch (ityp) {
|
|
case 2:
|
|
bitdepth *= 3; /* RGB */
|
|
break;
|
|
case 4:
|
|
bitdepth *= 2; /* gray+alpha */
|
|
break;
|
|
case 6:
|
|
bitdepth *= 4; /* RGBA */
|
|
break;
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n %ld x %ld image, %d-bit %s, %sinterlaced\n", w, h,
|
|
bitdepth, (ityp > 6)? png_type[1]:png_type[ityp], lace? "":"non-");
|
|
}
|
|
if (sz > 13) {
|
|
ush red, green, blue;
|
|
long alpha = -1;
|
|
int viewable = -1;
|
|
|
|
red = SH(buffer+13);
|
|
green = SH(buffer+15);
|
|
blue = SH(buffer+17);
|
|
if (sz > 19) {
|
|
alpha = (long)SH(buffer+19);
|
|
if (sz > 21)
|
|
viewable = buffer[21];
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
if (ityp == 0)
|
|
printf(" gray = 0x%04x", red);
|
|
else
|
|
printf(" red = 0x%04x, green = 0x%04x, blue = 0x%04x",
|
|
red, green, blue);
|
|
if (alpha >= 0) {
|
|
printf(", alpha = 0x%04lx", alpha);
|
|
if (viewable >= 0)
|
|
printf(", %sviewable", viewable? "" : "not ");
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| IPNG | (empty stand-in for IHDR)
|
|
*------*/
|
|
} else if (strcmp(chunkid, "IPNG") == 0) {
|
|
if (png || jng) {
|
|
printf("%s IPNG not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz != 0) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"IPNG ");
|
|
set_err(kMinorError);
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| PPLT |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "PPLT") == 0) {
|
|
if (png || jng) {
|
|
printf("%s PPLT not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz < 4) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"PPLT ");
|
|
set_err(kMinorError);
|
|
} else {
|
|
char *plus;
|
|
uch dtype = buffer[0];
|
|
uch first_idx = buffer[1];
|
|
uch last_idx = buffer[2];
|
|
uch *buf = buffer+3;
|
|
int bytes_left = sz-3;
|
|
int samples, npplt = 0, nblks = 0;
|
|
|
|
if (!verbose && printpal && !quiet)
|
|
printf(" PPLT chunk");
|
|
if (verbose)
|
|
printf(": %s\n", (dtype < sizeof(pplt_delta_type)/sizeof(char *))?
|
|
pplt_delta_type[dtype] : inv);
|
|
plus = (dtype & 1)? "+" : "";
|
|
if (dtype < 2)
|
|
samples = 3;
|
|
else if (dtype < 4)
|
|
samples = 1;
|
|
else
|
|
samples = 4;
|
|
while (bytes_left > 0) {
|
|
bytes_left -= samples*(last_idx - first_idx + 1);
|
|
if (bytes_left < 0)
|
|
break;
|
|
++nblks;
|
|
for (i = first_idx; i <= last_idx; ++i, buf += samples) {
|
|
++npplt;
|
|
if (printpal) {
|
|
if (samples == 4)
|
|
printf(" %3d: %s(%3d,%3d,%3d,%3d) = "
|
|
"%s(0x%02x,0x%02x,0x%02x,0x%02x)\n", i,
|
|
plus, buf[0], buf[1], buf[2], buf[3],
|
|
plus, buf[0], buf[1], buf[2], buf[3]);
|
|
else if (samples == 3)
|
|
printf(" %3d: %s(%3d,%3d,%3d) = %s(0x%02x,0x%02x,0x%02x)\n",
|
|
i, plus, buf[0], buf[1], buf[2],
|
|
plus, buf[0], buf[1], buf[2]);
|
|
else
|
|
printf(" %3d: %s(%3d) = %s(0x%02x)\n", i,
|
|
plus, *buf, plus, *buf);
|
|
}
|
|
}
|
|
if (bytes_left > 2) {
|
|
first_idx = buf[0];
|
|
last_idx = buf[1];
|
|
buf += 2;
|
|
bytes_left -= 2;
|
|
} else if (bytes_left)
|
|
break;
|
|
}
|
|
if (bytes_left) {
|
|
printf("%s invalid %slength (too %s bytes)\n",
|
|
verbose? ":" : fname, verbose? "" : "PPLT ",
|
|
(bytes_left < 0)? "few" : "many");
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError))
|
|
printf(" %d %s palette entr%s in %d block%s\n",
|
|
npplt, (dtype & 1)? "delta" : "replacement", npplt== 1? "y":"ies",
|
|
nblks, nblks== 1? "":"s");
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| PAST |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "PAST") == 0) {
|
|
if (png || jng) {
|
|
printf("%s PAST not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz < 41 || ((sz-11) % 30) != 0) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"PAST ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[2] > 2) {
|
|
printf("%s invalid %starget delta type (%u)\n",
|
|
verbose? ":":fname, verbose? "":"PAST ", buffer[2]);
|
|
set_err(kMinorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
ush dest_id = SH(buffer);
|
|
uch target_dtype = buffer[2];
|
|
long x = LG(buffer+3);
|
|
long y = LG(buffer+7);
|
|
uch *buf = buffer+11;
|
|
int bytes_left = sz-11;
|
|
|
|
if (verbose)
|
|
printf("\n destination ID = %u, target = {%ld,%ld}%s\n", dest_id,
|
|
x, y, target_dtype == 1? " (delta from previous PAST, same ID)" :
|
|
(target_dtype == 2? " (delta from previous PAST)" : ""));
|
|
|
|
/* now loop over remaining groups of 30 bytes */
|
|
while (bytes_left > 0) {
|
|
ush src_id = SH(buf);
|
|
uch comp_mode = buf[2];
|
|
uch orient = buf[3];
|
|
uch offset_origin = buf[4];
|
|
long xoff = LG(buf+5);
|
|
long yoff = LG(buf+9);
|
|
uch bdry_origin = buf[13];
|
|
long left_clip = LG(buf+14);
|
|
long right_clip = LG(buf+18);
|
|
long top_clip = LG(buf+22);
|
|
long bott_clip = LG(buf+26);
|
|
|
|
if (src_id == 0) {
|
|
printf("%s invalid %ssource ID\n",
|
|
verbose? ":":fname, verbose? "":"PAST ");
|
|
set_err(kMinorError);
|
|
} else if (comp_mode > 2) {
|
|
printf("%s invalid %scomposition mode (%u)\n",
|
|
verbose? ":":fname, verbose? "":"PAST ", comp_mode);
|
|
set_err(kMinorError);
|
|
} else if (orient > 8 || (orient & 1)) {
|
|
printf("%s invalid %sorientation (%u)\n",
|
|
verbose? ":":fname, verbose? "":"PAST ", orient);
|
|
set_err(kMinorError);
|
|
} else if (offset_origin > 1) {
|
|
printf("%s invalid %soffset origin (%u)\n",
|
|
verbose? ":":fname, verbose? "":"PAST ", offset_origin);
|
|
set_err(kMinorError);
|
|
} else if (bdry_origin > 1) {
|
|
printf("%s invalid %sboundary origin (%u)\n",
|
|
verbose? ":":fname, verbose? "":"PAST ", bdry_origin);
|
|
set_err(kMinorError);
|
|
}
|
|
if (!no_err(kMinorError))
|
|
break;
|
|
|
|
if (verbose) {
|
|
printf(" source ID = %u: composition mode = %s,\n",
|
|
src_id, composition_mode[comp_mode]);
|
|
printf(" orientation = %s,\n", orientation[orient >> 1]);
|
|
printf(" offset = {%ld,%ld} measured from {%ld,%ld} in "
|
|
"destination image,\n", xoff, yoff,
|
|
offset_origin? x:0, offset_origin? y:0);
|
|
printf(" clipping box = {%ld,%ld} to {%ld,%ld} measured "
|
|
"from {%ld,%ld}\n", left_clip, top_clip, right_clip, bott_clip,
|
|
bdry_origin? x:0, bdry_origin? y:0);
|
|
}
|
|
buf += 30;
|
|
bytes_left -= 30;
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| TERM |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "TERM") == 0) {
|
|
if (png || jng) {
|
|
printf("%s TERM not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (have_TERM) {
|
|
printf("%s multiple TERM not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if ((sz != 1 && sz != 10) ||
|
|
(sz == 1 && buffer[0] == 3) ||
|
|
(sz == 10 && buffer[0] != 3))
|
|
{
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"TERM ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[0] > 3) {
|
|
printf("%s %sinvalid termination action\n",
|
|
verbose? ":":fname, verbose? "":"TERM ");
|
|
set_err(kMinorError);
|
|
} else if (buffer[0] == 3 && buffer[1] > 2) {
|
|
printf("%s %sinvalid termination action-after-iterations\n",
|
|
verbose? ":":fname, verbose? "":"TERM ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n action = %s\n", termination_action[buffer[0] /* & 3 */]);
|
|
if (sz >= 10) {
|
|
ulg val = LG(buffer+2);
|
|
|
|
printf(" action after iterations = %s\n",
|
|
termination_action[buffer[1]]);
|
|
printf(" inter-iteration delay = %lu tick%s, max iterations = ",
|
|
val, (val == 1)? "":"s");
|
|
val = LG(buffer+6);
|
|
if (val == 0x7fffffff)
|
|
printf("infinite\n");
|
|
else
|
|
printf("%lu\n", val);
|
|
}
|
|
}
|
|
have_TERM = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| DISC |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "DISC") == 0) {
|
|
if (png || jng) {
|
|
printf("%s DISC not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz & 1) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"DISC ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
if (sz == 0) {
|
|
printf("\n discard all nonzero objects%s\n",
|
|
have_SAVE? " except those before SAVE":"");
|
|
} else {
|
|
uch *buf = buffer;
|
|
int bytes_left = sz;
|
|
|
|
printf(": %ld objects\n", sz >> 1);
|
|
while (bytes_left > 0) {
|
|
printf(" discard ID = %u\n", SH(buf));
|
|
buf += 2;
|
|
bytes_left -= 2;
|
|
}
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| pHYg |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "pHYg") == 0) {
|
|
if (png || jng) {
|
|
printf("%s pHYg not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (!top_level) {
|
|
printf("%s %smust appear at MNG top level\n",
|
|
verbose? ":":fname, verbose? "":"pHYg ");
|
|
set_err(kMinorError);
|
|
} else if (sz != 9 && sz != 0) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"pHYg ");
|
|
set_err(kMajorError);
|
|
} else if (sz && buffer[8] > 1) {
|
|
printf("%s invalid %sunit specifier (%u)\n",
|
|
verbose? ":":fname, verbose? "":"pHYg ", buffer[8]);
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
if (sz == 0)
|
|
printf("\n %s\n",
|
|
have_pHYg? "nullifies previous pHYg values":"(no effect)");
|
|
else {
|
|
ulg xres = LG(buffer);
|
|
ulg yres = LG(buffer+4);
|
|
unsigned units = buffer[8];
|
|
|
|
printf(": %lux%lu pixels/%s", xres, yres, units? "meter":"unit");
|
|
if (units && xres == yres)
|
|
printf(" (%lu dpi)", (ulg)(xres*0.0254 + 0.5));
|
|
else if (!units) {
|
|
ulg gcf_xres_yres = gcf(xres, yres);
|
|
|
|
printf(" (%lu:%lu)", xres/gcf_xres_yres, yres/gcf_xres_yres);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
have_pHYg = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| DROP |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "DROP") == 0) {
|
|
if (png || jng) {
|
|
printf("%s DROP not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz & 0x3) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"DROP ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
uch *buf = buffer;
|
|
int bytes_left = sz;
|
|
int num_names = 0;
|
|
|
|
while (bytes_left > 0) {
|
|
if (check_chunk_name((char *)buf, fname) != 0) {
|
|
printf("%s invalid chunk name to be dropped\n",
|
|
verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
break;
|
|
}
|
|
if (verbose)
|
|
printf("%s%.*s", (num_names%12)? " ":"\n ", 4, buf);
|
|
++num_names;
|
|
buf += 4;
|
|
bytes_left -= 4;
|
|
}
|
|
if (verbose)
|
|
printf("\n");
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| DBYK |
|
|
*------*/
|
|
/* NOTE: the spec's "keyword at beginning" and "null-terminated" restric-
|
|
* tions limit the (known) chunk types that can be dropped to iCCP, pCAL,
|
|
* iTXt, tEXt, and zTXt--and the three text chunks are irrelevant in
|
|
* any case. Other chunks with keyword-like fields that do NOT qualify
|
|
* include FRAM, SAVE, SEEK, and eXPI. */
|
|
} else if (strcmp(chunkid, "DBYK") == 0) {
|
|
if (png || jng) {
|
|
printf("%s DBYK not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz < 6) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"DBYK ");
|
|
set_err(kMajorError);
|
|
} else if (buffer[4] > 1) {
|
|
printf("%s invalid %spolarity (%u)\n",
|
|
verbose? ":":fname, verbose? "":"DBYK ", buffer[4]);
|
|
set_err(kMinorError);
|
|
} else if (check_chunk_name((char *)buffer, fname) != 0) {
|
|
printf("%s invalid chunk name to be dropped\n",
|
|
verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
uch *buf = buffer + 5;
|
|
int bytes_left = sz - 5;
|
|
int first = 1;
|
|
int space_left = 75;
|
|
|
|
if (verbose) {
|
|
printf("\n %.*s: drop %s", 4, buffer, buffer[4]? "all but":"only");
|
|
space_left -= buffer[4]? 18:15; /* e.g., "cHNK: drop all but" */
|
|
}
|
|
while (bytes_left > 0) {
|
|
char *sep;
|
|
int keylen;
|
|
|
|
if (check_keyword(buf, bytes_left, &keylen, "keyword", chunkid,
|
|
fname))
|
|
set_err(kMinorError);
|
|
else if (keylen < bytes_left && buf[keylen] != 0) {
|
|
/* realistically, this can never happen (due to keywordlen())... */
|
|
printf("%s unterminated %skeyword\n",
|
|
verbose? ":":fname, verbose? "":"DBYK ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (!no_err(kMinorError))
|
|
break;
|
|
if (verbose) {
|
|
/* account for trailing comma (along with space and two quotes)
|
|
* even though we're not yet printing it, because we'll need to
|
|
* add it *on the same line* if there are any more keywords: */
|
|
if (keylen+4 < space_left) {
|
|
sep = first? "":",";
|
|
space_left -= keylen+4;
|
|
} else {
|
|
sep = ",\n "; /* indent two extra spaces */
|
|
space_left = 75 - 2 - (keylen+4);
|
|
}
|
|
printf("%s \"%.*s\"", sep, keylen, buf);
|
|
}
|
|
first = 0;
|
|
buf += keylen+1; /* no NULL separator for last keyword... */
|
|
bytes_left -= keylen+1; /* ...but then bytes_left will be < 0: NP */
|
|
}
|
|
if (verbose)
|
|
printf("\n");
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| ORDR |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "ORDR") == 0) {
|
|
if (png || jng) {
|
|
printf("%s ORDR not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (sz % 5) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"ORDR ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
uch *buf = buffer;
|
|
int bytes_left = sz;
|
|
|
|
if (verbose)
|
|
printf("\n");
|
|
while (bytes_left > 0) {
|
|
if (check_chunk_name((char *)buf, fname) != 0) {
|
|
printf("%s %slisted chunk name is invalid\n",
|
|
verbose? ":":fname, verbose? "":"ORDR: ");
|
|
set_err(kMinorError);
|
|
} else if (!(buf[0] & 0x20)) {
|
|
printf("%s %scritical chunk (%.*s) not allowed\n",
|
|
verbose? ":":fname, verbose? "":"ORDR: ", 4, buf);
|
|
set_err(kMinorError);
|
|
} else if (buf[4] > 4) {
|
|
printf("%s invalid %sordering value\n",
|
|
verbose? ":":fname, verbose? "":"ORDR ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (!no_err(kMinorError))
|
|
break;
|
|
if (verbose)
|
|
printf(" %.*s: %s\n", 4, buf, order_type[buf[4]]);
|
|
buf += 5;
|
|
bytes_left -= 5;
|
|
}
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| MAGN |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "MAGN") == 0) {
|
|
if (png || jng) {
|
|
printf("%s MAGN not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if ((sz <= 4 && (sz & 1)) ||
|
|
(sz >= 5 && sz <= 17 && !(sz & 1)) ||
|
|
sz > 18)
|
|
{
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"MAGN ");
|
|
set_err(kMajorError);
|
|
}
|
|
if (no_err(kMinorError)) {
|
|
if (sz == 0) {
|
|
if (verbose)
|
|
printf("\n %s\n",
|
|
have_MAGN? "nullifies previous MAGN values":"(no effect)");
|
|
} else {
|
|
ush first = SH(buffer);
|
|
ush last = first;
|
|
uch xmeth = 0; /* no X magnification */
|
|
ush mx = 1;
|
|
ush my = mx;
|
|
ush ml = mx;
|
|
ush mr = mx;
|
|
ush mt = my;
|
|
ush mb = my;
|
|
uch ymeth = xmeth;
|
|
|
|
if (sz > 2)
|
|
last = SH(buffer+2);
|
|
if (sz > 4)
|
|
xmeth = buffer[4];
|
|
if (xmeth) {
|
|
if (sz > 5)
|
|
mx = SH(buffer+5);
|
|
if (sz > 9)
|
|
ml = SH(buffer+9);
|
|
if (sz > 11)
|
|
mr = SH(buffer+11);
|
|
}
|
|
if (sz > 17)
|
|
ymeth = buffer[17];
|
|
if (ymeth) {
|
|
if (sz > 7)
|
|
my = SH(buffer+7);
|
|
if (sz > 13)
|
|
mt = SH(buffer+13);
|
|
if (sz > 15)
|
|
mb = SH(buffer+15);
|
|
}
|
|
if (xmeth > 5 || ymeth > 5) {
|
|
printf("%s invalid %smagnification method(s)\n",
|
|
verbose? ":":fname, verbose? "":"MAGN ");
|
|
set_err(kMinorError);
|
|
}
|
|
if (verbose && no_err(kMinorError)) {
|
|
printf("\n magnified object ID");
|
|
if (first == last)
|
|
printf(" = %u\n", first);
|
|
else
|
|
printf("s = %u to %u\n", first, last);
|
|
if (xmeth == ymeth)
|
|
printf(" method = %s\n", magnification_method[xmeth]);
|
|
else
|
|
printf(" X method = %s\n Y method = %s\n",
|
|
magnification_method[xmeth], magnification_method[ymeth]);
|
|
printf(" X mag = %u, left mag = %u, right mag = %u\n",
|
|
mx, ml, mr);
|
|
printf(" Y mag = %u, top mag = %u, bottom mag = %u\n",
|
|
my, mt, mb);
|
|
}
|
|
}
|
|
}
|
|
have_MAGN = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*------*
|
|
| MEND |
|
|
*------*/
|
|
} else if (strcmp(chunkid, "MEND") == 0) {
|
|
if (png || jng) {
|
|
printf("%s MEND not defined in %cNG\n", verbose? ":":fname,
|
|
png? 'P':'J');
|
|
set_err(kMinorError);
|
|
} else if (have_MEND) {
|
|
printf("%s multiple MEND not allowed\n", verbose? ":":fname);
|
|
set_err(kMinorError);
|
|
} else if (sz != 0) {
|
|
printf("%s invalid %slength\n",
|
|
verbose? ":":fname, verbose? "":"MEND ");
|
|
set_err(kMinorError);
|
|
} else if (verbose) {
|
|
printf("\n");
|
|
}
|
|
have_MEND = 1;
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
|
|
/*===============*
|
|
* unknown chunk *
|
|
*===============*/
|
|
|
|
} else {
|
|
if (CRITICAL(chunkid) && SAFECOPY(chunkid)) {
|
|
/* a critical, safe-to-copy chunk is an error */
|
|
printf("%s illegal critical, safe-to-copy chunk%s%s\n",
|
|
verbose? ":":fname, verbose? "":" ", verbose? "":chunkid);
|
|
set_err(kMajorError);
|
|
} else if (RESERVED(chunkid)) {
|
|
/* a chunk with the reserved bit set is an error (or spec updated) */
|
|
printf("%s illegal reserved-bit-set chunk%s%s\n",
|
|
verbose? ":":fname, verbose? "":" ", verbose? "":chunkid);
|
|
set_err(kMajorError);
|
|
} else if (PUBLIC(chunkid)) {
|
|
/* GRR 20050725: all registered (public) PNG/MNG/JNG chunks are now
|
|
* known to pngcheck, so any unknown public ones are invalid (or have
|
|
* been proposed and approved since the last release of pngcheck) */
|
|
printf("%s illegal (unless recently approved) unknown, public "
|
|
"chunk%s%s\n", verbose? ":":fname, verbose? "":" ",
|
|
verbose? "":chunkid);
|
|
set_err(kMajorError);
|
|
} else if (/* !PUBLIC(chunkid) && */ CRITICAL(chunkid) &&
|
|
!suppress_warnings)
|
|
{
|
|
/* GRR 20060617: as Chris Nokleberg noted, "private, critical chunks
|
|
* should not be used in publicly available software or files" (PNG
|
|
* spec) */
|
|
printf("%s private, critical chunk%s%s (warning)\n",
|
|
verbose? ":":fname, verbose? "":" ", verbose? "":chunkid);
|
|
set_err(kWarning); /* not an error if used only internally */
|
|
} else if (verbose) {
|
|
printf("\n unknown %s, %s, %s%ssafe-to-copy chunk\n",
|
|
PRIVATE(chunkid) ? "private":"public",
|
|
ANCILLARY(chunkid) ? "ancillary":"critical",
|
|
RESERVED(chunkid) ? "reserved-bit-set, ":"",
|
|
SAFECOPY(chunkid) ? "":"un");
|
|
}
|
|
last_is_IDAT = last_is_JDAT = 0;
|
|
}
|
|
|
|
/*=======================================================================*/
|
|
|
|
if (no_err(kMinorError)) {
|
|
if (fpOut != NULL) {
|
|
putlong(fpOut, sz);
|
|
(void)fwrite(chunkid, 1, 4, fpOut);
|
|
(void)fwrite(buffer, 1, toread, fpOut);
|
|
}
|
|
|
|
while (sz > toread) {
|
|
int data_read;
|
|
sz -= toread;
|
|
toread = (sz > BS)? BS:sz;
|
|
|
|
data_read = fread(buffer, 1, toread, fp);
|
|
if (fpOut != NULL)
|
|
(void)fwrite(buffer, 1, data_read, fpOut);
|
|
|
|
if (data_read != toread) {
|
|
printf("%s EOF while reading %s%sdata\n",
|
|
verbose? ":":fname, verbose? "":chunkid, verbose? "":" ");
|
|
set_err(kCriticalError);
|
|
return global_error;
|
|
}
|
|
|
|
crc = update_crc(crc, (uch *)buffer, toread);
|
|
}
|
|
|
|
filecrc = getlong(fp, fname, "CRC value");
|
|
|
|
if (is_err(kMajorError))
|
|
return global_error;
|
|
|
|
if (filecrc != CRCCOMPL(crc)) {
|
|
printf("%s CRC error in chunk %s (computed %08lx, expected %08lx)\n",
|
|
verbose? "":fname, chunkid, CRCCOMPL(crc), filecrc);
|
|
set_err(kMinorError);
|
|
}
|
|
|
|
if (no_err(kMinorError) && fpOut != NULL)
|
|
putlong(fpOut, CRCCOMPL(crc));
|
|
|
|
} else if (force) {
|
|
/* force may result in set_err(kMajorError) or more upstream, and failing
|
|
* to read CRC bytes here guarantees immediate downstream error when
|
|
* attempting to read length bytes and chunk type/name bytes */
|
|
filecrc = getlong(fp, fname, "CRC value");
|
|
}
|
|
|
|
if (global_error > kWarning && !force)
|
|
return global_error;
|
|
}
|
|
|
|
/*----------------------- END OF IMMENSE WHILE-LOOP -----------------------*/
|
|
|
|
if (no_err(kMinorError)) {
|
|
if (((png || jng) && !have_IEND) || (mng && !have_MEND)) {
|
|
printf("%s file doesn't end with a%sEND chunk\n", verbose? "":fname,
|
|
mng? " M":"n I");
|
|
set_err(kMinorError);
|
|
}
|
|
}
|
|
|
|
if (global_error > kWarning && quiet != 2)
|
|
return global_error;
|
|
|
|
/* GRR 19970621: print compression ratio based on file size vs. byte-packed
|
|
* raw data size. Arguably it might be fairer to compare against the size
|
|
* of the unadorned, compressed data, but since PNG is a package deal...
|
|
* GRR 19990619: disabled for MNG, at least until we figure out a reasonable
|
|
* way to calculate the ratio; also switched to MNG-relevant stats. */
|
|
|
|
/* if (global_error == 0) */ { /* GRR 20061202: always print a summary */
|
|
if (mng) {
|
|
if (verbose) { /* already printed MHDR/IHDR/JHDR info */
|
|
printf("%s in %s (%ld chunks).\n",
|
|
global_error? warnings_detected : no_errors_detected, fname,
|
|
num_chunks);
|
|
} else if (!quiet) {
|
|
printf("%s: %s%s%s (%ldx%ld, %ld chunks",
|
|
global_error? brief_warn : brief_OK,
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"",
|
|
mng_width, mng_height, num_chunks);
|
|
if (vlc == 1)
|
|
printf(", VLC");
|
|
else if (lc == 1)
|
|
printf(", LC");
|
|
if (layers && layers < 0x7ffffffL)
|
|
printf(", %lu layer%s", layers, (layers == 1L)? "" : "s");
|
|
if (frames && frames < 0x7ffffffL)
|
|
printf(", %lu frame%s", frames, (frames == 1L)? "" : "s");
|
|
printf(").\n");
|
|
}
|
|
|
|
} else if (jng) {
|
|
char *sgn = "";
|
|
int cfactor;
|
|
ulg ucsize;
|
|
|
|
if (!did_stat) {
|
|
stat(fname, &statbuf); /* already know file exists; don't check rc */
|
|
}
|
|
|
|
/* uncompressed size (bytes), compressed size => returns 10*ratio (%) */
|
|
if (bitdepth == 20)
|
|
ucsize = h*(w + ((w*12+7)>>3));
|
|
else
|
|
ucsize = h*((w*bitdepth+7)>>3);
|
|
if (alphadepth > 0)
|
|
ucsize += h*((w*alphadepth+7)>>3);
|
|
if ((cfactor = ratio(ucsize, statbuf.st_size)) < 0)
|
|
{
|
|
sgn = "-";
|
|
cfactor = -cfactor;
|
|
}
|
|
|
|
if (verbose) { /* already printed JHDR info */
|
|
printf("%s in %s (%ld chunks, %s%d.%d%% compression).\n",
|
|
global_error? warnings_detected : no_errors_detected, fname,
|
|
num_chunks, sgn, cfactor/10, cfactor%10);
|
|
} else if (!quiet) {
|
|
if (jtyp < 2)
|
|
printf("%s: %s%s%s (%ldx%ld, %d-bit %s%s%s, %s%d.%d%%).\n",
|
|
global_error? brief_warn : brief_OK,
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"",
|
|
w, h, jbitd, and, jng_type[jtyp],
|
|
lace? ", progressive":"", sgn, cfactor/10, cfactor%10);
|
|
else
|
|
printf("%s: %s%s%s (%ldx%ld, %d-bit %s%s + %d-bit alpha%s, %s%d.%d%%)"
|
|
".\n", global_error? brief_warn : brief_OK,
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"",
|
|
w, h, jbitd, and, jng_type[jtyp-2],
|
|
alphadepth, lace? ", progressive":"", sgn, cfactor/10, cfactor%10);
|
|
}
|
|
|
|
} else {
|
|
char *sgn = "";
|
|
int cfactor;
|
|
|
|
if (!did_stat)
|
|
stat(fname, &statbuf); /* already know file exists */
|
|
|
|
/* uncompressed size (bytes), compressed size => returns 10*ratio (%) */
|
|
if ((cfactor = ratio((ulg)(h*((w*bitdepth+7)>>3)), statbuf.st_size)) < 0)
|
|
{
|
|
sgn = "-";
|
|
cfactor = -cfactor;
|
|
}
|
|
|
|
if (verbose) { /* already printed IHDR/JHDR info */
|
|
printf("%s in %s (%ld chunks, %s%d.%d%% compression).\n",
|
|
global_error? warnings_detected : no_errors_detected, fname,
|
|
num_chunks, sgn, cfactor/10, cfactor%10);
|
|
} else if (!quiet) {
|
|
printf("%s: %s%s%s (%ldx%ld, %d-bit %s%s, %sinterlaced, %s%d.%d%%).\n",
|
|
global_error? brief_warn : brief_OK,
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"",
|
|
w, h, bitdepth, (ityp > 6)? png_type[1] : png_type[ityp],
|
|
(ityp == 3 && have_tRNS)? "+trns" : "",
|
|
lace? "" : "non-", sgn, cfactor/10, cfactor%10);
|
|
} else if (quiet == 2) {
|
|
/* The same information but more concise: */
|
|
printf("%s%s%s %ld %ld %d %s%s %sinterlaced %s%d.%d%%",
|
|
color? COLOR_YELLOW:"", fname, color? COLOR_NORMAL:"",
|
|
w, h, bitdepth, (ityp > 6)? png_type[1] : png_type[ityp],
|
|
(ityp == 3 && have_tRNS)? "+trns" : "",
|
|
lace? "" : "non-", sgn, cfactor/10, cfactor%10);
|
|
#ifdef USE_ZLIB
|
|
/* Record the end-of-line filter: */
|
|
filter_counts[last_filter][5]++;
|
|
{
|
|
int i;
|
|
for (i=0; i<7; ++i) {
|
|
int j;
|
|
for (j=0; j<7; ++j)
|
|
printf(" %lu", filter_counts[i][j]);
|
|
}
|
|
}
|
|
#endif
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return global_error;
|
|
|
|
} /* end function pngcheck() */
|
|
|
|
|
|
|
|
int pnginfile(FILE *fp, char *fname, int ipng, int extracting)
|
|
{
|
|
char name[1024], *szdot;
|
|
int err = kOK;
|
|
FILE *fpOut = NULL;
|
|
|
|
#if 1
|
|
strncpy(name, fname, 1024-20);
|
|
name[1024-20] = 0;
|
|
szdot = strrchr(name, '.');
|
|
if (szdot == NULL)
|
|
szdot = name + strlen(name);
|
|
sprintf(szdot, "-%d", ipng);
|
|
#else
|
|
/* Use this if filename length is restricted. */
|
|
sprintf(name, "PNG%d", ipng);
|
|
szdot = name;
|
|
#endif
|
|
|
|
if (extracting) {
|
|
szdot += strlen(szdot);
|
|
strcpy(szdot, ".png");
|
|
fpOut = fopen(name, "wb");
|
|
if (fpOut == NULL) {
|
|
perror(name);
|
|
fprintf(stderr, "%s: could not write output (ignored)\n", name);
|
|
} else if (verbose) {
|
|
printf("%s: contains %s PNG %d\n", name, fname, ipng);
|
|
}
|
|
(void)fwrite(good_PNG_magic, 8, 1, fpOut);
|
|
*szdot = 0;
|
|
}
|
|
|
|
err = pngcheck(fp, name, 1, fpOut);
|
|
|
|
if (fpOut != NULL) {
|
|
if (ferror(fpOut) != 0 || fclose(fpOut) != 0) {
|
|
perror(name); /* will only show most recent error */
|
|
fprintf(stderr, "%s: error on output (ignored)\n", name);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
void pngsearch(FILE *fp, char *fname, int extracting)
|
|
{
|
|
/* Go through the file looking for a PNG magic number; if one is
|
|
found, check the data to see if it is a PNG and validate the
|
|
contents. Useful when something puts a PNG in something else. */
|
|
int ch;
|
|
int ipng = 0;
|
|
|
|
if (verbose)
|
|
printf("Scanning: %s\n", fname);
|
|
else if (extracting)
|
|
printf("Extracting PNGs from %s\n", fname);
|
|
|
|
/* This works because the leading 137 code is not repeated in the
|
|
magic, so ANSI C says we will break out of the comparison as
|
|
soon as the partial match fails, and we can start a new test.
|
|
*/
|
|
do {
|
|
ch = getc(fp);
|
|
while (ch == good_PNG_magic[0]) {
|
|
if ((ch = getc(fp)) == good_PNG_magic[1] &&
|
|
(ch = getc(fp)) == good_PNG_magic[2] &&
|
|
(ch = getc(fp)) == good_PNG_magic[3] &&
|
|
(ch = getc(fp)) == good_PNG_magic[4] &&
|
|
(ch = getc(fp)) == good_PNG_magic[5] &&
|
|
(ch = getc(fp)) == good_PNG_magic[6] &&
|
|
(ch = getc(fp)) == good_PNG_magic[7])
|
|
{
|
|
/* just after a PNG header */
|
|
/* int error = */ pnginfile(fp, fname, ++ipng, extracting);
|
|
}
|
|
}
|
|
} while (ch != EOF);
|
|
}
|
|
|
|
|
|
|
|
/* PNG_subs
|
|
*
|
|
* Utility routines for PNG encoders and decoders
|
|
* by Glenn Randers-Pehrson
|
|
*
|
|
*/
|
|
|
|
/* check_magic()
|
|
*
|
|
* Check the magic numbers in 8-byte buffer at the beginning of
|
|
* a (possible) PNG or MNG or JNG file.
|
|
*
|
|
* by Alexander Lehmann, Glenn Randers-Pehrson and Greg Roelofs
|
|
*
|
|
* This is free software; you can redistribute it and/or modify it
|
|
* without any restrictions.
|
|
*
|
|
*/
|
|
int check_magic(uch *magic, char *fname, int which)
|
|
{
|
|
int i;
|
|
const uch *good_magic = (which == 0)? good_PNG_magic :
|
|
((which == 1)? good_MNG_magic : good_JNG_magic);
|
|
|
|
for (i = 1; i < 3; ++i)
|
|
{
|
|
if (magic[i] != good_magic[i]) {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
if (magic[0] != good_magic[0] ||
|
|
magic[4] != good_magic[4] || magic[5] != good_magic[5] ||
|
|
magic[6] != good_magic[6] || magic[7] != good_magic[7]) {
|
|
|
|
if (!verbose) {
|
|
printf("%s: CORRUPTED by text conversion\n", fname);
|
|
return 1;
|
|
}
|
|
|
|
printf(" File is CORRUPTED. It seems to have suffered ");
|
|
|
|
/* This coding derived from Alexander Lehmann's checkpng code */
|
|
if (strncmp((char *)&magic[4], "\012\032", 2) == 0)
|
|
printf("DOS->Unix");
|
|
else if (strncmp((char *)&magic[4], "\015\032", 2) == 0)
|
|
printf("DOS->Mac");
|
|
else if (strncmp((char *)&magic[4], "\015\015\032", 3) == 0)
|
|
printf("Unix->Mac");
|
|
else if (strncmp((char *)&magic[4], "\012\012\032", 3) == 0)
|
|
printf("Mac->Unix");
|
|
else if (strncmp((char *)&magic[4], "\012\012", 2) == 0)
|
|
printf("DOS->Unix");
|
|
else if (strncmp((char *)&magic[4], "\015\015\012\032", 4) == 0)
|
|
printf("Unix->DOS");
|
|
else if (strncmp((char *)&magic[4], "\015\012\032\015", 4) == 0)
|
|
printf("Unix->DOS");
|
|
else if (strncmp((char *)&magic[4], "\015\012\012", 3) == 0)
|
|
printf("DOS EOF");
|
|
else if (strncmp((char *)&magic[4], "\015\012\032\012", 4) != 0)
|
|
printf("EOL");
|
|
else
|
|
printf("an unknown");
|
|
|
|
printf(" conversion.\n");
|
|
|
|
if (magic[0] == 9)
|
|
printf(" It was probably transmitted through a 7-bit channel.\n");
|
|
else if (magic[0] != good_magic[0])
|
|
printf(" It was probably transmitted in text mode.\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* GRR 20061203: now EBCDIC-safe */
|
|
int check_chunk_name(char *chunk_name, char *fname)
|
|
{
|
|
if (isASCIIalpha((int)chunk_name[0]) && isASCIIalpha((int)chunk_name[1]) &&
|
|
isASCIIalpha((int)chunk_name[2]) && isASCIIalpha((int)chunk_name[3]))
|
|
return 0;
|
|
|
|
printf("%s%s invalid chunk name \"%.*s\" (%02x %02x %02x %02x)\n",
|
|
verbose? "":fname, verbose? "":":", 4, chunk_name,
|
|
chunk_name[0], chunk_name[1], chunk_name[2], chunk_name[3]);
|
|
set_err(kMajorError); /* usually means we've "jumped the tracks": bail! */
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/* GRR 20050724 */
|
|
/* caller must do set_err(kMinorError) based on return value (0 == OK) */
|
|
/* keyword_name is "keyword" for most chunks, but it can instead be "name" or
|
|
* "identifier" or whatever makes sense for the chunk in question */
|
|
int check_keyword(uch *buffer, int maxsize, int *pKeylen,
|
|
char *keyword_name, char *chunkid, char *fname)
|
|
{
|
|
int j, prev_space = 0;
|
|
int keylen = keywordlen(buffer, maxsize);
|
|
|
|
if (pKeylen)
|
|
*pKeylen = keylen;
|
|
|
|
if (keylen == 0) {
|
|
printf("%s zero length %s%s%s\n",
|
|
verbose? ":":fname, verbose? "":chunkid, verbose? "":" ", keyword_name);
|
|
return 1;
|
|
}
|
|
|
|
if (keylen > 79) {
|
|
printf("%s %s %s is longer than 79 characters\n",
|
|
verbose? ":":fname, verbose? "":chunkid, keyword_name);
|
|
return 2;
|
|
}
|
|
|
|
if (buffer[0] == ' ') {
|
|
printf("%s %s %s has leading space(s)\n",
|
|
verbose? ":":fname, verbose? "":chunkid, keyword_name);
|
|
return 3;
|
|
}
|
|
|
|
if (buffer[keylen - 1] == ' ') {
|
|
printf("%s %s %s has trailing space(s)\n",
|
|
verbose? ":":fname, verbose? "":chunkid, keyword_name);
|
|
return 4;
|
|
}
|
|
|
|
for (j = 0; j < keylen; ++j) {
|
|
if (buffer[j] == ' ') {
|
|
if (prev_space) {
|
|
printf("%s %s %s has consecutive spaces\n",
|
|
verbose? ":":fname, verbose? "":chunkid, keyword_name);
|
|
return 5;
|
|
}
|
|
prev_space = 1;
|
|
} else {
|
|
prev_space = 0;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < keylen; ++j) {
|
|
if (latin1_keyword_forbidden[buffer[j]]) { /* [0,31] || [127,160] */
|
|
printf("%s %s %s has control character(s) (%u)\n",
|
|
verbose? ":":fname, verbose? "":chunkid, keyword_name, buffer[j]);
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* GRR 20070707 */
|
|
/* caller must do set_err(kMinorError) based on return value (0 == OK) */
|
|
int check_text(uch *buffer, int maxsize, char *chunkid, char *fname)
|
|
{
|
|
int j, ctrlwarn = verbose? 1 : 0; /* print message once, only if verbose */
|
|
|
|
if (quiet != 2) for (j = 0; j < maxsize; ++j) {
|
|
if (buffer[j] == 0) {
|
|
printf("%s %s text contains NULL character(s)\n",
|
|
verbose? ":":fname, verbose? "":chunkid);
|
|
return 1;
|
|
} else if (ctrlwarn && latin1_text_discouraged[buffer[j]]) {
|
|
printf(": text has control character(s) (%u) (discouraged)\n",
|
|
buffer[j]);
|
|
ctrlwarn = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* GRR 20061203 (used only for sCAL) */
|
|
/* caller must do set_err(kMinorError) based on return value (0 == OK) */
|
|
int check_ascii_float(uch *buffer, int len, char *chunkid, char *fname)
|
|
{
|
|
uch *qq = buffer, *bufEnd = buffer + len;
|
|
int have_integer = 0, have_dot = 0, have_fraction = 0;
|
|
int have_E = 0, have_Esign = 0, have_exponent = 0, in_digits = 0;
|
|
int have_nonzero = 0;
|
|
int rc = 0;
|
|
|
|
for (qq = buffer; qq < bufEnd && !rc; ++qq) {
|
|
switch (*qq) {
|
|
case '+':
|
|
case '-':
|
|
if (qq == buffer) {
|
|
in_digits = 0;
|
|
} else if (have_E && !have_Esign) {
|
|
have_Esign = 1;
|
|
in_digits = 0;
|
|
} else {
|
|
printf("%s invalid sign character%s%s (buf[%td])\n",
|
|
verbose? ":":fname, verbose? "":" in ", verbose? "":chunkid,
|
|
qq-buffer); // ptrdiff_t
|
|
rc = 1;
|
|
}
|
|
break;
|
|
|
|
case '.':
|
|
if (!have_dot && !have_E) {
|
|
have_dot = 1;
|
|
in_digits = 0;
|
|
} else {
|
|
printf("%s invalid decimal point%s%s (buf[%td])\n",
|
|
verbose? ":":fname, verbose? "":" in ", verbose? "":chunkid,
|
|
qq-buffer); // ptrdiff_t
|
|
rc = 2;
|
|
}
|
|
break;
|
|
|
|
case 'e':
|
|
case 'E':
|
|
if (have_integer || have_fraction) {
|
|
have_E = 1;
|
|
in_digits = 0;
|
|
} else {
|
|
printf("%s invalid exponent before mantissa%s%s (buf[%td])\n",
|
|
verbose? ":":fname, verbose? "":" in ", verbose? "":chunkid,
|
|
qq-buffer); // ptrdiff_t
|
|
rc = 3;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (*qq < '0' || *qq > '9') {
|
|
printf("%s invalid character ('%c' = 0x%02x)%s%s\n",
|
|
verbose? ":":fname, *qq, *qq,
|
|
verbose? "":" in ", verbose? "":chunkid);
|
|
rc = 4;
|
|
} else if (in_digits) {
|
|
/* still in digits: do nothing except check for non-zero digits */
|
|
if (!have_exponent && *qq != '0')
|
|
have_nonzero = 1;
|
|
} else if (!have_integer && !have_dot) {
|
|
have_integer = 1;
|
|
in_digits = 1;
|
|
if (*qq != '0')
|
|
have_nonzero = 1;
|
|
} else if (have_dot && !have_fraction) {
|
|
have_fraction = 1;
|
|
in_digits = 1;
|
|
if (*qq != '0')
|
|
have_nonzero = 1;
|
|
} else if (have_E && !have_exponent) {
|
|
have_exponent = 1;
|
|
in_digits = 1;
|
|
} else {
|
|
/* is this case possible? */
|
|
printf("%s invalid digits%s%s (buf[%td])\n",
|
|
verbose? ":":fname, verbose? "":" in ", verbose? "":chunkid,
|
|
qq-buffer); // ptrdiff_t
|
|
rc = 5;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* must have either integer part or fractional part; all else is optional */
|
|
if (rc == 0 && !have_integer && !have_fraction) {
|
|
printf("%s missing mantissa%s%s\n",
|
|
verbose? ":":fname, verbose? "":" in ", verbose? "":chunkid);
|
|
rc = 6;
|
|
}
|
|
|
|
/* non-exponent part must be non-zero (=> must have seen a non-zero digit) */
|
|
if (rc == 0 && !have_nonzero) {
|
|
if (verbose)
|
|
printf(": invalid zero value(s)\n");
|
|
else
|
|
printf("%s invalid zero %s value(s)\n", fname, chunkid);
|
|
rc = 7;
|
|
}
|
|
|
|
return rc;
|
|
}
|