pngcheck-3.0.2/README0000644000175000017500000000767214005624530014075 0ustar roelofsroelofspngcheck version 3.0.2 of 31 January 2021 This version fixes one more (probable) security vulnerability discovered by Ben Beasley of the Fedora Linux project (who also supplied the fix last month, oops...). Again, our thanks! I've also updated the MinGW32 cross-compiler makefile for both Win32 and Win64 targets (i.e., there are two such makefiles now), but other than verifying that binaries come out the other end, these are COMPLETELY UNTESTED. In particular, the printf() format "%td" for ptrdiff_t (pointer differences, i.e., subtraction) doesn't appear to be supported under Windows, but I don't know what the appropriate type is. So the five affected print statements presumably just won't work correctly. Here's a list of the major enhancements since version 1.98, which was the last release before I took over maintenance: - zlib support (to test the compressed stream and, optionally, to print out the image's row filters) - support for all remaining known PNG chunks (conformance) - complete support for all known MNG and JNG chunks (informational) - extended support for printing palettes (includes transparency info and histograms) - optional color (text) output - improved error-checking - info on the compression factor of the image (expressed as a percentage, where 0% is no compression and 100% would be total compression; note that this can be negative since it counts PNG's chunk overhead against the compression factor) - png-fix-IDAT-windowsize utility - pngsplit utility - compilation support for Win32 (using MSVC), RISC OS, and Amiga Also, this "anti-enhancement" occurred in version 3.0.0 for security reasons: - -f ("force continuation after major errors") option REMOVED There are also many fixes, of course, including ones from Tom Lane, Glenn Randers-Pehrson, Tom Zerucha, Paul Matzke, Darren Salt, John Bowler, and others. Thanks also to Chris Nokleberg (brokensuite), Tim Pritlove, Bob Friesenhahn, the GraalOnline folks, giantbranch, Ben Beasley, and others for test images. See the included CHANGELOG file for the complete, detailed list of who did what. Note that while MNG support is now complete in the sense of covering all registered chunk types, there are still numerous error conditions that pngcheck won't detect, plus a few non-error conditions that it will flag erroneously. Some of those can and will be fixed (particularly the latter class), but many of them involve complex interactions between different chunk types and would require virtually a full MNG decoder engine, something that is unlikely ever to happen in pngcheck. In other words, consider pngcheck a handy MNG debugging tool but not a full validator. Use it in conjunction with the MNG specification and a libmng-based viewer for best results. (PNG support, on the other hand, is pretty solid.) Also use zlib 1.2.x for best results--older versions failed to detect a number of invalid deflate/zlib conditions, including out-of-range LZ77 distance codes. Originally I had hoped to add support for EBCDIC-based systems (and perhaps UTF-16 and UTF-32-based ones, if there are any for which "char" defaults to more than 8 bits), but there doesn't seem to be much point in that anymore. I'd still kind of like to extend the zlib support to include zTXt, iTXt, iCCP, etc., but given the pace of recent years ("nonexistent" would be fair), folks should definitely not hold their breath waiting for that. Similarly, the code could do a better job with chunks whose data exceed the buffer size, and in general, immense if-else blocks (e.g., > 3000 lines) are extremely nasty and should be rewritten, but...yeah. The gap between 2.3.0 and 2.4.0 (the two previous releases) was bigger than that between 2.3.0 and the creation of the PNG format itself. :-/ (Did we mention that PNG turned 25 in 2020?) But if there ever are additional updates, you might find them here: http://www.libpng.org/pub/png/apps/pngcheck.html Greg Roelofs http://gregroelofs.com/greg_contact.html pngcheck-3.0.2/LICENSE0000644000175000017500000000247013742703636014225 0ustar roelofsroelofs Copyright 1995-2020 by Alexander Lehmann , Andreas Dilger , Glenn Randers-Pehrson , Greg Roelofs , John Bowler , Tom Lane 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. [This license applies to pngcheck.c and its associated makefiles and documentation in the main directory. The files in the "gpl" subdirectory-- specifically, pngsplit.c and png-fix-IDAT-windowsize.c--are licensed under the GNU General Public License. The files in "amiga" subdirectory are Copyright 2003 Simon Goodwin and were contributed without an explicit license statement, but insofar as the "gpl" subdirectory didn't exist at the time, it's safe to assume their intended license was similar to pngcheck's, just with Simon's copyright replacing the one above.] pngcheck-3.0.2/CHANGELOG0000644000175000017500000003606714005624305014427 0ustar roelofsroelofs * ChangeLog (oldest first): * * Started by Alexander Lehmann and subsequently * extended by people as listed below. * * AL - Alexander Lehmann * AED - Andreas Dilger * GRP - Glenn Randers-Pehrson * GRR - Greg Roelofs * JB - John Bowler * TGL - Tom Lane * * 19950223 AL: fixed wrong magic numbers * ["version 1.1"] * --------------- * 19950313 AL: CRC code from PNG spec; compiles on memory-impaired PCs now; * check for IHDR/IEND chunks * ["version 1.2"] * --------------- * 19950325 GRP: rewrote magic-number-checking and moved it to PNG_check_magic() * ["version 1.3"] * --------------- * 19950327 AL: fixed CRC code for 64-bit; -t switch; unsigned char vs. char * pointer changes * ["version 1.4"] * --------------- * 19960601 AL: check for data after IEND chunk * ["version 1.5"] * --------------- * 19950601 GRR: reformatted; print tEXt and zTXt keywords; added usage * 19950601 GRR: released version 1.6, a.k.a. pngcheck-grr1.c * -------------------------------------------- * 19950607 GRR: print image info (IHDR) and tIME; enable EMX wildcards * 19950615 GRR: released version 1.61, a.k.a. pngcheck-grr2.c * --------------------------------------------- * 19950731 AL: check for control chars; check for MacBinary header; new * force option * 19950731 AL: released version 1.7 * -------------------- * 19950827 AL: merged Greg's 1.61 changes: print IHDR and tIME contents, * call emx wildcard function * 19950827 AL: released version 1.8 * -------------------- * 19950904 GRR: added brackets on keyword error messages to distinguish from * keyword text * 19950904 GRR: released version 1.81 * --------------------- * 19951121 AED: re-ordered internal chunk checking in pngcheck(). * Now decodes most of the known chunk types to check for invalid * contents (except IDAT and zTXt). Information is printed * about the contents of known chunks, and unknown chunks have * their chunk name flags decoded. Also checks chunk ordering. * 19951121 AED: released version 1.9 * -------------------- * 19951126 AED: minor bug fixes and nicening of the output. Checks for * valid cHRM contents per Chris Lilley's recommendation. * 19951126 AED: released version 1.91 * --------------------- * 19951204 AED: minor bug in cHRM error output fixed * 19960105 AED: added -q flaq to output a message only if an error occurs * 19960105 AED: released version 1.92 * --------------------- * 19960119 AED: added ability to parse multiple options with a single '-'; * changed tIME output to be in RFC 1123 format * 19960306 AED: released version 1.93 * --------------------- * 19960517 GRR: fixed obvious(?) fprintf error; fixed multiple-tIME error msg * 19960517 GRR: released version 1.94 * --------------------- * 19960521 AL: fixed two tRNS errors reported by someone from W3C (whose name * I currently don't remember) (complained about missing palette * in greyscale images, missing breaks in case statement); avoid * reference to undefined array entry with out of range ityp * 19960521 AL: released version 1.95 * --------------------- * 19960605 AED: removed extra linefeed from cHRM output when not verbose; * added test to see if sBIT contents are valid; * added test for zero width or height in IHDR; * added test for insufficient IDAT data (minimum 10 bytes) * 19960605 AED: released version 1.96 * --------------------- * 19960605 AED: added -p flag to dump the palette contents (decimal and hex); * cleaned up internals * 19960927 AED: released version 1.97 * --------------------- * 19961231 JB: add decoding of the zlib header from the first IDAT chunk (16- * bit header code in first two bytes, see print_zlibheader). * 19970102 GRR: more sensible zlib-header output (version "1.97grr"); nuked * some tabs; fixed blank lines between files in verbose output * 19970106 AED: initialize the command-line flags; add macros to ensure the * error level doesn't go down; return error level to calling * program; consolidate PNG magic on one place; add "-" as input * file for stdin; check for valid tEXt/zTXt keywords per PNG * Spec 1.0; slight modification to output of tEXt/zTXt keywords/ * contents; change 'extract' to output only valid chunks (unless * forced)--this may allow one to fix minor errors in a PNG file * 19970107 AED: released version 1.98 * --------------------- * 19970107 GRR: added USE_ZLIB compile option to print line filters (with -vv) * 19970110 GRR: fixed line-filters code for large-IDAT case * 19970621 GRR: added compression-ratio info * 19980609 TGL: fixed pHYs buglet * 19980609 GRR: re-integrated minimal MNG support from 97.01.21 branch * 19980610 GRR: extended MNG (MHDR info, DHDR, nEED, DEFI, FRAM, MEND) * 19980611 GRR: extended MNG (more FRAM info; LOOP, ENDL) * 19980612 GRR: extended MNG (FRAM, BACK, MOVE, CLON, SHOW, CLIP, fPRI, eXPI) * 19980616 GRR: extended MNG (PROM, SAVE, SEEK) * 19980702 GRR: fixed line-filters bug reported by Theodore Goodman (97.10.19); * updated SAVE for MNG Draft 43 * 19980711 GRR: added sPLT; extended printpal (-p) to support tRNS, hIST, sPLT * 19981021 GRR: added Win32 fix and compilation info; fixed mng=0, DEFI and * printpal bugs * 19981206 GRR: added "File: %s" for printpal; fixed some plural%s; fixed and * extended unknown-chunk info (separate line now); added dpi info * to pHYs and flagged unit types > 1 as error * 19981228 GRR: nuked old comments; added proto-copyright message * 19990201 GRR: changed control-character warning to "one or more" * 19990327 GRR: added option to indent printtext; changed non-verbose summary * to two lines; added tRNS info to summary * 19990613 GRR: fixed remaining "must precede IDAT" messages for MNG; updated * MHDR and LOOP for Draft 64 * 19990619 GRR: released version 1.99-grr1 * -------------------------- * 19990620 GRR: fixed Glenn's e-mail address and help-screen MNG-draft version; * disabled compression ratio for MNG; updated FRAM for Draft 64; * fixed "not enough IDAT data" IEND bug; renamed * PNG_MNG_check_magic() to check_magic(); renamed * PNG_check_chunk_name() to check_chunk_name(); * 19990713 GRP: fixed MNG global-PLTE case (zero-length allowed) * 19990718 GRR: shortened non-verbose summary so fits on one line again; added * BASI and PPLT * 19991114 GRR: added gIFg, gIFx and part of gIFt * 19991117 GRR: finished gIFt; added sRGB and iCCP * 19991117 GRR: released version 1.99.2 * ----------------------- * 19991122 GRR: fixed typo in gIFx application ID format (%.*s) * 19991215 GRR: added notes about wildcard expansion under Windows * 20000304 GRR: fixed indentation on -vt printing of zTXt chunk; fixed bogus * double-backslash bug in printtext(); added -vvvv data-dump * capability (very crude, not correct, ... needs much work) * 20000902 GRR: added JNG support; JHDR, JDAT, JSEP; fRAc, iTXt, pCAL, mkBF, * mkBS, mkBT, prVW * 20000902 GRR: released version 1.99.3 * ----------------------- * 20000904 GRR: updated usage screen to include JNG; changed dtime to double * to quiet an MSVC conversion warning * 20000905 GRR: quieted several IRIX gcc warnings * 20000927 GRR: fixed formatting error when both -v and -p options given; * incorporated Tom Zerucha 's patch to use zlib * CRC routines if USE_ZLIB defined * 20001127 GRR: fixed MNG "incorrect [tRNS] length for grayscale image" bug * 20010104 GRR: added support for profile bits 6-9, with minimal checking of * 10-15 and 16-30 * 20010116 GRR: incorported Glenn's USE_ZLIB interlacing bugfix of 20000727 * 20010127 GRR: enhanced summary lines for PNG/MNG/JNG; fixed MNG "sBIT must * precede PLTE" bug * 20011209 GRR: started adding PAST support (incomplete!); further work on * summary lines and error-detection? * 20020922 GRR: fixed verbose missing newlines for SEEK and empty SAVE chunks * 20040120 GRR: fixed missing error level for zlib_error (thanks to Gerrit * Hannaert for the bug report) and for "not enough IDAT data" * (thanks to Eduardo Morras for the test file) * 20050712 GRR: updated/fixed SAVE support according to final MNG spec * 20050716 GRR: updated/fixed BACK support according to final MNG spec; added * TERM support; added summary of errors if multiple files; * incorporated Paul Matzke's Mac fixes; incorporated Darren * Salt's RISC OS fixes; fixed bad-PNG-signature bug; fixed bKGD * index-out-of-range bug; fixed typos ("pHYS", IDAT vs. JDAT); * cleaned up code and output formatting * 20050717 GRR: enabled zlib checking independent of verbosity level; added * more stringent zlib windowBits test (from John Bowler) to * check for libpng 1.2.6 bug; added -w option to disable * windowBits test; fixed faulty zlib "error" detection * 20050717 GRR: released version 2.0.0 * ---------------------- * 20050723 GRR: added checks for duplicate SAVE and SEEK without SAVE; * finished PAST and added DISC support * 20050724 GRR: updated pHYs with aspect ratio; added pHYg, DROP, DBYK, ORDR, * and MAGN support; added check_keyword() function; fixed zlib- * initialization bug in MNG delta-PNGs * 20050725 GRR: added error-checks for unknown public chunks and chunks with * reserved bit set * 20050727 GRR: fixed -f bug (failure to read CRC bytes) * 20050801 GRR: added png-fix-IDAT-windowsize utility (GNU GPL) * 20050814 GRR: added pngsplit utility (GNU GPL) -- still beta! * 20060617 GRR: added checks for too-big chunk length, missing PLTE in type-3 * (palette) image, missing IDAT in PNG image, missing JDAT in * JNG image, and private/critical chunks (warning only) (thanks * to Chris Nokleberg for "brokensuite" test files); added * CRITICAL, RESERVED, etc., macros; changed slightly confusing * CRC error message ("actual/should be" -> "computed/expected"); * added sTER support * 20060617 GRR: released version 2.1.0 * ---------------------- * 20061202 GRR: always print a per-file summary (even if error(s)) * 20061203 GRR: added tests for all but two of the error conditions present * in Chris Nokleberg's brokensuite-20061201; added new warning * level (treat as OK if private test, error if public); started * reducing dependence on global variables; added final totals * for errors, warnings, and "OK" files; switched to logical * names for error levels; fixed some inconsistent error levels; * started removing unsafe ASCII assumptions; added checks for * (private) cmOD, cpIp chunks; filled in old (missing) version * info in CHANGELOG * 20061203 GRR: released version 2.2.0 * ---------------------- * 20070211 GRR: extended pngsplit file-signature recognizer to support MNG and * JNG (as claimed in help screen) * 20070704 GRR: cleaner fix for narrow interlaced images (account for missing * passes); added rows per pass for interlaced images; added * pass-separators in -vv row-filter output; added -c option for * ANSI colors (filenames, chunk names, row filters, error/OK); * alphabetized options on usage screen; made const string-arrays * const * 20070707 GRR: fixed 64-bit ptrdiff_t printf() issue (%td); added check for * NULLs (and "discouraged" chars) in tEXt; added check for zero * values in sCAL; fixed error cascade for private interlace * method; fixed non-verbose-mode IDAT/PLTE message * 20070707 GRR: released version 2.3.0 * ---------------------- * 20070709 GRR: tweaked color definitions slightly to work better on terminals * with white/light backgrounds * 20070712 GRR: added Makefile.mingw32 * 20100504 GRR: fixed DHDR (pre-MNG-1.0) bug identified by Winfried * 20170713 GRP: added eXIf support (GRR: added check for II/MM/unknown format) * 20201012 BB: converted static const help/usage-related strings to macros so * -Werror=format-security doesn't trigger (Ben Beasley) * 20201015 BB: added (help2man-generated) man pages for all three utils * 20201017 GRR: added top-level LICENSE file; fixed various compiler warnings * 20201031 GRR: replaced gpl/COPYING (outdated address, references to Library * GPL) with https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt * (thanks to Ben Beasley for catching that) * 20201031 GRR: released version 2.4.0 * ---------------------- * 20201113 BB: fixed buffer-overflow vulnerability discovered by "giantbranch * of NSFOCUS Security Team" * https://bugzilla.redhat.com/show_bug.cgi?id=1897485 * 20201128 BB: found and fixed four additional vulnerabilities (null-pointer * dereference and three buffer overruns) * 20201209 LP: fixed an off-by-one bug in check_magic() (Lucy Phipps) * 20201209 LL: converted two zlib-version warnings/errors to go to stderr * (Lemures Lemniscati, actually from 20180318; forwarded by LP) * 20201210 BB: fixed another buffer-overflow vulnerability discovered by * "giantbranch of NSFOCUS Security Team" * https://bugzilla.redhat.com/show_bug.cgi?id=1905775 * 20201212 GRR: removed -f ("force") option due to multiple security issues * 20201212 GRR: released version 3.0.0 * ---------------------- * 20201214 BB: generalized previous sPLT buffer-overrun fix, and found and * fixed a PPLT vulnerability * 20210124 GRR: released version 3.0.1 * ---------------------- * 20201217 BB: fixed a crash bug (and probable vulnerability) in large (MNG) * LOOP chunks * 20210131 GRR: updated Makefile.mingw32 for modern versions and added * Makefile.mingw64 (targets Win64); both are essentially * UNTESTED, however! * 20210131 GRR: released version 3.0.2 * ---------------------- pngcheck-3.0.2/Makefile.mingw320000644000175000017500000000372314005623132016130 0ustar roelofsroelofs# Sample makefile for pngcheck using mingw32-gcc (native or cross) and make. # This one is currently set up for Win32 cross-compilation from Linux. # # Greg Roelofs # Last modified: 31 January 2021 # # Invoke this makefile from a DOS-prompt window or xterm or whatever via: # # make -f Makefile.mingw32 # # This makefile assumes zlib has already been built or downloaded and is in # a subdirectory at the same level as the current subdirectory (as indicated # by the ZPATH macro below). Edit as appropriate. # # Note that the names of the dynamic and static zlib libraries used below may # change in later releases of the library. This makefile builds statically # linked executables, but that can be changed by uncommenting the appropriate # ZLIB line. # macros -------------------------------------------------------------------- #ZPATH = ../zlib ZPATH = ../zlib-1.2.11-win32 ZINC = -I$(ZPATH) #ZLIB = $(ZPATH)/libzdll.a # link dynamically against DLL ZLIB = $(ZPATH)/libz.a # link statically INCS = $(ZINC) LIBS = $(ZLIB) #CC = gcc CC = i686-w64-mingw32-gcc # Linux -> Win32 cross-compilation LD = $(CC) RM = rm -f CFLAGS = -O -Wall $(INCS) $(MINGW_CCFLAGS) -DUSE_ZLIB # [note that -Wall is a gcc-specific compilation flag ("most warnings on")] LDFLAGS = $(MINGW_LDFLAGS) O = .o E = .win32.exe PROG = pngcheck PROG2 = pngsplit PROG3 = png-fix-IDAT-windowsize EXES = $(PROG)$(E) $(PROG2)$(E) $(PROG3)$(E) # implicit make rules ------------------------------------------------------- .c$(O): $(CC) -c $(CFLAGS) $< # dependencies -------------------------------------------------------------- all: $(EXES) $(PROG)$(E): $(PROG).c $(CC) $(CFLAGS) -o $@ $(PROG).c $(LIBS) # both of these require zlib, too (for crc32() function) $(PROG2)$(E): gpl/$(PROG2).c $(CC) $(CFLAGS) -o $@ gpl/$(PROG2).c $(LIBS) $(PROG3)$(E): gpl/$(PROG3).c $(CC) $(CFLAGS) -o $@ gpl/$(PROG3).c $(LIBS) # maintenance --------------------------------------------------------------- clean: $(RM) $(EXES) pngcheck-3.0.2/Makefile.mingw640000644000175000017500000000372514005623353016144 0ustar roelofsroelofs# Sample makefile for pngcheck using mingw32-gcc (native or cross) and make. # This one is currently set up for Win64 cross-compilation from Linux. # # Greg Roelofs # Last modified: 31 January 2021 # # Invoke this makefile from a DOS-prompt window or xterm or whatever via: # # make -f Makefile.mingw64 # # This makefile assumes zlib has already been built or downloaded and is in # a subdirectory at the same level as the current subdirectory (as indicated # by the ZPATH macro below). Edit as appropriate. # # Note that the names of the dynamic and static zlib libraries used below may # change in later releases of the library. This makefile builds statically # linked executables, but that can be changed by uncommenting the appropriate # ZLIB line. # macros -------------------------------------------------------------------- #ZPATH = ../zlib ZPATH = ../zlib-1.2.11-win64 ZINC = -I$(ZPATH) #ZLIB = $(ZPATH)/libzdll.a # link dynamically against DLL ZLIB = $(ZPATH)/libz.a # link statically INCS = $(ZINC) LIBS = $(ZLIB) #CC = gcc CC = x86_64-w64-mingw32-gcc # Linux -> Win64 cross-compilation LD = $(CC) RM = rm -f CFLAGS = -O -Wall $(INCS) $(MINGW_CCFLAGS) -DUSE_ZLIB # [note that -Wall is a gcc-specific compilation flag ("most warnings on")] LDFLAGS = $(MINGW_LDFLAGS) O = .o E = .win64.exe PROG = pngcheck PROG2 = pngsplit PROG3 = png-fix-IDAT-windowsize EXES = $(PROG)$(E) $(PROG2)$(E) $(PROG3)$(E) # implicit make rules ------------------------------------------------------- .c$(O): $(CC) -c $(CFLAGS) $< # dependencies -------------------------------------------------------------- all: $(EXES) $(PROG)$(E): $(PROG).c $(CC) $(CFLAGS) -o $@ $(PROG).c $(LIBS) # both of these require zlib, too (for crc32() function) $(PROG2)$(E): gpl/$(PROG2).c $(CC) $(CFLAGS) -o $@ gpl/$(PROG2).c $(LIBS) $(PROG3)$(E): gpl/$(PROG3).c $(CC) $(CFLAGS) -o $@ gpl/$(PROG3).c $(LIBS) # maintenance --------------------------------------------------------------- clean: $(RM) $(EXES) pngcheck-3.0.2/Makefile.riscos0000644000175000017500000000104407713233610016144 0ustar roelofsroelofsCC = cc LD = link CFLAGS = -c -Otime -Ospace -fa -IC:,Zlib: -DUSE_ZLIB -DCHAR_IS_UNSIGNED -DENGLISH -throwback -depend !Depend LDFLAGS = LIBS = zlib:o.zlib C:o.gststubs # Static dependencies install: pngcheck squeeze -v pngcheck Boot:Library.pngcheck pngcheck: o.pngcheck $(LD) $(LDFLAGS) -o pngcheck o.pngcheck $(LIBS) o.pngcheck: c.pngcheck $(CC) $(CFLAGS) -o o.pngcheck c.pngcheck # Maintenance clean: remove pngcheck remove o.pngcheck # Dynamic dependencies: o.pngcheck: c.pngcheck o.pngcheck: Zlib:h.zlib o.pngcheck: Zlib:h.zconf pngcheck-3.0.2/Makefile.unx0000644000175000017500000000311313742707373015466 0ustar roelofsroelofs# gcc/gmake makefile for pngcheck. # Greg Roelofs # Last modified: 17 October 2020 # # Invoke via: # # make -f Makefile.unx # # This makefile assumes zlib has already been built or downloaded and is in # a subdirectory at the same level as the current subdirectory (as indicated # by the ZPATH macro below). Edit as appropriate. # # Note that the names of the dynamic and static zlib libraries used below may # change in later releases of the library. This makefile builds statically # linked executables, but that can be changed by uncommenting the appropriate # ZLIB lines. # macros -------------------------------------------------------------------- ZPATH = ../zlib ZINC = -I$(ZPATH) ZLIB = -L$(ZPATH) -lz #ZLIB = $(ZPATH)/libz.a INCS = $(ZINC) LIBS = $(ZLIB) CC = gcc LD = gcc RM = rm CFLAGS = -O -Wall $(INCS) -DUSE_ZLIB # [note that -Wall is a gcc-specific compilation flag ("all warnings on")] O = .o E = LDFLAGS = PROG = pngcheck PROG2 = pngsplit PROG3 = png-fix-IDAT-windowsize #OBJS = $(PROG)$(O) OBJS = EXES = $(PROG)$(E) $(PROG2)$(E) $(PROG3)$(E) # implicit make rules ------------------------------------------------------- .c$(O): $(CC) -c $(CFLAGS) $< # dependencies -------------------------------------------------------------- all: $(EXES) $(PROG)$(E): $(PROG).c $(CC) $(CFLAGS) -o $@ $(PROG).c $(LIBS) $(PROG2)$(E): gpl/$(PROG2).c $(CC) $(CFLAGS) -o $@ gpl/$(PROG2).c $(LIBS) $(PROG3)$(E): gpl/$(PROG3).c $(CC) $(CFLAGS) -o $@ gpl/$(PROG3).c $(LIBS) # maintenance --------------------------------------------------------------- clean: $(RM) -f $(EXES) $(OBJS) pngcheck-3.0.2/Makefile.w320000644000175000017500000000555410445104443015264 0ustar roelofsroelofs# MSVC/NMAKE makefile for pngcheck. # Greg Roelofs # Last modified: 17 June 2006 # # Invoke this makefile from a DOS prompt window via: # # %devstudio%\vc\bin\vcvars32.bat # nmake -nologo -f Makefile.w32 # # where %devstudio% is the installation directory for MSVC / DevStudio. If # you get "environment out of space" errors, create a desktop shortcut with # "c:\windows\command.com /e:4096" as the program command line and set the # working directory to this directory. Then double-click to open the new # DOS-prompt window with a bigger environment and retry the commands above. # # This makefile assumes zlib has already been built or downloaded and is in # a subdirectory at the same level as the current subdirectory (as indicated # by the ZPATH macro below). Edit as appropriate. # # Note that the names of the dynamic and static zlib libraries used below may # change in later releases of the library. This makefile builds statically # linked executables, but that can be changed by uncommenting the appropriate # ZLIB lines. !include # macros -------------------------------------------------------------------- ZPATH = ../zlib ZINC = -I$(ZPATH) #ZLIB = $(ZPATH)/zlibdll.lib ZLIB = $(ZPATH)/zlibstat.lib INCS = $(ZINC) LIBS = $(ZLIB) CC = cl LD = link RM = del CFLAGS = -nologo -O -W3 $(INCS) -DUSE_ZLIB $(cvars) # [note that -W3 is an MSVC-specific compilation flag ("all warnings on")] # [see %devstudio%\vc\include\win32.mak for cvars macro definition] O = .obj E = .exe LDFLAGS = -nologo PROG = pngcheck PROG2 = pngsplit PROG3 = png-fix-IDAT-windowsize OBJS = $(PROG)$(O) OBJS2 = $(PROG2)$(O) OBJS3 = $(PROG3)$(O) EXES = $(PROG)$(E) $(PROG2)$(E) $(PROG3)$(E) # implicit make rules ------------------------------------------------------- # GRR 20060617: Can't remember if this dumps objects in current dir or in # same dir as source. If latter, will need to modify PROG2/PROG3 targets # below (and/or OBJS* macros above) to include "gpl/" prefix on objects. .c$(O): $(CC) -c $(CFLAGS) $< # dependencies -------------------------------------------------------------- all: $(EXES) # setargv.obj expands wildcards and is included as part of MSVC; may be called # "wildargs.obj" (or similar) for Borland or Watcom compilers $(PROG)$(E): $(OBJS) $(LD) $(LDFLAGS) -out:$@ $(OBJS) setargv.obj $(LIBS) $(PROG2)$(E): $(OBJS2) $(CC) $(CFLAGS) -out:$@ $(OBJS2) setargv.obj $(LIBS) $(PROG3)$(E): $(OBJS3) $(CC) $(CFLAGS) -out:$@ $(OBJS3) setargv.obj $(LIBS) $(PROG)$(O): gpl/$(PROG).c $(PROG2)$(O): gpl/$(PROG2).c $(PROG3)$(O): gpl/$(PROG3).c # maintenance --------------------------------------------------------------- clean: # ideally we could just do this: # $(RM) $(EXES) $(OBJS) [etc.] # ...but the Windows "DEL" command is none too bright, so: $(RM) $(PROG)$(E) $(RM) $(PROG)$(O) $(RM) $(PROG2)$(E) $(RM) $(PROG2)$(O) $(RM) $(PROG3)$(E) $(RM) $(PROG3)$(O) pngcheck-3.0.2/pngcheck.c0000644000175000017500000054332314005613305015137 0ustar roelofsroelofs/* * 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 * ChangeLog: see CHANGELOG file */ /*============================================================================ * * Copyright 1995-2021 by Alexander Lehmann , * Andreas Dilger , * Glenn Randers-Pehrson , * Greg Roelofs , * John Bowler , * Tom Lane * * 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 "3.0.2 of 31 January 2021" /* * 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 eXIf fRAc gAMA gIFg gIFt gIFx // ancillary PNG chunks * hIST iCCP iTXt oFFs pCAL pHYs sBIT sCAL * sPLT sRGB sTER 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 * - REFACTOR THE WHOLE THING! split out each chunk's code into a separate * XXXX() function (e.g., IDAT(), tRNS()) * * with USE_ZLIB, print zTXt and compressed iTXt chunks if -t option * (break out zlib decoder into separate function and reuse) * (also iCCP?) * ? 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 -Wall -O -o pngcheck pngcheck.c * with zlib support (recommended): * gcc -Wall -O -DUSE_ZLIB -o pngcheck pngcheck.c -lz * or (if zlib lives in non-standard location): * gcc -Wall -O -DUSE_ZLIB -I/zlibpath -o pngcheck pngcheck.c -L/zlibpath -lz * or (static zlib): * gcc -Wall -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 #include #include #include #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 # if defined(__MWERKS__) && defined(macintosh) /* pxm for CodeWarrior */ # include # include # elif defined(applec) || defined(THINK_C) /* via Mark Fleming; not tested */ # include # include # else # include # include # endif #endif #if defined(unix) || (defined(__MWERKS__) && defined(macintosh)) /* pxm */ # include /* isatty() */ #endif #ifdef WIN32 # include #endif #ifdef USE_ZLIB # include #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); char const * u2name_helper(unsigned int value, const char **names, size_t nnames); #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) /* Map unsigned value to enumerated string name, safely with fallback */ #define U2NAME(x, names) (u2name_helper(x, &names[0], \ sizeof(names) / sizeof(names[0]))) #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)) #define no_err(x) (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 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 'h': usage(stdout); return err; case 'p': printpal = 1; ++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 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]) { fprintf(stderr, "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) { fprintf(stderr, "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 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 [-7cpqtv] file.{png|jng|mng} [file2.{png|jng|mng} [...]]\n" " or: ... | pngcheck [-7cpqstvx]\n" " or: pngcheck [-7cpqstvx] 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" " -p print contents of PLTE, tRNS, hIST, sPLT and PPLT (can be used with -q)\n" " -q test quietly (output only errors)\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 (a 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) { if (size < 1) return; if (indent) printf(" "); while (size--) { uch c; c = *buf++; if ((c < ' ' && c != '\t' && c != '\n') || (sevenbit? c > 127 : (c >= 127 && c < 160))) printf("\\%02X", c); /* else if (c == '\\') printf("\\\\"); */ else putchar(c); if (c < 32 || (c >= 127 && c < 160)) { if (c == '\n') { prbuf->lf = 1; if (indent && size > 0) printf(" "); } 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; } 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_DHDR = 0, */ 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_eXIf = 0, have_gAMA = 0, have_hIST = 0; int have_iCCP = 0, have_oFFs = 0, have_pCAL = 0, have_pHYs = 0, have_sBIT = 0; int have_sCAL = 0, 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; int bitdepth = 0, sampledepth = 0, ityp = 1, jtyp = 0, lace = 0, nplte = 0; int jbitd = 0, alphadepth = 0; int did_stat = 0; 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; 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); return global_error; } sz = getlong(fp, fname, "chunk length"); if (is_err(kMajorError)) 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); 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); 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); 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, U2NAME(ityp, png_type)); 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, U2NAME(ityp, png_type)); 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, U2NAME(ityp, png_type), 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, U2NAME(jtyp, jng_type)); 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, U2NAME(jtyp, jng_type), lace? ", progressive":""); else printf("\n %ld x %ld image, %d-bit %s%s + %d-bit alpha%s\n", w, h, jbitd, and, U2NAME(jtyp-2, jng_type), 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, U2NAME(ityp, png_type)); 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 ", U2NAME(ityp, png_type)); set_err(kMajorError); } else if (verbose) printf("\n"); if (!no_err(kMinorError)) 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)), U2NAME(FLEVEL, deflate_type), FDICT? ", preset dictionary":""); } else { printf("deflated, %d-byte window, %s compression%s\n", (1 << (CINFO+8)), U2NAME(FLEVEL, deflate_type), 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; /* 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 > 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("\nEOF while reading %s data\n", 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 > 0.8 || 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 > 0.8 || ry < 0 || ry > 0.8 || 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 > 0.8 || gy < 0 || gy > 0.8 || 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 > 0.8 || by < 0 || by > 0.8 || 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; /*------* | eXIf | *------*/ } else if (strcmp(chunkid, "eXIf") == 0) { if (jng) { printf("%s eXIf not defined in JNG\n", verbose? ":":fname); set_err(kMinorError); } else if (png && have_eXIf) { printf("%s multiple eXIf not allowed\n", verbose? ":":fname); set_err(kMinorError); } else if (verbose /* && no_err(kMinorError) */) { if (SH(buffer) == 0x4d4d && buffer[2] == 0 && buffer[3] == 0x2a) { printf(": EXIF metadata, big-endian (MM) format\n"); } else if (SH(buffer) == 0x4949 && buffer[2] == 0x2a && buffer[3] == 0) { printf(": EXIF metadata, little-endian (II) format\n"); } else { printf(": EXIF metadata, unrecognized format: 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0], buffer[1], buffer[2], buffer[3]); } } have_eXIf = 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); } // FIXME? probably need to distinguish from minor errors in this chunk // (no need for new line) and those in previous chunks (need newline for // verbose mode, and no real harm in printing gAMA info, too); likely // applies to many other chunks as well, but need to create an appropriate // test PNG to verify 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 (sz > BS) { printf("%s invalid %slength\n", /* or input buffer too small */ verbose? ":":fname, verbose? "":"hIST "); set_err(kMinorError); } else 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) { 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, taglen = 0; 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); } if (no_err(kMinorError)) { taglen = keywordlen(buffer+keylen+3, toread-keylen-3); 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 UTF-8 text\n", sz - (keylen+3+taglen+1)); else printf("\n %ld bytes of translated keyword and UTF-8 text\n", sz - (keylen+3+taglen)); } else if (printtext) { if (buffer[keylen+3+taglen+1] == 0) printf(" (no translated keyword, %ld bytes of UTF-8 text)\n", sz - (keylen+3+taglen+1)); else printf(" (%ld bytes of translated keyword and UTF-8 text)\n", sz - (keylen+3+taglen)); } } 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 (sz > BS) { /* FIXME: large sCAL chunks are unusual, but should be supported */ printf("%s checking large %schunk not currently supported\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)) { if (pPixheight == NULL) { /* missing pixel height, but -f was given */ pPixheight = buffer+sz; } 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 != buffer+sz && *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, jstep = ((bytes == 1) ? 6 : 10); if (nsplt < 10) spc = " "; else if (nsplt < 100) spc = " "; else if (nsplt < 1000) spc = " "; else if (nsplt < 10000) spc = " "; else spc = " "; /* TODO: Support larger sPLT contents with an input-reading loop */ if (nsplt > (BS - j) / jstep) { printf("%s printing truncated %scontents\n", verbose? ":":fname, verbose? "":"sPLT "); nsplt = (BS - j) / jstep; } /* 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 += jstep) 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 += jstep) 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", U2NAME(buffer[0], rendering_intent)); } 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; 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); } /* FIXME: add support for checking zlib header bytes of zTXt (and iTXt, iCCP, etc.) */ } else if (check_text(buffer + keylen + 1, toread - keylen - 1, chunkid, fname)) { set_err(kMinorError); } 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); else { printf("%s(compressed %s text)", verbose? " " : "", chunkid); /* FIXME: add support for decompressing/printing zTXt */ } /* 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"); } 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 ", U2NAME(ityp, png_type)); 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 ", U2NAME(ityp, png_type)); 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 ", U2NAME(ityp, png_type)); 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 ", U2NAME(ityp, png_type)); 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); 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", U2NAME(dtype, delta_type)); 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)); } } } } //have_DHDR = 1; 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 (sz > BS) { /* FIXME: large FRAM chunks are unusual, but should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"FRAM "); set_err(kMinorError); } else if (verbose) { uch fmode = buffer[0]; printf(": mode %d\n %s\n", fmode, U2NAME(fmode, framing_mode)); 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", U2NAME(cid, change_interframe_delay)); /* 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", U2NAME(ctt, change_timeout_and_termination)); /* 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", U2NAME(cscb, change_subframe_clipping_boundaries)); /* 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", U2NAME(csil, change_sync_id_list)); 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 > BS) { printf("%s invalid %slength\n", /* or input buffer too small */ verbose? ":":fname, verbose? "":"FRAM "); set_err(kMinorError); } else if (sz > BS) { /* FIXME: large SAVE chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"SAVE "); 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", U2NAME(type, entry_type)); ++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) { if (sz >= BS) sz = BS-1; buffer[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 > BS && verbose) { /* FIXME: large nEED chunks are unusual, but should be supported */ printf(": printing large chunk not currently supported\n"); } 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", U2NAME(ct, clone_type), U2NAME(dns, do_not_show), 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", U2NAME(smode, show_mode)); } 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); } else if (sz > BS) { /* FIXME: large LOOP chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"LOOP "); set_err(kMinorError); } else 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] : U2NAME(buffer[5] & 0x3, termination_condition)); /* 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, U2NAME(ityp, png_type)); 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, U2NAME(ityp, png_type)); 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]:U2NAME(ityp, png_type), 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 || sz > BS) { 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]; int base = 3; int bytes_left = sz-3; int samples, npplt = 0, nblks = 0; if (!verbose && printpal && !quiet) printf(" PPLT chunk"); if (verbose) printf(": %s\n", U2NAME(dtype, pplt_delta_type)); 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, base += samples) { if (sz - samples < base) { printf("%s implied sample outside %schunk bounds\n", verbose? ":":fname, verbose? "":"PPLT "); set_err(kMinorError); /* break out of outer loop, and suppress additional length error */ bytes_left = 0; break; } ++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, buffer[base + 0], buffer[base + 1], buffer[base + 2], buffer[base + 3], plus, buffer[base + 0], buffer[base + 1], buffer[base + 2], buffer[base + 3]); else if (samples == 3) printf(" %3d: %s(%3d,%3d,%3d) = %s(0x%02x,0x%02x,0x%02x)\n", i, plus, buffer[base + 0], buffer[base + 1], buffer[base + 2], plus, buffer[base + 0], buffer[base + 1], buffer[base + 2]); else printf(" %3d: %s(%3d) = %s(0x%02x)\n", i, plus, buffer[base], plus, buffer[base]); } } if (bytes_left > 2) { first_idx = buffer[base + 0]; last_idx = buffer[base + 1]; base += 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 (sz > BS) { /* FIXME: large PAST chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"PAST "); set_err(kMinorError); } 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) && sz <= BS) { 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, U2NAME(comp_mode, composition_mode)); printf(" orientation = %s,\n", U2NAME(orient >> 1, orientation)); 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", U2NAME(buffer[0] /* & 3 */, termination_action)); if (sz >= 10) { ulg val = LG(buffer+2); printf(" action after iterations = %s\n", U2NAME(buffer[1], termination_action)); 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); } else if (sz > BS) { /* FIXME: large DISC chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"DISC "); set_err(kMinorError); } if (verbose && no_err(kMinorError) && sz <= BS) { 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); } else if (sz > BS) { /* FIXME: large DROP chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"DROP "); set_err(kMinorError); } if (no_err(kMinorError) && sz <= BS) { 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 (sz > BS) { /* FIXME: large DBYK chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"DBYK "); set_err(kMinorError); } else 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 (sz > BS) { /* FIXME: large ORDR chunks should be supported */ printf("%s checking large %schunk not currently supported\n", verbose? ":":fname, verbose? "":"ORDR "); set_err(kMinorError); } else 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, U2NAME(buf[4], order_type)); 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", U2NAME(xmeth, magnification_method)); else printf(" X method = %s\n Y method = %s\n", U2NAME(xmeth, magnification_method), U2NAME(ymeth, magnification_method)); 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)); } if (global_error > kWarning) 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) 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, U2NAME(jtyp, jng_type), 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, U2NAME(jtyp-2, jng_type), 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] : U2NAME(ityp, png_type), (ityp == 3 && have_tRNS)? "+trns" : "", lace? "" : "non-", sgn, cfactor/10, cfactor%10); } } } 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)(uch)chunk_name[0]) && isASCIIalpha((int)(uch)chunk_name[1]) && isASCIIalpha((int)(uch)chunk_name[2]) && isASCIIalpha((int)(uch)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 */ 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_sign = 0, */ 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) { //have_sign = 1; 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; } char const * u2name_helper(unsigned int value, const char **names, size_t nnames) { return (value < nnames) ? names[value] : inv; } pngcheck-3.0.2/pngcheck.10000644000175000017500000000243214005612546015052 0ustar roelofsroelofs.TH PNGCHECK "1" "January 2021" "pngcheck 3.0.2" "User Commands" .SH NAME pngcheck \- manual page for pngcheck 3.0.2 .SH SYNOPSIS .B pngcheck .RI [ \-7cpqtv ] .I file.{png|jng|mng} .RI [ file2.{png|jng|mng} \ [...]] .br .RB ...\ |\ pngcheck .RI [ \-7cpqstvx ] .br .B pngcheck .RI [ \-7cpqstvx ] \ file-containing-PNGs ... .SH DESCRIPTION PNGcheck, version 3.0.2 of 31 January 2021, by Alexander Lehmann, Andreas Dilger and Greg Roelofs. .PP Test PNG, JNG or MNG image files for corruption, and print size/type info. .SH OPTIONS .TP .B \-7 print contents of tEXt chunks, escape chars >=128 (for 7\-bit terminals) .TP .B \-c colorize output (for ANSI terminals) .TP .B \-p print contents of PLTE, tRNS, hIST, sPLT and PPLT (can be used with .BR \-q ) .TP .B \-q test quietly (output only errors) .TP .B \-s search for PNGs within another file .TP .B \-t print contents of tEXt chunks (can be used with .BR \-q ) .TP .B \-v test verbosely (print most chunk data) .TP .B \-vv test very verbosely (decode & print line filters) .TP .B \-w suppress windowBits test (more\-stringent compression check) .TP .B \-x search for PNGs within another file and extract them when found .SH NOTE MNG support is more informational than conformance\-oriented. .SH "SEE ALSO" .BR pngsplit (1), .BR png-fix-IDAT-windowsize (1) pngcheck-3.0.2/amiga/0000755000175000017500000000000010644104556014265 5ustar roelofsroelofspngcheck-3.0.2/amiga/scoptions0000644000175000017500000000010607712120556016226 0ustar roelofsroelofsCPU=68000 DATA=FAR CODE=FAR OPT NOSTACKCHECK LINK library=lib:scm.lib pngcheck-3.0.2/amiga/README.Amiga0000644000175000017500000000631607712271270016170 0ustar roelofsroelofs[This is the original Amiga README for pngcheck 1.99.3, lightly edited to remove version-specific and/or redundant info.] Amiga port of pngcheck compiled by Simon N Goodwin, July 2003 ------------------------------------------------------------- PNG is a Portable Network Graphics files format designed to address the flaws of GIF. It has better compression, support for images with more than 256 colours, and no licence fees. This utility checks the integrity of a PNG file and optionally extracts information from it such as the colour format, palette contents, and embedded text identification strings. It was compiled with SAS C for all Amigas at the request of the maintainer Greg Roelofs, whose readme for the source follows. I volunteered for this after spotting that the link to Aminet on the pngcheck home page was broken, and sending the correction to Greg. He pointed out that pngcheck has since been updated (the latest source is about three times the size of that for the old Aminet release) and asked me to compile the latest one for Amiga users. Simon Goodwin, ami@studio.co.uk Limitations of the Amiga port ----------------------------- The optional ZLIB capability is not implemented on the Amiga version. Wildcards are not supported (as they are implemented by the shell on Unix/Linux, but not in AmigaDOS) but can be simulated with the Amiga Shell LIST .. LFORMAT facility, e.g: list cbig:Graphix/#?.png lformat="pngcheck %P%N" >ram:temp type ram:temp pngcheck cbig:Graphix/ZX80A.PNG pngcheck cbig:Graphix/ZX80A16.PNG pngcheck cbig:Graphix/ZX80A32.PNG pngcheck cbig:Graphix/ZX80A8.PNG pngcheck cbig:Graphix/ZX80grey.PNG execute ram:temp OK: cbig:Graphix/ZX80A.PNG (656x480, 8-bit colormap, non-interlaced, 41.5%). OK: cbig:Graphix/ZX80A16.PNG (656x480, 4-bit colormap, non-interlaced, 75.3%). OK: cbig:Graphix/ZX80A32.PNG (656x480, 8-bit colormap, non-interlaced, 81.1%). OK: cbig:Graphix/ZX80A8.PNG (656x480, 4-bit colormap, non-interlaced, 72.5%). OK: cbig:Graphix/ZX80grey.PNG (656x480, 8-bit colormap, non-interlaced, 41.3%). A Amiga shell script, pngcheck_all, is included to simplify this for you: pngcheck_all cbig:Graphix/ZX80A#?.png OK: cbig:Graphix/ZX80A.PNG (656x480, 8-bit colormap, non-interlaced, 41.5%). OK: cbig:Graphix/ZX80A16.PNG (656x480, 4-bit colormap, non-interlaced, 75.3%). OK: cbig:Graphix/ZX80A32.PNG (656x480, 8-bit colormap, non-interlaced, 81.1%). OK: cbig:Graphix/ZX80A8.PNG (656x480, 4-bit colormap, non-interlaced, 72.5%). Note: optional parameters (-t, -v etc) are not yet supported by this script. Compilation notes ----------------- The source was compiled without alteration using this AmigaDOS command: sc data=far code=far opt lib lib:scm.lib pngcheck.c If you have the supplied scoptions file in the current directory with the source you can simplify this to: sc pngcheck.c The compiled version uses generic 68000 code. This makes it 516 bytes longer and marginally slower on some CPUs than a 32 bit optimised version, but means it is suitable for any Amiga. The 68020 optimised version is available on request from Simon Goodwin, ami@studio.co.uk If you have a suitable compiler (SAS was used for 1.99, and 1.97 was compiled with GCC) you can easily build your own. pngcheck-3.0.2/amiga/pngcheck_all0000644000175000017500000000027507712271460016627 0ustar roelofsroelofs.key files/a .bra [ .ket ] ; Script to pngcheck files selected by wildcard, SNG July 2003 list [files] lformat="pngcheck %P%N" >ram:png_temp execute ram:png_temp delete ram:png_temp >nil: pngcheck-3.0.2/gpl/COPYING0000644000175000017500000004325411352247755015042 0ustar roelofsroelofs GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. pngcheck-3.0.2/gpl/png-fix-IDAT-windowsize.10000644000175000017500000000136213747332432020347 0ustar roelofsroelofs.TH PNG-FIX-IDAT-WINDOWSIZE "1" "October 2020" "png-fix-IDAT-windowsize 1.0" "User Commands" .SH NAME png-fix-IDAT-windowsize \- manual page for png-fix-IDAT-windowsize 1.0 .SH SYNOPSIS .B png\-fix\-IDAT\-windowsize .RI [ options ] .I pngfile .RI [ pngfile \ ...] .SH DESCRIPTION png\-fix\-IDAT\-windowsize, version 1.0 of 31 October 2020, by Greg Roelofs. .PP Uses explicit, hardcoded compression settings and line filters, writing result to output file with extension "\-fixed.png". .PP This software is licensed under the GNU General Public License. There is NO warranty. .SH OPTIONS .TP .B \-force overwrite existing output files .TP .B \-verbose print more status messages (synonym: .BR \-noquiet ) .SH "SEE ALSO" .BR pngcheck (1), .BR pngsplit (1) pngcheck-3.0.2/gpl/png-fix-IDAT-windowsize.c0000644000175000017500000004610213747332405020432 0ustar roelofsroelofs/* png-fix-IDAT-windowsize.c - simple utility to reset first IDAT's zlib ** window-size bytes and fix up CRC to match ** ** Downloads: ** ** http://gregroelofs.com/greg_software.html ** ** To compile: ** ** gcc -Wall -O2 -I/path-to-zlib png-fix-IDAT-windowsize.c \ -o png-fix-IDAT-windowsize -L/path-to-zlib -lz ** ** ** Copyright 2005-2020 Greg Roelofs ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define VERSION "1.0 of 31 October 2020" /* * TO DO: * - output dir (required) instead of "-fixed.png" name (=> strip existing * path components) * - same timestamp * - summary of how many done (and maybe also "fixing ..." msg) * - own crc32() function => no zlib dependency */ #include #include #include #include #include // for crc32() only typedef unsigned long ulg; typedef unsigned short ush; typedef unsigned char uch; #define PNG_FIX_IDAT_WINDOWSIZE_USAGE "\ usage: png-fix-IDAT-windowsize [options] pngfile [pngfile ...]\n\ options:\n\ -force overwrite existing output files\n\ -verbose print more status messages (synonym: -noquiet)\n\n\ Uses explicit, hardcoded compression settings and line filters, writing\n\ result to output file with extension \"-fixed.png\".\n" #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define FNMAX 1024 /* max filename length */ #define BUFSZ 4096 #define MIN_PNG_SIZE (8 + 4+4+13+4 + 4+4+10+4 + 4+4+4) // 67 (IDAT = 10: empir.) #define U16(x) ( ((ush)(((uch *)(x))[0]) << 8) | \ ((ush)(((uch *)(x))[1]) ) ) #define U32(x) ( ((ulg)(((uch *)(x))[0]) << 24) | \ ((ulg)(((uch *)(x))[1]) << 16) | \ ((ulg)(((uch *)(x))[2]) << 8) | \ ((ulg)(((uch *)(x))[3]) ) ) uch pngsig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; ulg IHDR = ( (73L << 24) | (72L << 16) | (68L << 8) | (82L) ); // 0x49484452 ulg IDAT = ( (73L << 24) | (68L << 16) | (65L << 8) | (84L) ); // 0x49444154 static int png_fix_IDAT_windowsize (char *filename, int force, int verbose); static char *chunkstr (ulg typ); int main(int argc, char *argv[]) { char *filename; int argn; int force = FALSE; int verbose = 0; int latest_error=0, error_count=0, file_count=0; #ifdef __EMX__ _wildcard(&argc, &argv); /* Unix-like globbing for OS/2 and DOS */ #endif #ifdef GRR_MALLOC_DEBUG mtrace(); #endif argn = 1; while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { if ( 0 == strncmp( argv[argn], "-force", 2 ) ) force = TRUE; else if ( 0 == strncmp( argv[argn], "-noforce", 4 ) ) force = FALSE; else if ( 0 == strncmp( argv[argn], "-verbose", 2 ) || 0 == strncmp( argv[argn], "-noquiet", 4 ) ) ++verbose; else if ( 0 == strncmp( argv[argn], "-noverbose", 4 ) || 0 == strncmp( argv[argn], "-quiet", 2 ) ) verbose = 0; else { fprintf(stderr, "png-fix-IDAT-windowsize, version %s, by Greg Roelofs.\n", VERSION); fprintf(stderr, " This software is licensed under the GNU " "General Public License.\n There is NO warranty.\n"); fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", ZLIB_VERSION, zlib_version); fprintf(stderr, "\n"); fprintf(stderr, PNG_FIX_IDAT_WINDOWSIZE_USAGE); fflush(stderr); return 1; } ++argn; } printf("png-fix-IDAT-windowsize, version %s, by Greg Roelofs.\n", VERSION); printf(" This software is licensed under the GNU " "General Public License.\n There is NO warranty.\n"); printf(" Compiled with zlib %s; using zlib %s.\n", ZLIB_VERSION, zlib_version); printf("\n"); fflush(stdout); if ( argn == argc ) { fprintf(stderr, PNG_FIX_IDAT_WINDOWSIZE_USAGE); fflush(stderr); return 5; } else { filename = argv[argn]; ++argn; } /*============================= MAIN LOOP =============================*/ while (argn <= argc) { int retval; if (verbose) { printf("%s:\n", filename); fflush(stdout); } retval = png_fix_IDAT_windowsize(filename, force, verbose); if (retval) { latest_error = retval; ++error_count; } ++file_count; if (verbose) { printf("\n"); fflush(stdout); } filename = argv[argn]; ++argn; } /*=======================================================================*/ if (verbose) { if (error_count) printf("There were errors fixing %d PNG file%s out of a" " total of %d file%s.\n", error_count, (error_count == 1)? "" : "s", file_count, (file_count == 1)? "" : "s"); else printf("No errors detected while fixing %d PNG image%s.\n", file_count, (file_count == 1)? "" : "s"); fflush(stdout); } return latest_error; } static int png_fix_IDAT_windowsize(char *filename, int force, int verbose) { FILE *infile, *outfile; uch inbuf[BUFSZ], *inptr, *endptr; uch *cbuf=NULL, *cptr; char outname[FNMAX]; static char *colortype_name[] = { "grayscale", "[INVALID]", "RGB", "palette", "gray+alpha", "[INVALID]", "RGBA" }; int fnlen, incnt; int depth, colortype, /* compress_method, filter_method, */ interlaced; int channels, bitsperpixel, error; int haveIDAT, haveEOF; long width, height, bitwidth, bytewidth; long chunklen, /* numfilters, */ ucsize; //long csize, csize_orig; ulg chunktyp, chunkcrc, calc_crc; ulg file_offset, file_offset_IDATs=0L; ulg bytes_remaining; if ((infile = fopen(filename, "rb")) == NULL) { fprintf(stderr, " error: cannot open %s for reading\n", filename); fflush(stderr); return 2; } /* build the output filename from the input name by inserting "-fixed" * before the ".png" extension (or by appending that plus ".png" if * there isn't any extension), then make sure it doesn't exist already */ fnlen = strlen(filename); if (fnlen > FNMAX-9) { fprintf(stderr, " warning: base filename [%s] will be truncated\n", filename); fflush(stderr); fnlen = FNMAX-9; } strncpy(outname, filename, fnlen); if (strncmp(outname+fnlen-4, ".png", 4) == 0) strcpy(outname+fnlen-4, "-fixed.png"); else strcpy(outname+fnlen, "-fixed.png"); if (!force) { if ((outfile = fopen(outname, "rb")) != NULL) { fprintf(stderr, " error: %s exists; not overwriting\n", outname); fflush(stderr); fclose(outfile); return 15; } } /* ** Step 1: read in the input image. */ // check PNG header inptr = endptr = inbuf; incnt = fread(endptr, 1, BUFSZ, infile); // 4096 bytes endptr += incnt; if (incnt < MIN_PNG_SIZE || memcmp(inbuf, pngsig, 8) != 0) { fprintf(stderr, " error: %s is not a PNG file\n", filename); fflush(stderr); return 17; } inptr += 8; // check IHDR length, name/ID bytes, and CRC over data chunklen = U32(inptr); inptr += 4; chunktyp = U32(inptr); if (chunklen != 13 || chunktyp != IHDR) { fprintf(stderr, " error: %s has bad IHDR chunk\n", filename); fflush(stderr); return 18; } //inptr += 4; still pointing at chunk name/ID for now calc_crc = crc32(0L, Z_NULL, 0); calc_crc = crc32(calc_crc, inptr, 4+chunklen); inptr += 4; // now pointing at start of chunk data chunkcrc = U32(inptr+chunklen); if (calc_crc != chunkcrc) { fprintf(stderr, " error: %s has bad IHDR CRC (got 0x%08lx, " "expected 0x%08lx)\n", filename, calc_crc, chunkcrc); fflush(stderr); return 19; } // store IHDR data width = U32(inptr); inptr += 4; height = U32(inptr); inptr += 4; depth = *inptr++; colortype = *inptr++; ++inptr; //compress_method = *inptr++; ++inptr; //filter_method = *inptr++; interlaced = *inptr++; inptr += 4; // skip over IHDR CRC incnt = endptr - inptr; file_offset = inptr - inbuf; if (width <= 0 || height <= 0) { fprintf(stderr, " error: %s has invalid dimensions (%ld x %ld)\n", filename, width, height); fflush(stderr); return 20; } error = 0; switch (colortype) { case 0: channels = 1; if (depth != 1 && depth !=2 && depth != 4 && depth != 8 && depth != 16) ++error; break; case 2: channels = 3; if (depth != 8 && depth != 16) ++error; break; case 3: channels = 1; if (depth != 1 && depth !=2 && depth != 4 && depth != 8) ++error; break; case 4: channels = 2; if (depth != 8 && depth != 16) ++error; break; case 6: channels = 4; if (depth != 8 && depth != 16) ++error; break; default: fprintf(stderr, " error: %s has invalid colortype (%d)\n", filename, colortype); fflush(stderr); return 22; } if (error) { fprintf(stderr, " error: %s has invalid sample depth (%d) for " "colortype (%d = %s)\n", filename, depth, colortype, colortype_name[colortype]); fflush(stderr); return 23; } bitsperpixel = depth * channels; /* can't overflow */ if (verbose) { printf(" %ldx%ld, %d-bit, %sinterlaced, %s (type %d) image\n", width, height, bitsperpixel, interlaced? "" : "non-", colortype_name[colortype], colortype); } /* Calculate uncompressed image size and check for overflows. Strictly * speaking, this doesn't apply to interlaced images, but since no * interlace-pass subimage can be bigger than the complete image, the * latter checks are sufficient. */ /* (this calculation works for both sub-8-bit and 8-bit or greater) */ bitwidth = width * bitsperpixel; bytewidth = 1 + (bitwidth + 7) / 8; // 1 -> row-filter byte ucsize = bytewidth * height; //numfilters = height; if (bitwidth/width != bitsperpixel || bytewidth <= 0 || ucsize/bytewidth != height) { fprintf(stderr, " error: %s has invalid dimensions\n", filename); fflush(stderr); return 20; } #define GET_U32(val, docrc) \ if (incnt < 4) { \ int j=incnt, remainder=(4-incnt); \ uch tmpbuf[4]; \ uch *tptr = tmpbuf; \ \ /* copy bytes to temporary buffer */ \ for (; j > 0; --j) \ *tptr++ = *inptr++; \ /* read more bytes into main buffer */ \ inptr = endptr = inbuf; \ incnt = fread(endptr, 1, BUFSZ, infile); /* 4096 bytes */ \ endptr += incnt; \ /* if still fewer than 4 bytes, bail */ \ if (incnt < remainder) { \ haveEOF = TRUE; \ break; \ } \ /* copy remaining bytes to temporary buffer... */ \ for (j = remainder; j > 0; --j) \ *tptr++ = *inptr++; \ incnt -= remainder; \ /* ...and assemble into 32-bit int */ \ val = U32(tmpbuf); \ if (docrc) \ calc_crc = crc32(calc_crc, tmpbuf, 4); \ file_offset += 4; \ } else { \ val = U32(inptr); \ if (docrc) \ calc_crc = crc32(calc_crc, inptr, 4); \ inptr += 4; \ incnt -= 4; \ file_offset += 4; \ } // FIXME: change GET_U32() macro into a function (too big => cache trash) /* Next loop over remaining chunks, verifying CRC of each but otherwise * ignoring them. Stop as soon as find first IDAT. */ //inptr currently points at "length" bytes of first chunk after IHDR //csize_orig = 0L; error = haveEOF = haveIDAT = FALSE; while (!error && !haveEOF) { // check chunk length, name/ID bytes, and CRC over data GET_U32(chunklen, 0) // this advances inptr, refills buffer as needed calc_crc = crc32(0L, Z_NULL, 0); GET_U32(chunktyp, 1) // ...and also updates calc_crc in this case // NOTE: The displayed file offset is that of the chunk name/ID, // *not* the true beginning of the chunk (length-bytes, // 4 bytes earlier). This matches pngcheck's behavior. if (verbose) { printf(" %s chunk (0x%lx), length %ld, at file offset %lu " "(0x%05lx)\n", chunkstr(chunktyp), chunktyp, chunklen, file_offset-4, file_offset-4); } if (chunktyp == IDAT) { haveIDAT = TRUE; file_offset_IDATs = file_offset - 8; // start of chunklen //csize_orig += chunklen; break; } // now pointing at chunk data (i.e., AFTER chunk name/ID) while (chunklen > incnt) { calc_crc = crc32(calc_crc, inptr, incnt); chunklen -= incnt; file_offset += incnt; /* read more bytes into buffer */ inptr = inbuf; incnt = fread(inbuf, 1, BUFSZ, infile); /* 4096 bytes */ if (incnt <= 0) { fprintf(stderr, " error: unexpected EOF while reading %s " "(chunk is missing %ld bytes)\n", filename, chunklen); fflush(stderr); error = haveEOF = TRUE; break; } endptr = inbuf + incnt; // rarely used, but maybe sometimes? } if (error) break; calc_crc = crc32(calc_crc, inptr, chunklen); inptr += chunklen; incnt -= chunklen; file_offset += chunklen; GET_U32(chunkcrc, 0) if (calc_crc != chunkcrc) { fprintf(stderr, " error: %s has bad %s CRC (got 0x%08lx, " "expected 0x%08lx)\n", filename, chunkstr(chunktyp), chunkcrc, chunkcrc); fflush(stderr); error = TRUE; break; } } // end of while-loop over post-IHDR chunks (first pass) if (!haveIDAT) { fprintf(stderr, " error: found no IDAT chunks in %s\n", filename); fflush(stderr); error = TRUE; } if (error) { fclose(infile); return 24; } /* end of "sniffer" pass; now read first IDAT into buffer */ if (fseek(infile, file_offset_IDATs, SEEK_SET) < 0) { fprintf(stderr, " error: %s: can't seek back to start of IDATs?!\n", filename); fflush(stderr); fclose(infile); return 25; } // allocate compressed-image buffer (unless more than 10 MB?) if ((cbuf = (uch *)malloc(chunklen+12)) == NULL) { fprintf(stderr, " error: %s: can't allocate buffer for first IDAT\n", filename); fflush(stderr); fclose(infile); return 26; } // second pass through file: loop over IDATs, reading data into cbuf file_offset = file_offset_IDATs; cptr = cbuf; //csize = csize_orig; incnt = fread(cbuf, 1, chunklen+12, infile); calc_crc = crc32(0L, Z_NULL, 0); calc_crc = crc32(calc_crc, cbuf+4, chunklen+4); chunkcrc = U32(cbuf+4 + chunklen+4); if (calc_crc != chunkcrc) { fprintf(stderr, " error: %s has bad %s CRC (got 0x%08lx, " "expected 0x%08lx)\n", filename, chunkstr(chunktyp), chunkcrc, chunkcrc); fflush(stderr); fclose(infile); return 23; } cptr = cbuf + 8; // if (cptr[0] == 0x68 && cptr[1] == 0x81) // (overly) conservative check if (cptr[0] != 0x78 || cptr[1] != 0x9c) // aggressive check { cptr[0] = 0x78; cptr[1] = 0x9c; } else { fprintf(stderr, " note: %s does not appear to have bad zlib " "windowBits; skipping\n", filename); fflush(stderr); fclose(infile); return 0; } calc_crc = crc32(0L, Z_NULL, 0); calc_crc = crc32(calc_crc, cbuf+4, chunklen+4); cptr = cbuf+4 + chunklen+4; cptr[0] = (calc_crc >> 24); cptr[1] = (calc_crc >> 16) & 0xff; cptr[2] = (calc_crc >> 8) & 0xff; cptr[3] = (calc_crc ) & 0xff; if ((outfile = fopen(outname, "wb")) == NULL) { fprintf(stderr, " error: cannot open %s for writing\n", outname); fflush(stderr); return 16; } /* Now copy infile up to first IDAT; copy cbuf (replaced IDAT); and finally * copy infile from end of first IDAT to end of file. */ // copy infile to outfile, up to file_offset_IDATs fseek(infile, 0, SEEK_SET); bytes_remaining = file_offset_IDATs; while (bytes_remaining > 0) { /* read more bytes into buffer */ incnt = (bytes_remaining > BUFSZ)? BUFSZ : bytes_remaining; incnt = fread(inbuf, 1, incnt, infile); /* 4096 bytes (or less) */ if (incnt <= 0) { fprintf(stderr, " error: unexpected EOF while copying %s\n", filename); fflush(stderr); error = haveEOF = TRUE; break; } fwrite(inbuf, 1, incnt, outfile); bytes_remaining -= incnt; } // copy modified IDAT (cbuf) to outfile fwrite(cbuf, 1, chunklen+12, outfile); // copy rest of infile to outfile fseek(infile, file_offset_IDATs + chunklen+12, SEEK_SET); while (!haveEOF) { /* read more bytes into buffer */ incnt = fread(inbuf, 1, BUFSZ, infile); /* 4096 bytes */ if (incnt <= 0) { // apparently we're done haveEOF = TRUE; break; } fwrite(inbuf, 1, incnt, outfile); } fclose(outfile); if (cbuf) { free(cbuf); cbuf = NULL; } fclose(infile); return error? 27 : 0; } /* end of function png_fix_IDAT_windowsize() */ /* convert chunk type to character-string "name" (assuming charset is ASCII!) */ static char *chunkstr(ulg typ) { static char str[5]; str[0] = ((typ >> 24) & 0xff); str[1] = ((typ >> 16) & 0xff); str[2] = ((typ >> 8) & 0xff); str[3] = ((typ ) & 0xff); str[4] = '\0'; return str; } pngcheck-3.0.2/gpl/pngsplit.10000644000175000017500000000124413747332436015723 0ustar roelofsroelofs.TH PNGSPLIT "1" "October 2020" "pngsplit 1.0" "User Commands" .SH NAME pngsplit \- manual page for pngsplit 1.0 .SH SYNOPSIS .B pngsplit .RI [ options ] .I pngfile .RI [ pngfile \ [...]] .SH DESCRIPTION pngsplit, version 1.0 of 31 October 2020, by Greg Roelofs. .PP Split a PNG, MNG or JNG file into individual, numbered chunks (filenames "foo.png.0000.sig", "foo.png.0001.IHDR", etc.). .PP This software is licensed under the GNU General Public License. There is NO warranty. .SH OPTIONS .TP .B \-force overwrite existing output files .TP .B \-verbose print more status messages (synonym: .BR \-noquiet ) .IP .SH "SEE ALSO" .BR pngcheck (1), .BR png-fix-IDAT-windowsize (1) pngcheck-3.0.2/gpl/pngsplit.c0000644000175000017500000003576513747332413016017 0ustar roelofsroelofs/* pngsplit.c - split a PNG file into individual chunk-files (and check CRCs) ** ** Downloads: ** ** http://www.libpng.org/pub/png/apps/pngcheck.html ** ** To compile (assuming zlib path is ../zlib): ** ** gcc -Wall -O2 -I../zlib pngsplit.c -o pngsplit -L../zlib -lz ** ** ** Copyright 2005-2020 Greg Roelofs ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** */ #define VERSION "1.0 of 31 October 2020" /* * TO DO: * - fix filename-mismatch bookkeeping error (2nd FIXME below) * - convert GET_U32() macro to function (optionally inlinable) * - clean up code, promote to 1.0 */ #include #include #include #include #include // for crc32() /* to use this, define and setenv MALLOC_TRACE /path/to/pngsplit-mtrace.log */ #undef GRR_MALLOC_DEBUG #ifdef GRR_MALLOC_DEBUG # include #endif typedef unsigned long ulg; typedef unsigned short ush; typedef unsigned char uch; #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define FNMAX 1024 /* max filename length */ #define BUFSZ 4096 /* flag bits for writing chunk files */ #define WR_FORCE 0x01 #define WR_OPEN 0x02 #define WR_CLOSE 0x04 #define MIN_PNG_SIZE (8 + 4+4+13+4 + 4+4+10+4 + 4+4+4) // 67 (IDAT = 10: empir.) #define U16(x) ( ((ush)(((uch *)(x))[0]) << 8) | \ ((ush)(((uch *)(x))[1]) ) ) #define U32(x) ( ((ulg)(((uch *)(x))[0]) << 24) | \ ((ulg)(((uch *)(x))[1]) << 16) | \ ((ulg)(((uch *)(x))[2]) << 8) | \ ((ulg)(((uch *)(x))[3]) ) ) #define PNGSPLIT_HDR "\ pngsplit, version " VERSION ", by Greg Roelofs.\n\ This software is licensed under the GNU General Public License.\n\ There is NO warranty.\n\n" #define PNGSPLIT_USAGE "\ usage: pngsplit [options] pngfile [pngfile [...]]\n\ options:\n\ -force overwrite existing output files\n\ -verbose print more status messages (synonym: -noquiet)\n\n\ Split a PNG, MNG or JNG file into individual, numbered chunks (filenames\n\ \"foo.png.0000.sig\", \"foo.png.0001.IHDR\", etc.).\n" static const uch pngsig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; static const uch mngsig[8] = {138, 77, 78, 71, 13, 10, 26, 10}; static const uch jngsig[8] = {139, 74, 78, 71, 13, 10, 26, 10}; /* * ulg IHDR = ( (73L << 24) | (72L << 16) | (68L << 8) | (82L) ); // 0x49484452 * ulg IDAT = ( (73L << 24) | (68L << 16) | (65L << 8) | (84L) ); // 0x49444154 * ulg IEND = ( (73L << 24) | (69L << 16) | (78L << 8) | (68L) ); // 0x49454e44 */ static int pngsplit (char *filename, int force, int verbose); static char *chunkstr (ulg typ); static int write_chunk_to_file (char *basename, int baselen, ulg num, ulg chunklen, ulg chunktyp, ulg chunkcrc, uch *buf, ulg len, int flags); int main(int argc, char *argv[]) { char *filename; int argn; int force = FALSE; int verbose = 0; int latest_error=0, error_count=0, file_count=0; #ifdef __EMX__ _wildcard(&argc, &argv); /* Unix-like globbing for OS/2 and DOS */ #endif #ifdef GRR_MALLOC_DEBUG mtrace(); #endif argn = 1; while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { if ( 0 == strncmp( argv[argn], "-force", 2 ) ) force = TRUE; else if ( 0 == strncmp( argv[argn], "-noforce", 4 ) ) force = FALSE; else if ( 0 == strncmp( argv[argn], "-verbose", 2 ) || 0 == strncmp( argv[argn], "-noquiet", 4 ) ) ++verbose; else if ( 0 == strncmp( argv[argn], "-noverbose", 4 ) || 0 == strncmp( argv[argn], "-quiet", 2 ) ) verbose = 0; else { fprintf(stderr, PNGSPLIT_HDR); fprintf(stderr, PNGSPLIT_USAGE); fflush(stderr); return 1; } ++argn; } if ( argn == argc ) { fprintf(stderr, PNGSPLIT_HDR); fprintf(stderr, PNGSPLIT_USAGE); fflush(stderr); return 5; } else { filename = argv[argn]; ++argn; } /*============================= MAIN LOOP =============================*/ fprintf(stdout, PNGSPLIT_HDR); fflush(stdout); while (argn <= argc) { int retval; if (verbose >= 0) { printf("%s:\n", filename); fflush(stdout); } retval = pngsplit(filename, force, verbose); if (retval) { latest_error = retval; ++error_count; } ++file_count; if (verbose) { printf("\n"); fflush(stdout); } filename = argv[argn]; ++argn; } /*=======================================================================*/ if (verbose) { if (error_count) printf("There were errors splitting %d PNG file%s out of a" " total of %d file%s.\n", error_count, (error_count == 1)? "" : "s", file_count, (file_count == 1)? "" : "s"); else printf("No errors detected while splitting %d PNG image%s.\n", file_count, (file_count == 1)? "" : "s"); fflush(stdout); } return latest_error; } #define GET_U32(val, docrc) \ if (incnt < 4) { \ int j=incnt, remainder=(4-incnt); \ uch tmpbuf[4]; \ uch *tptr = tmpbuf; \ \ /* copy bytes to temporary buffer */ \ for (; j > 0; --j) \ *tptr++ = *inptr++; \ /* read more bytes into main buffer */ \ inptr = inbuf; \ incnt = fread(inbuf, 1, BUFSZ, infile); /* 4096 bytes */ \ /* if still fewer than 4 bytes, bail */ \ if (incnt < remainder) { \ haveEOF = TRUE; \ break; \ } \ /* copy remaining bytes to temporary buffer... */ \ for (j = remainder; j > 0; --j) \ *tptr++ = *inptr++; \ incnt -= remainder; \ /* ...and assemble into 32-bit int */ \ val = U32(tmpbuf); \ if (docrc) \ calc_crc = crc32(calc_crc, tmpbuf, 4); \ file_offset += 4; \ } else { \ val = U32(inptr); \ if (docrc) \ calc_crc = crc32(calc_crc, inptr, 4); \ inptr += 4; \ incnt -= 4; \ file_offset += 4; \ } // FIXME: change GET_U32() macro into a function (too big => cache trash) static int pngsplit(char *filename, int force, int verbose) { FILE *infile; uch inbuf[BUFSZ], *inptr; int fnlen, incnt, flags, error, haveEOF; ulg chunknum=0L, chunklen, chunktyp, chunkcrc, calc_crc; ulg file_offset=0L; if (force) force = WR_FORCE; // ensure compatibility with flags below if ((infile = fopen(filename, "rb")) == NULL) { fprintf(stderr, " error: cannot open %s for reading\n", filename); fflush(stderr); return 2; } fnlen = strlen(filename); if (fnlen > FNMAX-12) { fprintf(stderr, " warning: base filename [%s] will be truncated\n", filename); fflush(stderr); fnlen = FNMAX-12; } /* ** Step 1: read in the input image. */ // check PNG/MNG/JNG header inptr = inbuf; incnt = fread(inbuf, 1, BUFSZ, infile); // 4096 bytes if (incnt < MIN_PNG_SIZE || (memcmp(inbuf, pngsig, 8) != 0 && memcmp(inbuf, mngsig, 8) != 0 && memcmp(inbuf, jngsig, 8) != 0)) { fprintf(stderr, " error: %s does not appear to be a PNG, MNG, or " "JNG file\n", filename); fflush(stderr); return 17; } inptr += 8; incnt -= 8; file_offset = 8L; flags = force | WR_OPEN | WR_CLOSE; if (write_chunk_to_file(filename, fnlen, chunknum, 0,0,0, inbuf, 8, flags)) return 18; error = haveEOF = FALSE; while (!error && !haveEOF) { // check chunk length, name/ID bytes, and CRC over data GET_U32(chunklen, 0) // this advances inptr, refills buffer as needed calc_crc = crc32(0L, Z_NULL, 0); GET_U32(chunktyp, 1) // ...and also updates calc_crc in this case ++chunknum; error = write_chunk_to_file(filename, fnlen, chunknum, chunklen, chunktyp, 0, inbuf, 0, WR_OPEN | force); if (error) return 19; // NOTE: The displayed file offset is that of the chunk name/ID, // *not* the true beginning of the chunk (length-bytes, // 4 bytes earlier). This matches pngcheck's behavior. if (verbose) { printf(" %s chunk (0x%lx), length %ld, at file offset %lu " "(0x%05lx)\n", chunkstr(chunktyp), chunktyp, chunklen, file_offset-4, file_offset-4); } // now pointing at chunk data (i.e., AFTER chunk name/ID) while (chunklen > incnt) { calc_crc = crc32(calc_crc, inptr, incnt); error = write_chunk_to_file(filename, fnlen, chunknum, 0, 0, 0, inptr, incnt, force); if (error) return 20; chunklen -= incnt; file_offset += incnt; /* read more bytes into buffer */ inptr = inbuf; incnt = fread(inbuf, 1, BUFSZ, infile); /* 4096 bytes */ if (incnt <= 0) { fprintf(stderr, " error: unexpected EOF while reading %s " "(chunk is missing %ld bytes)\n", filename, chunklen); fflush(stderr); return 21; } } calc_crc = crc32(calc_crc, inptr, chunklen); error = write_chunk_to_file(filename, fnlen, chunknum, 0, 0, 0, inptr, chunklen, force); if (error) return 22; inptr += chunklen; incnt -= chunklen; file_offset += chunklen; GET_U32(chunkcrc, 0) error = write_chunk_to_file(filename, fnlen, chunknum, 0, 0, chunkcrc, inbuf, 0, WR_CLOSE | force); if (error) return 23; if (calc_crc != chunkcrc) { fprintf(stderr, " error: %s has bad %s CRC (got 0x%08lx, " "expected 0x%08lx)\n", filename, chunkstr(chunktyp), calc_crc, chunkcrc); fflush(stderr); return 24; } } // end of while-loop over chunks fclose(infile); return 0; /* success! */ } /* end of function pngsplit() */ /* convert chunk type to character-string "name" (assuming charset is ASCII!) */ static char *chunkstr(ulg typ) { static char str[5]; str[0] = ((typ >> 24) & 0xff); str[1] = ((typ >> 16) & 0xff); str[2] = ((typ >> 8) & 0xff); str[3] = ((typ ) & 0xff); str[4] = '\0'; return str; } /* GRR FIXME: have bookkeeping error when file already exists: note name mismatch on 2nd-4th file/error messages: ArcTriomphe-cHRM-red-blue-swap.png: warning: ArcTriomphe-cHRM-red-blue-swap.png.0000.sig exists; not overwriting ArcTriomphe-cHRM-red-green-swap.png: write_chunk_to_file() logic error: OPEN flag set but ArcTriomphe-cHRM-red-blue-swap.png.0000.sig still open ArcTriomphe-iCCP-red-blue-swap.png: write_chunk_to_file() logic error: OPEN flag set but ArcTriomphe-cHRM-red-blue-swap.png.0000.sig still open ArcTriomphe-iCCP-red-green-swap.png: write_chunk_to_file() logic error: OPEN flag set but ArcTriomphe-cHRM-red-blue-swap.png.0000.sig still open */ // 0 = OK, 1 = warning (exists but continuing), 2 = error static int write_chunk_to_file(char *basename, int baselen, ulg num, ulg chunklen, ulg chunktyp, ulg chunkcrc, uch *buf, ulg len, int flags) { static FILE *outfile=NULL; static char outname[FNMAX]; ulg wlen; if (flags & WR_OPEN) { if (outfile) { fprintf(stderr, " write_chunk_to_file() logic error: OPEN flag " "set but %s still open\n", outname); fflush(stderr); return 2; } strncpy(outname, basename, baselen); if (num == 0L) { strcpy(outname+baselen, ".0000.sig"); } else { sprintf(outname+baselen, ".%04lu.%s", num, chunkstr(chunktyp)); } if (!(flags & WR_FORCE)) { if ((outfile = fopen(outname, "rb")) != NULL) { fprintf(stderr, " warning: %s exists; not overwriting\n", outname); fflush(stderr); fclose(outfile); return 1; } } if ((outfile = fopen(outname, "wb")) == NULL) { fprintf(stderr, " error: cannot open %s for writing\n", outname); fflush(stderr); return 2; } if (num != 0L) { fputc((int)((chunklen >> 24) & 0xff), outfile); fputc((int)((chunklen >> 16) & 0xff), outfile); fputc((int)((chunklen >> 8) & 0xff), outfile); fputc((int)((chunklen ) & 0xff), outfile); fputc((int)((chunktyp >> 24) & 0xff), outfile); fputc((int)((chunktyp >> 16) & 0xff), outfile); fputc((int)((chunktyp >> 8) & 0xff), outfile); fputc((int)((chunktyp ) & 0xff), outfile); } } if (!outfile) { fprintf(stderr, " write_chunk_to_file() logic error: about to " "fwrite() but no file is open\n"); fflush(stderr); return 2; } wlen = len? fwrite(buf, 1L, len, outfile) : 0L; if (flags & WR_CLOSE) { if (num != 0L) { fputc((int)((chunkcrc >> 24) & 0xff), outfile); fputc((int)((chunkcrc >> 16) & 0xff), outfile); fputc((int)((chunkcrc >> 8) & 0xff), outfile); fputc((int)((chunkcrc ) & 0xff), outfile); } if (!outfile) { fprintf(stderr, " write_chunk_to_file() logic error: CLOSE flag " "set but no file is open\n"); fflush(stderr); return 2; } fclose(outfile); outfile = NULL; outname[0] = '\0'; } if (wlen != len) { fprintf(stderr, " error: wrote %lu of %lu bytes (%s)\n", wlen, len, outname); fflush(stderr); return 2; } return 0; }