pax_global_header00006660000000000000000000000064151163570210014513gustar00rootroot0000000000000052 comment=861c978c3b1bac93659202fd25b9b76bc852eee1 pngcheck-4.0.1/000077500000000000000000000000001511635702100132775ustar00rootroot00000000000000pngcheck-4.0.1/.editorconfig000066400000000000000000000007461511635702100157630ustar00rootroot00000000000000# https://editorconfig.org root = true [*] charset = utf-8 indent_style = space insert_final_newline = true max_doc_length = 80 max_line_length = 100 trim_trailing_whitespace = true [Makefile*] indent_style = unset insert_final_newline = unset max_doc_length = unset max_line_length = unset trim_trailing_whitespace = unset [*.md] indent_style = space max_doc_length = unset max_line_length = unset [COMMIT_EDITMSG] indent_style = space max_doc_length = unset max_line_length = 72 pngcheck-4.0.1/.github/000077500000000000000000000000001511635702100146375ustar00rootroot00000000000000pngcheck-4.0.1/.github/workflows/000077500000000000000000000000001511635702100166745ustar00rootroot00000000000000pngcheck-4.0.1/.github/workflows/build-makefile.yml000066400000000000000000000034131511635702100222720ustar00rootroot00000000000000name: build-makefile on: push: branches: [ main ] pull_request: workflow_dispatch: concurrency: group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' cancel-in-progress: true jobs: load-matrix: name: Load Build Matrix runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set matrix outputs id: set-matrix run: | echo "matrix=$(jq -c '.' matrix-makefile.json)" >> $GITHUB_OUTPUT build-makefile: name: ${{ matrix.name }}-makefile runs-on: ${{ matrix.runner }} needs: load-matrix strategy: fail-fast: false matrix: include: ${{ fromJson(needs.load-matrix.outputs.matrix) }} steps: - name: Checkout uses: actions/checkout@v4 # Ubuntu dependencies - name: Install dependencies (Ubuntu) run: | sudo apt-get update sudo apt-get install -y build-essential zlib1g-dev # Makefile builds - name: Build with Makefile (Ubuntu) run: | make - name: Test Makefile build (Ubuntu) run: | ./pngcheck -h # - name: Prepare artifacts (Ubuntu) # run: | # mkdir -p artifacts # cp pngcheck artifacts/pngcheck-${{ matrix.name }}-makefile # cd artifacts # if command -v sha256sum >/dev/null 2>&1; then # sha256sum * > checksums.txt # elif command -v shasum >/dev/null 2>&1; then # shasum -a 256 * > checksums.txt # fi # - name: Upload artifacts # uses: actions/upload-artifact@v4 # with: # name: pngcheck-${{ matrix.name }}-makefile # path: artifacts/ # retention-days: 30 pngcheck-4.0.1/.github/workflows/build.yml000066400000000000000000000110741511635702100205210ustar00rootroot00000000000000name: build-cmake on: push: branches: [ main ] pull_request: workflow_dispatch: concurrency: group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' cancel-in-progress: true jobs: load-matrix: name: Load Build Matrix runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set matrix outputs id: set-matrix run: | echo "matrix=$(jq -c '.' matrix.json)" >> $GITHUB_OUTPUT build-cmake: name: ${{ matrix.name }}-cmake runs-on: ${{ matrix.runner }} needs: load-matrix strategy: fail-fast: false matrix: include: ${{ fromJson(needs.load-matrix.outputs.matrix) }} steps: - name: Checkout uses: actions/checkout@v4 # MSYS2 setup - name: Setup MSYS2 if: matrix.os == 'windows-msys2' uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.msys }} path-type: minimal update: true install: >- git make pacboy: >- toolchain:p zlib:p cmake:p # Ubuntu dependencies - name: Install dependencies (Ubuntu) if: matrix.os == 'ubuntu' run: | sudo apt-get update sudo apt-get install -y cmake build-essential zlib1g-dev # macOS dependencies - name: Install dependencies (macOS) if: matrix.os == 'macos' run: | brew list zlib || brew install zlib # Windows dependencies - name: Setup MSVC (Windows) if: matrix.os == 'windows' uses: microsoft/setup-msbuild@v2 - name: Install vcpkg dependencies (Windows) if: matrix.os == 'windows' run: | $arch = if ("${{ matrix.runner }}" -eq "windows-11-arm") { "arm64" } else { "x64" } vcpkg install zlib:$arch-windows # CMake builds - name: Build with CMake (Ubuntu/macOS) if: matrix.os == 'ubuntu' || matrix.os == 'macos' run: | cmake -B build-cmake \ -DCMAKE_BUILD_TYPE=Release \ -DPNGCHECK_USE_SYSTEM_ZLIB=ON cmake --build build-cmake --config Release - name: Build with CMake (Windows MSVC) if: matrix.os == 'windows' run: | $arch = if ("${{ matrix.runner }}" -eq "windows-11-arm") { "ARM64" } else { "x64" } cmake -B build-cmake -A $arch -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake cmake --build build-cmake --config Release - name: Build with CMake (Windows MSYS2) if: matrix.os == 'windows-msys2' shell: msys2 {0} run: | cmake -B build-cmake \ -G "MSYS Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DPNGCHECK_USE_SYSTEM_ZLIB=ON cmake --build build-cmake --config Release # Testing CMake builds - name: Test CMake build (MSYS2) if: matrix.os == 'windows-msys2' shell: msys2 {0} run: | build-cmake/pngcheck.exe -h - name: Test CMake build (Windows MSVC) if: matrix.os == 'windows' run: | build-cmake/Release/pngcheck.exe -h - name: Test CMake build (Unix/macOS) if: matrix.os == 'ubuntu' || matrix.os == 'macos' run: | ./build-cmake/pngcheck -h # Artifacts - name: Prepare artifacts (MSYS2) if: matrix.os == 'windows-msys2' shell: msys2 {0} run: | mkdir -p artifacts cp build-cmake/pngcheck.exe artifacts/pngcheck-${{ matrix.name }}-cmake.exe cd artifacts sha256sum * > checksums.txt - name: Prepare artifacts (Windows MSVC) if: matrix.os == 'windows' run: | mkdir -p artifacts cp build-cmake/Release/pngcheck.exe artifacts/pngcheck-${{ matrix.name }}-cmake.exe cd artifacts Get-FileHash -Algorithm SHA256 *.exe | ForEach-Object { "$($_.Hash.ToLower()) $($_.Path | Split-Path -Leaf)" } > checksums.txt - name: Prepare artifacts (Unix/macOS) if: matrix.os == 'ubuntu' || matrix.os == 'macos' run: | mkdir -p artifacts cp build-cmake/pngcheck artifacts/pngcheck-${{ matrix.name }}-cmake cd artifacts if command -v sha256sum >/dev/null 2>&1; then sha256sum * > checksums.txt elif command -v shasum >/dev/null 2>&1; then shasum -a 256 * > checksums.txt fi - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: pngcheck-${{ matrix.name }}-cmake path: artifacts/ retention-days: 30 pngcheck-4.0.1/.github/workflows/release.yml000066400000000000000000000054151511635702100210440ustar00rootroot00000000000000name: release on: push: tags: - 'v*' workflow_dispatch: jobs: build: name: Build Release Artifacts runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: # Ubuntu 22.04 builds only, compatibile with 22+ later versions - runner: ubuntu-22.04 os: ubuntu arch: x64 - runner: ubuntu-22.04-arm os: ubuntu arch: arm64 steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y zlib1g-dev cmake build-essential - name: Configure CMake run: | cmake -B build \ -DCMAKE_BUILD_TYPE=Release \ -DPNGCHECK_USE_SYSTEM_ZLIB=ON - name: Build run: cmake --build build --config Release - name: Prepare release artifacts run: | mkdir -p release-artifacts cp build/pngcheck release-artifacts/pngcheck-${{ matrix.runner }}-${{ matrix.arch }} # Generate checksums cd release-artifacts sha256sum * > checksums-${{ matrix.runner }}-${{ matrix.arch }}.txt - name: Upload release artifacts uses: actions/upload-artifact@v4 with: name: pngcheck-${{ matrix.runner }}-${{ matrix.arch }} path: release-artifacts/ retention-days: 90 release: name: Create GitHub Release runs-on: ubuntu-latest needs: build if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout uses: actions/checkout@v4 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: all-artifacts - name: Prepare release assets run: | mkdir -p release-assets # Flatten artifact structure and rename files find all-artifacts -type f -name "pngcheck*" -exec cp {} release-assets/ \; find all-artifacts -type f -name "checksums*" -exec cp {} release-assets/ \; # Create a combined checksums file cd release-assets cat checksums-*.txt > combined-checksums.txt # List all files for verification ls -la - name: Create Release uses: softprops/action-gh-release@v1 with: name: Release ${{ github.ref_name }} body: | ## pngcheck ${{ github.ref_name }} Compiled binaries for Ubuntu 22.04: ### Ubuntu 22.04 - `pngcheck-ubuntu-22.04-x64` - Ubuntu 22.04 (x64) - `pngcheck-ubuntu-22.04-arm-arm64` - Ubuntu 22.04 (ARM64) All binaries are built with CMake and system zlib, and include SHA256 checksums for verification. files: release-assets/* draft: false prerelease: false token: ${{ secrets.GITHUB_TOKEN }} pngcheck-4.0.1/.gitignore000066400000000000000000000021351511635702100152700ustar00rootroot00000000000000# Build artifacts *.[ao] *.l[ao] *.lai *.slo *.so *.so.* *.app/ *.dylib *.dll *.exe *.exp *.ilk *.lib *.obj *.res a.out # Debugging artifacts .gdbinit .lldbinit *.dSYM/ *.dmp *.idb *.pdb *.su *.sym *.tds core core.[0-9]* !core/ # Coverage testing artifacts *.gcda *.gcno *.gcov # Tag files TAGS .TAGS !TAGS/ tags .tags !tags/ gtags.files GTAGS GRTAGS GPATH GSYMS cscope.files cscope.out cscope.*.out # Text editing and text processing artifacts \#*\# .\#* [._]*.sw[a-p] [._]sw[a-p] *.bak *.orig *.rej *.tmp *~ # IDE files and directories ## Eclipse .cproject/ .project/ .settings/ ## Embarcadero RAD Studio *.cbproj.* __recovery/ ## JetBrains .idea/ ## NetBeans nbbuild/ nbdist/ nbproject/ ## Visual Studio 2015 and newer .vs/ ## Visual Studio Code .vscode/* !.vscode/extensions.json !.vscode/launch.json !.vscode/settings.json !.vscode/tasks.json ## Xcode 9 and newer xcuserdata/ ## (Various) *.*cache *.cache* [._]*_history .history/ [Bb]ackup*/ CMakeLists.txt.user* CMakeUserPresets.json # Build, test and CI output directories [._]build*/ /[Bb]uild/ /[Oo]ut/ *[Dd]ebug/ [Dd]ebug*/ *[Rr]elease/ [Rr]elease*/ pngcheck-4.0.1/CHANGELOG000066400000000000000000000426661511635702100145270ustar00rootroot00000000000000 * ChangeLog (oldest first): * * Started by Alexander Lehmann and subsequently * extended by people as listed below. * * AL - Alexander Lehmann * AED - Andreas Dilger * BB - Ben Beasley (Fedora Linux) * CL - Chris Lilley * CT - Cosmin Truta * GRP - Glenn Randers-Pehrson * GRR - Greg Roelofs * JB - John Bowler * TGL - Tom Lane * RT - Ronald Tse (Ribose) * * 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 * ---------------------- * 20210416 BB: fixed a divide-by-zero crash bug (and probable vulnerability) * in interlaced images with extra compressed data beyond the * nominal end of the image data (found by "chiba of topsec alpha * lab") * 20210425 GRR: released version 3.0.3 * ---------------------- * 20250106 CL: Added basic cICP support, flagged uses of cLLi and mDCv * 20250115 CL: Add mDCV support. Add cLLI support * 20250116 CL: Fix narrow-range bug * 20250123 CL: Detect eXIf after IDAT for PNG Third Edition compliance * 20250124 CL: Added APNG support * 20250203 CL: Test for fdAT after each fcTL (except first) * 20250204 CL: Either IDAT or fdAT needed after fcTL * 20250207 CL: Fix (almost) all warnings about signed vs unsigned comparisons * 20250218 CL: Detect unknown (zero) in cLLI * 20250218 CT: Merge git history (to 3.0.3) and unofficial fork * 20250221 JB: CLEANUP: Add warnings, add const to char * * 20250303 CL: Add P3D65-PQ to known cICP color spaces * 20250304 CT: Added a CMake file and an installation instructions file * 20250304 CL: released version 4.0.0 * ---------------------- * 20250305 CT: Removed gpl/pngsplit and gpl/png-fix-IDAT-windowsize from this project; we're moving them to a new home * 20250307 CL: test for tIME valid day in month * 20250328 CL: Add cICP autodetect for BT.601 PAL, SECAM and NTSC * 20250516 CL: Add test for missing cICP, if mDCV is present * 20250604 CT: Removed the need to define the WIN32 macro for Windows builds * 20250604 CT: Required the zlib library as a non-optional dependency, and removed the need to define the USE_ZLIB macro externally * 20250605 CT: Added third_party/wildargs to auto-expand wildcard arguments * 20250707 RT: Prevent hard fails on chunks of unknown type in compliance with PNG spec. By Maxim Samsonov (Ribose), metanorma/pngcheck-metanorma#1. * 20250708 RT: Added CI workflows and Windows Makefiles (MSVC and MinGW) for GitHub Actions on Linux, macOS, and Windows. * 20251114 CL: Added iDOT support * 20251208 CL: Added (minimal) caBX support for PNG 4th Edition * 20251210 CT: released version 4.0.1 * ---------------------- pngcheck-4.0.1/CMakeLists.txt000066400000000000000000000035761511635702100160520ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.14...4.0) project(pngcheck VERSION 4.0.1 DESCRIPTION "PNG file checker" HOMEPAGE_URL "http://www.libpng.org/pub/png/apps/pngcheck.html" LANGUAGES C ) # Options for zlib support option(PNGCHECK_USE_SYSTEM_ZLIB "Use the system-installed zlib" ON) if(PNGCHECK_USE_ZLIB) message(DEPRECATION "The option PNGCHECK_USE_ZLIB has been discontinued") endif() # Source files set(PNGCHECK_SOURCES pngcheck.c) if(NOT UNIX) list(APPEND PNGCHECK_SOURCES third_party/wildargs/wildargs.c) endif() # Executables add_executable(pngcheck ${PNGCHECK_SOURCES}) # Dependency handling if(PNGCHECK_USE_SYSTEM_ZLIB) find_package(ZLIB) if(ZLIB_FOUND) target_link_libraries(pngcheck PRIVATE ZLIB::ZLIB) else() message(WARNING "System ZLIB not found, falling back to FetchContent") set(PNGCHECK_USE_SYSTEM_ZLIB OFF) endif() endif() if(NOT PNGCHECK_USE_SYSTEM_ZLIB) include(FetchContent) FetchContent_Declare(zlib GIT_REPOSITORY https://github.com/madler/zlib.git GIT_TAG v1.3.1 ) FetchContent_MakeAvailable(zlib) get_target_property(PNGCHECK_ZLIB_SOURCE_DIR zlibstatic SOURCE_DIR) if(PNGCHECK_ZLIB_SOURCE_DIR) message(STATUS "Using local ZLIB subproject: ${PNGCHECK_ZLIB_SOURCE_DIR}") else() message(STATUS "Using local ZLIB subproject (location not available)") endif() target_link_libraries(pngcheck PRIVATE zlibstatic) endif() # Installation rules include(GNUInstallDirs) install(TARGETS pngcheck RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime ) install(FILES pngcheck.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT Documentation ) # Package generation set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION}) include(CPack) pngcheck-4.0.1/INSTALL.md000066400000000000000000000102031511635702100147230ustar00rootroot00000000000000# Installing pngcheck ## Requirements - Standard C compiler - CMake 3.14 or later - zlib library or compatible (e.g. zlib-ng) ## Build Instructions ### Using CMake (recommended) #### Unix/Linux/etc. 1. Create a build directory: ```sh mkdir build && cd build ``` 2. Configure with one of these options: ```sh # Default build with the system-installed zlib (if available) cmake .. # Build with a locally-downloaded zlib cmake -DPNGCHECK_USE_SYSTEM_ZLIB=OFF .. ``` 3. Build: ```sh cmake --build . ``` 4. Install (optional): ```sh # Depending your system setup, you might need to use 'sudo' cmake --install . ``` #### Windows (MSVC with vcpkg) 1. Install zlib via vcpkg: ```cmd vcpkg install zlib:x64-windows # For ARM64: vcpkg install zlib:arm64-windows ``` 2. Configure and build: ```cmd cmake -B build -A x64 -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake # For ARM64: cmake -B build -A ARM64 -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake cmake --build build --config Release ``` #### Windows (MSYS2/MinGW) 1. Install dependencies in MSYS2: ```sh pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-zlib mingw-w64-x86_64-cmake # For 32-bit: mingw-w64-i686-toolchain mingw-w64-i686-zlib mingw-w64-i686-cmake ``` 2. Configure and build: ```sh cmake -B build -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON cmake --build build --config Release ``` #### macOS (with Homebrew zlib) If using Homebrew-installed zlib instead of system zlib: 1. Install zlib via Homebrew: ```sh brew install zlib ``` 2. Configure and build (same as generic instructions above) ### Using Make (the traditional method) * With the system-installed zlib (if available): ```sh make ``` * With zlib installed in a non-standard location, using dynamic linking, forcing compilation with gcc: ```sh make CC="gcc" \ CPPFLAGS="-I/path/to/zlib" \ LDFLAGS="-L/path/to/zlib" ``` * With zlib installed in a non-standard location, using static linking, forcing compilation with clang at the highest optimization level targeting the local machine: ```sh make CC="clang" \ CFLAGS="-O3 -march=native -mtune=native" \ CPPFLAGS="-I/path/to/zlib" \ LIBS="/path/to/zlib/libz.a" ``` ### Using the compiler and linker directly (the "hard" method) #### Unix/Linux/etc. * With the system-installed zlib (if available): ```sh cc -Wall -O -o pngcheck pngcheck.c -lz ``` * With zlib installed in a non-standard location, using dynamic linking: ```sh cc -Wall -O -I/path/to/zlib -o pngcheck pngcheck.c -L/path/to/zlib -lz ``` * With zlib installed in a non-standard location, using static linking: ```sh cc -Wall -O -I/path/to/zlib -o pngcheck pngcheck.c /path/to/zlib/libz.a ``` #### Windows (MSVC) * With zlib compiled as a Windows DLL (typically distributed as `zlib1.dll`): ```cmd cl -nologo -O2 -W3 -I\path\to\zlib -c pngcheck.c cl -nologo -O2 -W3 -c third_party\wildargs\wildargs.c link -nologo pngcheck.obj wildargs.obj \path\to\zlib\zdll.lib ``` Copy `pngcheck.exe` and `zlib1.dll` to the installation directory. * With zlib compiled as a static library: ```cmd cl -nologo -O2 -W3 -I\path\to\zlib -c pngcheck.c cl -nologo -O2 -W3 -c third_party\wildargs\wildargs.c link -nologo pngcheck.obj wildargs.obj \path\to\zlib\zlib.lib ``` Copy `pngcheck.exe` to the installation directory. ## Notes - CMake build is recommended as it handles dependencies and platform differences automatically. - zlib support used to be optional, but now it is mandatory. - Automatic wildcard argument expansion is supported on select non-Unix compilers and platforms. We currently support EMX/GCC on OS2, as well as Microsoft Visual C, Embarcadero Borland C, MinGW/GCC and Clang on Windows. ## More Information - zlib: http://www.zlib.net/ - PNG/MNG/JNG: http://www.libpng.org/pub/png/ - pngcheck: http://www.libpng.org/pub/png/apps/pngcheck.html pngcheck-4.0.1/LICENSE000066400000000000000000000021521511635702100143040ustar00rootroot00000000000000 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 "amiga" subdirectory are Copyright 2003 Simon Goodwin and were contributed without an explicit license statement, but we assume assume that their intended license was the same as pngcheck's, just with Simon's copyright replacing the one above.] pngcheck-4.0.1/Makefile000066400000000000000000000030361511635702100147410ustar00rootroot00000000000000# Makefile for pngcheck by Greg Roelofs et al. # # This makefile assumes zlib has either been built in a subdirectory at the # same level as the current subdirectory (as indicated by the ZLIB_PATH macro # below), or installed system-wide. Edit the ZLIB_* macros as appropriate. # macros -------------------------------------------------------------------- ZLIB_PATH = ../zlib ZLIB_INCPATH = $(ZLIB_PATH) ZLIB_LIBPATH = $(ZLIB_PATH) ZLIB_LIB = -lz CC = cc # gcc or clang LD = $(CC) RM_F = rm -f STDC = -pedantic-errors # -std=c99 or -std=c11 or -std=c17 etc. WARN = -Wall -Wextra -Wundef WARNMORE = -Wcast-align -Wconversion -Wpointer-arith -Wwrite-strings \ -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes \ -Wno-implicit-fallthrough LOCAL_CPPFLAGS = LOCAL_CFLAGS = $(STDC) $(WARN) # $(WARNMORE) LOCAL_LDFLAGS = CPPFLAGS = -I$(ZLIB_INCPATH) CFLAGS = -O2 # -g LDFLAGS = -L$(ZLIB_LIBPATH) # -g LIBS = $(ZLIB_LIB) ALL_CPPFLAGS = $(LOCAL_CPPFLAGS) $(CPPFLAGS) ALL_CFLAGS = $(LOCAL_CFLAGS) $(CFLAGS) ALL_LDFLAGS = $(LOCAL_LDFLAGS) $(LDFLAGS) EXEEXT = # .exe EXES = pngcheck$(EXEEXT) OBJS = pngcheck.o # implicit make rules ------------------------------------------------------- .c.o: $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) -o $@ $*.c # dependencies -------------------------------------------------------------- all: $(EXES) pngcheck$(EXEEXT): pngcheck.o $(LD) $(ALL_LDFLAGS) -o $@ pngcheck.c $(LIBS) # maintenance --------------------------------------------------------------- clean: $(RM_F) $(EXES) $(OBJS) pngcheck-4.0.1/Makefile.riscos000066400000000000000000000010311511635702100162330ustar00rootroot00000000000000CC = cc LD = link CFLAGS = -c -Otime -Ospace -fa -IC:,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-4.0.1/README-303000066400000000000000000000102171511635702100144630ustar00rootroot00000000000000pngcheck version 3.0.3 of 25 April 2021 This version fixes another (probable) security vulnerability discovered by "chiba of topsec alpha lab". Ben Beasley of the Fedora Linux project fixed it, and I generalized it a bit to forestall other attempts by the code to keep decoding beyond the declared image dimensions. As always, many thanks to Ben and to the various security researchers who continue to ferret out problematic code. 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, chiba, 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-4.0.1/README.md000066400000000000000000000042551511635702100145640ustar00rootroot00000000000000# pngcheck 4.0.1 pngcheck is a command-line utility to check PNG image files, including animated PNG, for validity and to give information about metadata inside the file (apart from the actual image data). This version was derived from pngcheck 3.0.3 (see [3.0.3 README](./README-303)) and adds the following new features from [PNG Third Edition](https://www.w3.org/TR/png-3/): - Coding Independent Code Points [`cICP`](https://w3c.github.io/png/#cICP-chunk) - Mastering Display Color Volume [`mDCV`](https://w3c.github.io/png/#mDCV-chunk) - Content Light Level Information [`cLLI`](https://w3c.github.io/png/#cLLI-chunk) - Animated PNG [`acTL`](https://w3c.github.io/png/#acTL-chunk), [`fcTL`](https://w3c.github.io/png/#fcTL-chunk) and [`fdAT`](https://w3c.github.io/png/#fdAT-chunk) It adds the following new features from [PNG Fourth Edition](https://w3c.github.io/png/): - Content Credentials [`caBX`](https://w3c.github.io/png/#caBX) It also warns if [`eXIf`](https://w3c.github.io/png/#eXIf) is found after the image data [`IDAT`](https://w3c.github.io/png/#11IDAT), which will be ignored by web browsers and is [no longer valid](https://w3c.github.io/png/#5ChunkOrdering) in PNG Third Edition. All registered extension chunks are supported, including the recently registered (but widespread) Apple [`iDOT`](https://w3c.github.io/png/extensions/Overview.html#R.iDOT) chunk. Sample usage: ```text $ pngcheck -c -v test_pattern-PQ.png File: test_pattern-PQ.png (12033 bytes) chunk IHDR at offset 0x0000c, length 13 1024 x 1024 image, 48-bit RGB, non-interlaced chunk iCCP at offset 0x00025, length 2181 profile name = 1, compression method = 0 (deflate) compressed profile = 2178 bytes chunk cHRM at offset 0x008b6, length 32 White x = 0.3127 y = 0.329, Red x = 0.708 y = 0.292 Green x = 0.17 y = 0.797, Blue x = 0.131 y = 0.046 chunk cICP at offset 0x008e2, length 4 Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system White x = 0.3127 y = 0.329, Red x = 0.708 y = 0.292 Green x = 0.17 y = 0.797, Blue x = 0.131 y = 0.046 Full range chunk cLLi at offset 0x008f2, length 8 Old version of CLLI, do not use ERRORS DETECTED in test_pattern-PQ.png ``` pngcheck-4.0.1/amiga/000077500000000000000000000000001511635702100143555ustar00rootroot00000000000000pngcheck-4.0.1/amiga/README.Amiga000066400000000000000000000063151511635702100162570ustar00rootroot00000000000000[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-4.0.1/amiga/pngcheck_all000066400000000000000000000002751511635702100167160ustar00rootroot00000000000000.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-4.0.1/amiga/scoptions000066400000000000000000000001061511635702100163160ustar00rootroot00000000000000CPU=68000 DATA=FAR CODE=FAR OPT NOSTACKCHECK LINK library=lib:scm.lib pngcheck-4.0.1/matrix-makefile.json000066400000000000000000000002671511635702100172560ustar00rootroot00000000000000[ { "runner": "ubuntu-24.04", "os": "ubuntu", "name": "ubuntu-24.04" }, { "runner": "ubuntu-24.04-arm", "os": "ubuntu", "name": "ubuntu-24.04-arm" } ] pngcheck-4.0.1/matrix.json000066400000000000000000000013451511635702100155010ustar00rootroot00000000000000[ { "runner": "ubuntu-24.04", "os": "ubuntu", "name": "ubuntu-24.04" }, { "runner": "ubuntu-24.04-arm", "os": "ubuntu", "name": "ubuntu-24.04-arm" }, { "runner": "macos-13", "os": "macos", "name": "macos-13" }, { "runner": "macos-15", "os": "macos", "name": "macos-15" }, { "runner": "windows-2022", "os": "windows", "name": "windows-2022" }, { "runner": "windows-11-arm", "os": "windows", "name": "windows-11-arm" }, { "runner": "windows-latest", "os": "windows-msys2", "msys": "mingw32", "name": "mingw32" }, { "runner": "windows-latest", "os": "windows-msys2", "msys": "mingw64", "name": "mingw64" } ] pngcheck-4.0.1/pngcheck.1000066400000000000000000000023461511635702100151500ustar00rootroot00000000000000.TH PNGCHECK "1" "March 2025" "pngcheck 4.0.1" "User Commands" .SH NAME pngcheck \- manual page for pngcheck 4.0.1 .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 4.0.1 of 12 December 2025, by Alexander Lehmann, Andreas Dilger, Greg Roelofs and Chris Lilley. .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. pngcheck-4.0.1/pngcheck.c000066400000000000000000006141131511635702100152330ustar00rootroot00000000000000/* * 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-2025 by Alexander Lehmann , * Andreas Dilger , * Chris Lilley , * 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 "4.0.1" /* * 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 * * acTL fcTL fdAT // animated PNG * * cICP mDCV cLLI // PNG 3e * * caBX // PNG 4e * * 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()) * * print zTXt, compressed iTXt and iCCP chunks if -t option * (break out zlib decoder into separate function and reuse) * ? 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. */ /* * 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 #if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) # include #endif #include typedef unsigned char uch; typedef unsigned short ush; typedef unsigned long ulg; typedef signed char sch; typedef signed short ssh; typedef signed long slg; /* printbuf state variables */ typedef struct printbuf_state { int cr; int lf; int nul; int control; int esc; } printbuf_state; /* int main (int argc, const char *argv[]); */ void usage (FILE *fpMsg); ulg getlong (FILE *fp, const char *fname, const 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, const 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, const char *_fname, int searching, FILE *fpOut); int pnginfile (FILE *fp, const char *fname, int ipng, int extracting); void pngsearch (FILE *fp, const char *fname, int extracting); int check_magic (uch *magic, const char *fname, int which); int check_chunk_name (const char *chunk_name, const char *fname); int check_keyword (uch *buffer, int maxsize, int *pKeylen, const char *keyword_name, const char *chunkid, const char *fname); int check_text (uch *buffer, int maxsize, const char *chunkid, const char *fname); int check_ascii_float (uch *buffer, int len, const char *chunkid, const 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)) #define SSH(p) ((ssh)(uch)((p)[1]) | ((ssh)(sch)((p)[0]) << 8)) #define SLG(p) ((slg)(SH((p)+2)) | ((slg)(SSH(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 }; /* zlib stream processing variables */ int first_idat = 1; /* flag: is this the first IDAT chunk? */ int zlib_error = 0; /* gets 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; 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" }; 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 */ "", "", "", "", "", "", "", "" }; 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 unsigned 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, const 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; pass_color = pass_color_enabled; color_off = pass_color_enabled[0]; } else { brief_error = brief_error_plain; errors_detected = errors_plain; pass_color = pass_color_disabled; color_off = pass_color_disabled[0]; } if (argc == 1) { if (isatty(0)) { /* if stdin not redirected, give the user help */ usage(stdout); } else { const 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 { /* 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); } /* main loop over files listed on command line */ for (i = 1; i < argc; ++i) { const 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"); fprintf(fpMsg, " Compiled with zlib %s; using zlib %s.\n", ZLIB_VERSION, zlib_version); 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" " -vv test very verbosely (decode & print line filters)\n" " -w suppress windowBits test (a more-stringent compression check)\n" " -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); } # define CRCCOMPL(c) c # define CRCINIT (0) # define update_crc crc32 ulg getlong(FILE *fp, const char *fname, const 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, const 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, const char *fname, int searching, FILE *fpOut) { unsigned 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'}; const 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 have_acTL = 0, have_fcTL = 0; int have_cICP = 0, have_mDCV = 0, have_cLLI = 0; int have_caBX = 0; int have_iDOT = 0; int top_level = 1; // Animated PNG stuff ulg num_frames = 0L, num_plays = 0L, num_fcTL = 0L; int just_seen_fcTL = 0; ulg sequence_number = 0L, frame_width = 0L, frame_height = 0L; ulg x_offset = 0L, y_offset = 0L; unsigned int delay_num = 0, delay_den = 0; uch dispose_op = 0, blend_op = 0; ulg next_sequence_number = 0L; long segments = 0; ulg zhead = 1; /* 0x10000 indicates both zlib header bytes read */ ulg crc, filecrc; ulg layers = 0L, frames = 0L; ulg num_chunks = 0L; ulg w = 0L, h = 0L; ulg mng_width = 0L, mng_height = 0L; int vlc = -1, lc = -1; unsigned 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; 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; /*------* | 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) { const 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; zlib_error = 0; zlib_windowbits = 15; 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; if (check_windowbits) /* check for libpng 1.2.6 windowBits bug */ zlib_windowbits = CINFO + 8; 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); } } } } if (check_zlib && !zlib_error) { static uch *p; /* always points to next filter byte */ static unsigned 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; unsigned 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 */ unsigned int passm1, yskip=0, yoff=0, xoff=0; if (verbose) 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 zlib loop */ } /* now have uncompressed, filtered image data in outbuf */ eod = outbuf + BS - zstrm.avail_out; while (p < eod) { /* GRR 20210425: protect against run-on data, intentional or otherwise */ if ((lace && cur_pass > 7) || (!lace && cur_y > h)) { printf("%s extra data beyond end of image: possible exploit attempt\n", verbose > 1? "\n " : (verbose == 1? " " : fname)); zlib_error = 1; /* fatal error only for this PNG */ err = Z_STREAM_END; /* kill middle loop */ break; /* kill "innermost" loop (not counting short-image interlace one) */ } 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; /* 20210416: fix by Ben Beasley for bug found by chiba of topsec alpha lab */ if (cur_xskip == 0) { printf("%s invalid interlacing state (zero xskip) in image data\n", verbose > 1? "\n " : (verbose == 1? " " : fname)); zlib_error = 1; /* fatal error only for this PNG */ break; } /* 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 */ } if (zlib_error) { /* GRR 20210425: propagate error out of remaining loops */ err = Z_STREAM_END; /* kill middle loop */ break; /* kill "innermost" loop (not counting short-image interlace one) */ } } 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); int r = inflateEnd(&zstrm); /* we're all done */ if (r == Z_OK || r == Z_STREAM_ERROR) printf(" inflateEnd() returns %s\n ", r == Z_OK? "Z_OK" : "Z_STREAM_ERROR"); else printf(" inflateEnd() returns %d\n ", r); fflush(stdout); } else inflateEnd(&zstrm); /* we're all done */ zlib_error = -1; /* kill outermost loop (over consecutive PNG-mode IDAT chunks) */ err = Z_STREAM_END; /* kill middle loop */ break; /* kill "innermost" loop (not counting short-image interlace one) */ } } /* end of byte-loop over uncompressed data */ if (!zlib_error && no_err(kMinorError)) { 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; } } } /* end of zlib decoding loop */ 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); just_seen_fcTL = 0; 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); } else if (have_acTL && (num_frames != num_fcTL)) { printf("%s Expected %lu frames, but found %lu\n", verbose? ":":fname, num_frames, num_fcTL); set_err(kMinorError); } else if (have_acTL && just_seen_fcTL) { printf("%s Missing fdAT after fcTL\n", verbose? ":":fname); set_err(kMinorError); /* * 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)SLG(buffer)/100000; wy = (double)SLG(buffer+4)/100000; rx = (double)SLG(buffer+8)/100000; ry = (double)SLG(buffer+12)/100000; gx = (double)SLG(buffer+16)/100000; gy = (double)SLG(buffer+20)/100000; bx = (double)SLG(buffer+24)/100000; by = (double)SLG(buffer+28)/100000; if (verbose) { printf("\n"); } if (verbose && no_err(kMinorError)) { printf(" White x = %#6.5f y = %#6.5f, Red x = %#6.5f y = %#6.5f\n", wx, wy, rx, ry); printf(" Green x = %#6.5f y = %#6.5f, Blue x = %#6.5f y = %#6.5f\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 (png && have_IDAT) { printf("%s eXIf after IDAT no longer 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)) { const 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; /*------* | iDOT | *------*/ } else if (strcmp(chunkid, "iDOT") == 0) { if (!mng && have_iDOT) { printf("%s multiple iDOT not allowed\n", verbose? ":":fname); set_err(kMinorError); } else if (!mng && (have_IDAT || have_JDAT)) { printf("%s %smust precede %cDAT\n", verbose? ":":fname, verbose? "":"iDOT ", have_IDAT? 'I':'J'); set_err(kMinorError); } else if (sz < 16) { printf("%s invalid %slength (too short)\n", verbose? ":":fname, verbose? "":"iDOT "); set_err(kMinorError); } else { segments = LG(buffer); if (sz != 4 + segments * 12) { printf("%s invalid %slength\n", verbose? ":":fname, verbose? "":"iDOT "); set_err(kMinorError); } } if (verbose && no_err(kMinorError)) { printf(": Parallel segments: %ld\n", segments); } have_iDOT = 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]; unsigned 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; unsigned 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)) { const char *spc; unsigned 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]; /* zero-based; tIME month is 1-based */ int daysInMonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; /* leap year check, if needed */ if (mo == 2) { if (yr % 400 == 0) daysInMonth[1] = 29; else if (yr % 100 != 0) { if (yr % 4 == 0) daysInMonth[1] = 29; } } 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 (before PNG existed!) (%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 > daysInMonth[mo - 1]) { 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: PNG Third Edition prefers RFC 3339 format*/ /* 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)) { const 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; /*===========================================*/ /* Animated PNG chunks */ /*------* | acTL | *------*/ } else if (strcmp(chunkid, "acTL") == 0) { if (have_IDAT) { printf("%s acTL must be before first IDAT\n", verbose? ":":fname); set_err(kMinorError); } else if (have_acTL) { printf("%s multiple acTL not allowed\n", verbose? ":":fname); set_err(kMinorError); } else if (sz != 8) { printf("%s invalid %slength\n", verbose? ":":fname, verbose? "":"acTL "); set_err(kMinorError); } if (no_err(kMinorError)) { num_frames = LG(buffer); num_plays = LG(buffer+4); // printf(" Animated PNG, %d frames, %d plays\n", num_frames, num_plays); if (num_frames == 0) { printf("%s %snumber of frames cannot be zero \n", verbose? ":":fname, verbose? "":"acTL "); set_err(kMinorError); } else if (verbose) { if (num_plays == 0) { printf("\n Animated PNG, %lu frames, plays continuously\n", num_frames); } else { printf("\n Animated PNG, %lu frames, plays %lu times\n", num_frames, num_plays); } } } have_acTL = 1; last_is_IDAT = last_is_JDAT = 0; /*------* | fcTL | *------*/ } else if (strcmp(chunkid, "fcTL") == 0) { if (sz != 26) { printf("%s invalid %slength \n", verbose? ":":fname, verbose? "":"fcTL "); set_err(kMinorError); } if (no_err(kMinorError)) { sequence_number = LG(buffer); frame_width = LG(buffer+4); frame_height = LG(buffer+8); x_offset = LG(buffer+12); y_offset = LG(buffer+16); delay_num = SH(buffer+20); delay_den = SH(buffer+22); dispose_op = *(buffer+24); blend_op = *(buffer+25); } // First frame is IDAT checks if (!have_fcTL && !have_IDAT) { if (x_offset > 0 || y_offset > 0) { printf("%s First frame is IDAT, so offsets must be zero\n", verbose? ":":fname); set_err(kMinorError); } if (frame_height != h || frame_width != w) { printf("%s First frame is IDAT, so first frame must be same size as static image\n", verbose? ":":fname); set_err(kMinorError); } } // Missing fdAT check if (just_seen_fcTL) { printf("%s At least one fdAT is required for each frame (except first, if IDAT)\n", verbose? ":":fname); set_err(kMinorError); } // Sequence numbers if (!have_fcTL && (sequence_number != 0)) { // is this the first fcTL? printf("%s sequence numbers must start at zero\n", verbose? ":":fname); set_err(kMinorError); } else if (sequence_number != next_sequence_number){ // are sequence numbers increasing? printf("%s sequence numbers must be sequential\n", verbose? ":":fname); set_err(kMinorError); } else { next_sequence_number++; } // Widths, heights, and offsets if ((frame_width == 0) || (frame_height == 0)) { printf("%s frame width and height must not be zero\n", verbose? ":":fname); set_err(kMinorError); } if ((frame_width + x_offset > w) || (frame_height + y_offset > h)) { printf("%s frame must be rendered within image bounds\n", verbose? ":":fname); set_err(kMinorError); } // Delays if (delay_den == 0) { // If the denominator is 0, it is to be treated as if it were 100 delay_den = 100; } // Dispose if (dispose_op > 2) { printf("%s invalid dispose operator %u\n", verbose? ":":fname, dispose_op); set_err(kMinorError); } // Blend if (blend_op > 1) { printf("%s invalid blend operator %u\n", verbose? ":":fname, blend_op); set_err(kMinorError); } if (no_err(kMinorError) && verbose) { printf("\n Frame, sequence number %lu\n", sequence_number); printf(" Width %lu, height %lu starting at (%lu, %lu)\n", frame_width, frame_height, x_offset, y_offset); printf(" Frame delay %u (%u / %u) sec\n", delay_num/delay_den, delay_num, delay_den); if (dispose_op == 0) { printf(" No disposal before next frame\n"); } else if (dispose_op == 1) { printf(" Cleared to transparent black before next frame\n"); } else { printf(" Reverts to previous contents before next frame\n"); } if (blend_op == 0) { printf(" Frame overwrites buffer\n"); } else { printf(" Frame composites (source over) with buffer\n"); } } have_fcTL = 1; just_seen_fcTL = 1; num_fcTL++; last_is_IDAT = last_is_JDAT = 0; /*------* | fdAT | *------*/ } else if (strcmp(chunkid, "fdAT") == 0) { if (sz < 4) { printf("%s invalid %slength (must be at least 4 bytes)\n", verbose? ":":fname, verbose? "":"fdAT "); set_err(kMinorError); } if (no_err(kMinorError)) { sequence_number = LG(buffer); } if (sequence_number != next_sequence_number){ // are sequence numbers increasing? printf("%s sequence numbers must be sequential\n", verbose? ":":fname); set_err(kMinorError); } else { next_sequence_number++; } if (no_err(kMinorError) && verbose) { printf("\n Frame data, sequence number %lu\n", sequence_number); } just_seen_fcTL = 0; last_is_IDAT = last_is_JDAT = 0; /*===========================================*/ /* PNG Third Edition new chunks */ /*------* | cICP | *------*/ /* https://w3c.github.io/png/#cICP-chunk */ /* chunk ordering and chunk length tests */ } else if (strcmp(chunkid, "cICP") == 0) { if (!mng && have_cICP) { printf("%s multiple cICP not allowed\n", verbose? ":":fname); set_err(kMinorError); } else if (!mng && have_PLTE) { printf("%s %smust precede PLTE\n", verbose? ":":fname, verbose? "":"cICP "); set_err(kMinorError); } else if (!mng && (have_IDAT || have_JDAT)) { printf("%s %smust precede %cDAT\n", verbose? ":":fname, verbose? "":"cICP ", have_IDAT? 'I':'J'); set_err(kMinorError); } else if (sz != 4) { printf("%s invalid %slength\n", verbose? ":":fname, verbose? "":"cICP "); set_err(kMajorError); } if (no_err(kMinorError)) { char primaries, transfer, fullrange; /* check for obviously wrong values first */ if (buffer[2] > 0) { printf("%s %s matrix coefficients must be zero (RGB), found %d\n", verbose? ":":fname, verbose? "":"cICP ", buffer[2]); set_err(kMinorError); } else if (buffer[3] > 1) { printf("%s %s invalid video full range flag, found %d\n", verbose? ":":fname, verbose? "":"cICP ", buffer[3]); set_err(kMinorError); } else if (buffer[0] > 22) { printf("%s %s reserved color primaries, found %d\n", verbose? ":":fname, verbose? "":"cICP ", buffer[0]); set_err(kMinorError); } else if (buffer[1] > 18) { printf("%s %s reserved transfer characteristics, found %d\n", verbose? ":":fname, verbose? "":"cICP ", buffer[0]); set_err(kMinorError); } else { primaries = buffer[0]; transfer = buffer[1]; fullrange = buffer [3]; double wx, wy, rx, ry, gx, gy, bx, by; if (primaries == 1) { if (transfer == 1) { // 709 printf("\n%s %s Rec. ITU-R BT.709-6 \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 8) { // linear printf("\n%s %s linear-light sRGB \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 13) { // sRGB printf("\n%s %s IEC 61966-2-1 sRGB \n", verbose? "":fname, verbose? "":"cICP "); } else { // unknown transfer function for these primaries printf("\n%s %s unknown sRGB-like \n", verbose? "":fname, verbose? "":"cICP "); set_err(kMinorError); } wx = 0.3127; wy = 0.3290; rx = 0.640; ry = 0.330; gx = 0.300; gy = 0.600; bx = 0.150; by = 0.060; } else if (primaries == 5) { if (transfer == 4) { // SECAM printf("\n%s %s Rec. ITU-R BT.1700-0 625 (SECAM) \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 6) { // PAL printf("\n%s %s Rec. ITU-R BT.601-7 625 (PAL) \n", verbose? "":fname, verbose? "":"cICP "); } else { // unknown transfer function for these primaries printf("\n%s %s unknown 625-line PAL-like \n", verbose? "":fname, verbose? "":"cICP "); set_err(kMinorError); } wx = 0.3127; wy = 0.3290; rx = 0.640; ry = 0.330; gx = 0.290; gy = 0.600; bx = 0.150; by = 0.060; } else if (primaries == 6) { if (transfer == 6) { // NTSC printf("\n%s %s Rec. ITU-R BT.601-7 525 (NTSC) \n", verbose? "":fname, verbose? "":"cICP "); } else { // unknown transfer function for these primaries printf("\n%s %s unknown 525-line NTSC-like \n", verbose? "":fname, verbose? "":"cICP "); set_err(kMinorError); } wx = 0.3127; wy = 0.3290; rx = 0.630; ry = 0.340; gx = 0.310; gy = 0.595; bx = 0.155; by = 0.070; } else if (primaries == 9) { if (transfer == 14) { // 2020 10-bit printf("\n%s %s Rec. ITU-R BT.2020-2 (10-bit system) \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 15) { // 2020 12-bit printf("\n%s %s Rec. ITU-R BT.2020-2 (12-bit system) \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 16) { // PQ printf("\n%s %s Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 18) { // HLG printf("\n%s %s Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system \n", verbose? "":fname, verbose? "":"cICP "); } else { // unknown transfer function for these primaries printf("\n%s %s unknown rec2020-like \n", verbose? "":fname, verbose? "":"cICP "); set_err(kMinorError); } wx = 0.3127; wy = 0.3290; rx = 0.708; ry = 0.292; gx = 0.170; gy = 0.797; bx = 0.131; by = 0.046; } else if (primaries == 11) { // DCI P3 if (transfer == 17) { printf("\n%s %s SMPTE RP 431-2 with SMPTE ST 428-1 D-Cinema Distribution Master (DCI-P3) \n", verbose? "":fname, verbose? "":"cICP "); } else { // unknown transfer function for these primaries printf("\n%s %s unknown DCI-P3-like \n", verbose? "":fname, verbose? "":"cICP "); set_err(kMinorError); } wx = 0.314; wy = 0.351; rx = 0.680; ry = 0.320; gx = 0.265; gy = 0.690; bx = 0.150; by = 0.060; } else if (primaries == 12) { // P3D65 if (transfer == 13) { // Display P3 uses the sRGB transfer function printf("\n%s %s Display P3 \n", verbose? "":fname, verbose? "":"cICP "); } else if (transfer == 16) { //P3D65-PQ printf("\n%s %s P3D65-PQ \n", verbose? "":fname, verbose? "":"cICP "); } else { // unknown transfer function for these primaries printf("\n%s %s unknown D65 P3-like \n", verbose? "":fname, verbose? "":"cICP "); set_err(kMinorError); } wx = 0.3127; wy = 0.3290; rx = 0.680; ry = 0.320; gx = 0.265; gy = 0.690; bx = 0.150; by = 0.060; } else { // mystery meat if (verbose) { printf("\n unrecognised (reserved or historical)\n"); printf(" primaries: %d \n", primaries); printf(" transfer function: %d \n", transfer); printf(fullrange? " Narrow range \n" : " Full range \n"); set_err(kMinorError); } } 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); // printf(fullrange? " Narrow range \n" : " Full range \n"); if (fullrange == 0) printf(" Narrow range \n"); if (fullrange == 1) printf(" Full range \n"); } have_cICP = 1; last_is_IDAT = last_is_JDAT = 0; } } /*------* | mDCV | *------*/ /* https://w3c.github.io/png/#mDCV-chunk */ } else if (strcmp(chunkid, "mDCV") == 0) { if (!mng && have_mDCV) { printf("%s multiple mDCV not allowed\n", verbose? ":":fname); set_err(kMinorError); } else if (!mng && have_PLTE) { printf("%s %smust precede PLTE\n", verbose? ":":fname, verbose? "":"mDCV "); set_err(kMinorError); } else if (!mng && (have_IDAT || have_JDAT)) { printf("%s %smust precede %cDAT\n", verbose? ":":fname, verbose? "":"mDCV ", have_IDAT? 'I':'J'); set_err(kMinorError); } else if (sz != 24) { printf("%s invalid %slength\n", verbose? ":":fname, verbose? "":"mDCV "); set_err(kMajorError); } if (no_err(kMinorError)) { double rx, ry, gx, gy, bx, by, wx, wy, maxlum, minlum; /* notice different order, length, and divisor, compared to cHRM */ rx = (double)SH(buffer)/50000; ry = (double)SH(buffer+2)/50000; gx = (double)SH(buffer+4)/50000; gy = (double)SH(buffer+6)/50000; bx = (double)SH(buffer+8)/50000; by = (double)SH(buffer+10)/50000; wx = (double)SH(buffer+12)/50000; wy = (double)SH(buffer+14)/50000; maxlum = (double)LG(buffer+16)/10000; minlum = (double)LG(buffer+20)/10000; if (wx < 0 || wx > 0.8 || wy < 0 || wy > 0.8 || wx + wy > 1.0) { printf("%s invalid mastering %swhite point %0g %0g\n", verbose? ":":fname, verbose? "":"mDCV ", wx, wy); set_err(kMinorError); } else if (rx < 0 || rx > 0.8 || ry < 0 || ry > 0.8 || rx + ry > 1.0) { printf("%s invalid mastering %sred point %0g %0g\n", verbose? ":":fname, verbose? "":"mDCV ", rx, ry); set_err(kMinorError); } else if (gx < 0 || gx > 0.8 || gy < 0 || gy > 0.8 || gx + gy > 1.0) { printf("%s invalid mastering %sgreen point %0g %0g\n", verbose? ":":fname, verbose? "":"mDCV ", gx, gy); set_err(kMinorError); } else if (bx < 0 || bx > 0.8 || by < 0 || by > 0.8 || bx + by > 1.0) { printf("%s invalid mastering %sblue point %0g %0g\n", verbose? ":":fname, verbose? "":"mDCV ", bx, by); set_err(kMinorError); } else if (maxlum > 10000) { printf("%s invalid mastering %smax luminance %0g cd/m^2\n", verbose? ":":fname, verbose? "":"mDCV ", maxlum); } else if (minlum > 10) { printf("%s invalid mastering %smin luminance %0g cd/m^2\n", verbose? ":":fname, verbose? "":"mDCV ", minlum); } else if (verbose) { printf("\n Mastering Display\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); printf(" Maximum luminance = %0g cd/m^2\n", maxlum); printf(" Minimum luminance = %0g cd/m^2\n", minlum); } } have_mDCV = 1; last_is_IDAT = last_is_JDAT = 0; /*------* | cLLI | *------*/ /* https://w3c.github.io/png/#cLLI-chunk */ } else if (strcmp(chunkid, "cLLI") == 0) { if (!mng && have_cLLI) { printf("%s multiple cLLI not allowed\n", verbose? ":":fname); set_err(kMinorError); } else if (!mng && have_PLTE) { printf("%s %smust precede PLTE\n", verbose? ":":fname, verbose? "":"cLLI "); set_err(kMinorError); } else if (!mng && (have_IDAT || have_JDAT)) { printf("%s %smust precede %cDAT\n", verbose? ":":fname, verbose? "":"cLLI ", have_IDAT? 'I':'J'); set_err(kMinorError); } else if (sz != 8) { printf("%s invalid %slength\n", verbose? ":":fname, verbose? "":"cLLI "); set_err(kMajorError); } if (no_err(kMinorError)) { ulg cll, fall; double maxCLL, maxFALL; cll = LG(buffer); fall = LG(buffer+4); maxCLL = (double)cll/10000; maxFALL = (double)fall/10000; if (maxCLL > 10000) { printf("%s invalid %smaximum light level %0g\n", verbose? ":":fname, verbose? "":"cLLI ", maxCLL); set_err(kMinorError); } else if (verbose) { printf("\n"); } if (verbose && no_err(kMinorError)) { printf(" Maximum content light level "); if (!cll) { printf("unknown\n"); } else { printf("= %0g cd/m^2\n", maxCLL); } printf(" Maximum frame average light level "); if (!fall) { printf("unknown\n"); } else { printf("= %0g cd/m^2\n", maxFALL); } } } have_cLLI = 1; last_is_IDAT = last_is_JDAT = 0; /*------* | caBX | *------*/ /* PNG 4th Edition https://w3c.github.io/png/#caBX */ } else if (strcmp(chunkid, "caBX") == 0) { if (!mng && (have_IDAT || have_JDAT)) { printf("%s %smust precede %cDAT\n", verbose? ":":fname, verbose? "":"caBX ", have_IDAT? 'I':'J'); set_err(kMinorError); } else if (verbose) { printf("\n Content Credentials\n"); } have_caBX = 1; last_is_IDAT = last_is_JDAT = 0; /*------* | cLLi | *------*/ } else if (strcmp(chunkid, "cLLi") == 0) { if (verbose) printf("\n " "Old version of CLLI, do not use\n"); set_err(kMinorError); last_is_IDAT = last_is_JDAT = 0; /*------* | mDCv | *------*/ } else if (strcmp(chunkid, "mDCv") == 0) { if (verbose) printf("\n " "Old version of mDCV, do not use\n"); set_err(kMinorError); 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; 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; /*------* | 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 { const 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]; ulg xoff = LG(buf+5); ulg 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((const 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((const 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) { const 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((const 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); } else { if (have_mDCV && !have_cICP) { printf("%s file has %smDCV%s, but does not have %scICP%s\n", verbose? "":fname, color? COLOR_YELLOW:"", color? COLOR_NORMAL:"", color? COLOR_YELLOW:"", color? COLOR_NORMAL:""); 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) { const 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 { const 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, ", 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-"); if (have_acTL) { printf("animated (%lu frame%s), ", num_frames, (num_frames>1)? "s" : ""); } else { printf("static, "); } printf("%s%d.%d%%).\n", sgn, cfactor/10, cfactor%10); } } } return global_error; } /* end function pngcheck() */ int pnginfile(FILE *fp, const 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, const 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, const 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((const char *)&magic[4], "\012\032", 2) == 0) printf("DOS->Unix"); else if (strncmp((const char *)&magic[4], "\015\032", 2) == 0) printf("DOS->Mac"); else if (strncmp((const char *)&magic[4], "\015\015\032", 3) == 0) printf("Unix->Mac"); else if (strncmp((const char *)&magic[4], "\012\012\032", 3) == 0) printf("Mac->Unix"); else if (strncmp((const char *)&magic[4], "\012\012", 2) == 0) printf("DOS->Unix"); else if (strncmp((const char *)&magic[4], "\015\015\012\032", 4) == 0) printf("Unix->DOS"); else if (strncmp((const char *)&magic[4], "\015\012\032\015", 4) == 0) printf("Unix->DOS"); else if (strncmp((const char *)&magic[4], "\015\012\012", 3) == 0) printf("DOS EOF"); else if (strncmp((const 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(const char *chunk_name, const 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, const char *keyword_name, const char *chunkid, const 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, const char *chunkid, const 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, const char *chunkid, const 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-4.0.1/third_party/000077500000000000000000000000001511635702100156305ustar00rootroot00000000000000pngcheck-4.0.1/third_party/wildargs/000077500000000000000000000000001511635702100174445ustar00rootroot00000000000000pngcheck-4.0.1/third_party/wildargs/LICENSE_BSL_1_0.txt000066400000000000000000000024721511635702100224330ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pngcheck-4.0.1/third_party/wildargs/README.md000066400000000000000000000015141511635702100207240ustar00rootroot00000000000000wildargs ======== Automatic command-line wildcard expansion for platforms and environments that aren't based on the Un\*x shell. Usage ----- Simply compile and link the `wildargs.c` module to the rest of the program: cl -O2 wildargs_test.c wildargs.c Run the program and check the expansion of wildcard arguments. In this example, you should see a listing of the `*.c` files: wildargs_test *.c Compatibility ------------- The following compilers and runtimes are currently supported on Windows: * Microsoft Visual C++, supporting MSVCRT and UCRT+VCRUNTIME; * MinGW/MinGW32 and MinGW-w64, supporting MSVCRT and UCRT; * Borland (Embarcadero) C/C++, supporting Borland RTL. On Unix and Unix-like systems, the wildargs module is a no-op. Supporting other compilers, runtimes, and even platforms other than Windows, is TODO. pngcheck-4.0.1/third_party/wildargs/wildargs.c000066400000000000000000000044151511635702100214300ustar00rootroot00000000000000/* * wildargs.c * Automatic command-line wildcard expansion for * environments that aren't based on the Un*x shell. * * Copyright (C) 2003-2025 Cosmin Truta. * * Use, modification and distribution are subject * to the Boost Software License, Version 1.0. * See the accompanying file LICENSE_BSL_1_0.txt * or visit https://www.boost.org/LICENSE_1_0.txt * * SPDX-License-Identifier: BSL-1.0 */ #if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) /* * Compiler and runtime detection on Windows. * * NOTE: * Some compilers for Windows claim to have a MSC version number * without actually being Microsoft C/C++ compilers. To ensure a * proper detection of the actual runtime, the MSC version checks * should be performed last. */ #if defined(__BORLANDC__) #define WILDARGS_WINDOWS_BORLANDRTL #elif defined(__MINGW64__) || defined(__MINGW32__) #define WILDARGS_WINDOWS_MINGW #elif defined(_MSC_VER) && (_MSC_VER >= 1900) #define WILDARGS_WINDOWS_VCRUNTIME #elif defined(_MSC_VER) && (_MSC_VER >= 800) #define WILDARGS_WINDOWS_MSVCRT #endif #endif #if defined(WILDARGS_WINDOWS_BORLANDRTL) /* * Automatic wildargs expansion for the Borland Runtime Library (RTL). * The implementation is inspired from BMP2PNG by MIYASAKA Masaru. */ #include typedef void _RTLENTRY (* _RTLENTRY _argv_expand_fn)(char *, _PFN_ADDARG); typedef void _RTLENTRY (* _RTLENTRY _wargv_expand_fn)(wchar_t *, _PFN_ADDARG); _argv_expand_fn _argv_expand_ptr = _expand_wild; _wargv_expand_fn _wargv_expand_ptr = _wexpand_wild; #elif defined(WILDARGS_WINDOWS_MINGW) || defined(WILDARGS_WINDOWS_MSVCRT) /* * Automatic wildargs expansion for MinGW32, MinGW-w64 and MSVCRT. * The implementation is inspired from MinGW32 by Colin Peters. */ int _dowildcard = 1; #elif defined(WILDARGS_WINDOWS_VCRUNTIME) /* * Automatic wildargs expansion for VCRUNTIME, used by the modern * versions of Microsoft Visual C++ (from Visual C++ 2015 onwards). * The implementation is inspired from the VCRUNTIME source code. */ #include _crt_argv_mode __CRTDECL _get_startup_argv_mode(void) { return _crt_argv_expanded_arguments; } #else /* * Dummy declaration for a guaranteed non-empty translation unit. */ #ifndef __cplusplus typedef int wildargs_dummy_t; #endif #endif pngcheck-4.0.1/third_party/wildargs/wildargs_test.c000066400000000000000000000007521511635702100224670ustar00rootroot00000000000000/* * wildargs_test.c * Test program to be linked with wildargs.c * * Copyright (C) 2003-2025 Cosmin Truta. * * Use, modification and distribution are subject * to the Boost Software License, Version 1.0. * See the accompanying file LICENSE_BSL_1_0.txt * or visit https://www.boost.org/LICENSE_1_0.txt * * SPDX-License-Identifier: BSL-1.0 */ #include int main(int argc, char *argv[]) { int i; for (i = 1; i < argc; ++i) puts(argv[i]); return 0; }