# giflib 5.2.2 — the GIF codec library and the six tools upstream installs. # # WHAT MAKES THIS SUITE DIFFERENT FROM THE FOUR CODEC SUITES BESIDE IT. libpng, # libjpeg-turbo, libwebp and tiff each had to reason about whether a decode is # reproducible across machines, and the audio ports next door (57-libvorbis, # 57-speexdsp, 57-mpg123) each refused a stored digest outright. GIF needs none # of that argument: the whole decode is LZW over a byte table followed by three # palette bytes per pixel — no float, no libm, nothing for a compiler to # reassociate. So the oracle here is the strongest form available, `cmp` against # a reference RGB dump produced by an x86-64 build, and it is used without # qualification. # # Three assertions need NO stored reference at all, and they are the ones that # would survive even a wrong reference file: # giflib/interlace-same-pixels the interlaced fixture and the plain one hold # the same image in different row orders, so # their decodes must be byte-equal. # giflib/rebuild-body-identical `gifbuild -d` then `gifbuild` reproduces the # original from byte 7 on — LZW stream included. # giflib/giftool-*-pixels giftool re-orders rows on target and the # decode is unchanged. # # NO COMPILED PROBE, AND THAT IS THE dtc RULE RATHER THAN AN OMISSION. The forty # -staticdev suites that ship a probe do so because their port installs no # program that links the archive. Here six do — gif2rgb, gifbuild, giftext, # gifclrmp, giffix and giftool are libgif.a linked and run, covering the decode # path, the encode path, the structure walk, the colour-map path, the # error-recovery path and the property-edit path. A probe would re-link the same # archive to re-ask the same question. # # BST-TIMEOUT: 300 . "$BST_LIB" require gif2rgb giflib/present tmp=${TMPDIR:-/tmp}/bst-giflib.$$ mkdir -p "$tmp" 2>/dev/null GRID=$BST_DATA/giflib-grid.gif ILACE=$BST_DATA/giflib-interlaced.gif ANIM=$BST_DATA/giflib-anim.gif NARROW=$BST_DATA/giflib-narrowmap.gif TRUNC=$BST_DATA/giflib-trunc.gif REF=$BST_DATA/giflib-grid.ref.rgb # `wc -c /dev/null | tr -d ' '; } # has — substring assertion against a file already written, # for the cases where one command's output feeds several checks and re-running it # per needle would be the expensive part of the suite. has() { case "$(cat "$2" 2>/dev/null)" in *"$3"*) bst_ok "$1" ;; *) bst_notok "$1" "missing: $3" ;; esac } # ------------------------------------------------------------- 1. what shipped for t in gifbuild giftext gifclrmp giffix giftool; do if command -v "$t" >/dev/null 2>&1; then bst_ok "giflib/tool-$t" else bst_notok "giflib/tool-$t" "not installed (giflib-utils ships six programs)"; fi done check giflib/staticlib test -f /usr/lib/libgif.a check giflib/header test -f /usr/include/gif_lib.h # The distro is static-only: a shared object here would mean the bbappend's # do_compile had gone back to upstream's `all` target, which links one. if [ -n "$(ls /usr/lib/libgif.so* 2>/dev/null)" ]; then bst_notok giflib/no-shared "libgif.so present on a static-only distro" else bst_ok giflib/no-shared fi # libutil.a holds the argument parser the six tools share; upstream installs # neither it nor getarg.h, and neither does this port. check giflib/no-libutil test ! -f /usr/lib/libutil.a check giflib/no-getarg-h test ! -f /usr/include/getarg.h # THE MAN PAGES ARE BUILT CORRECTLY AND ARE NOT ON THIS IMAGE, WHICH IS TWO # SEPARATE FACTS. Upstream's install-man installs doc/*.xml -- the DocBook # SOURCES -- into man1, where no man(1) can render them; the bbappend installs # the pre-generated .1/.7 pages instead, and they package into giflib-doc. # EMCRAFT_TOOLS does not name giflib-doc, because this image installs no port's # -doc package (man-pages is the Linux man-pages project, not per-tool docs), and # one port shipping them alone would be an unexplained singularity. So the only # thing worth asserting on target is the negative: whatever a future image # decides about -doc, upstream's XML must never land in man1. check giflib/man-no-xml test ! -f /usr/share/man/man1/gif2rgb.xml check giflib/man-no-doc test ! -f /usr/share/man/man1/gif2rgb.1 check_out giflib/version "Version 5.2" gif2rgb -h # ------------------------------------------------------- 2. the fixtures exist check giflib/fixture-grid test -f "$GRID" check giflib/fixture-interlace test -f "$ILACE" check giflib/fixture-anim test -f "$ANIM" check giflib/fixture-narrowmap test -f "$NARROW" check giflib/fixture-trunc test -f "$TRUNC" check giflib/fixture-ref test -f "$REF" check_eq giflib/fixture-ref-size 9216 "$(size "$REF")" # ------------------------------------------------ 3. gif2rgb: the decode oracle check giflib/decode-rc gif2rgb -1 -o "$tmp/grid.rgb" "$GRID" check_eq giflib/decode-size 9216 "$(size "$tmp/grid.rgb")" check giflib/decode-vs-ref cmp "$tmp/grid.rgb" "$REF" # Pixels 0-1 are palette entry 0 (black) and pixel 8 is entry 1 (red): a channel # swap or an off-by-one in the palette lookup lands on a named case rather than # inside the cmp above, where it would say only "differ: byte 1". check_eq giflib/pixel-0-1 "000000000000" \ "$(od -An -N6 -tx1 "$tmp/grid.rgb" 2>/dev/null | tr -d ' \n')" check_eq giflib/pixel-8 "ff0000" \ "$(dd if="$tmp/grid.rgb" bs=1 skip=24 count=3 2>/dev/null | od -An -tx1 | tr -d ' \n')" # Interlaced storage, identical image. No reference file, no tolerance. check giflib/interlace-rc gif2rgb -1 -o "$tmp/ilace.rgb" "$ILACE" check giflib/interlace-same-pixels cmp "$tmp/ilace.rgb" "$tmp/grid.rgb" # The two-frame fixture composites onto one screen; frame 2's local table has # eight entries, so every index frame 1 painted is in range for it. check giflib/anim-decode-rc gif2rgb -1 -o "$tmp/anim.rgb" "$ANIM" check_eq giflib/anim-decode-size 9216 "$(size "$tmp/anim.rgb")" if cmp -s "$tmp/anim.rgb" "$tmp/grid.rgb"; then bst_notok giflib/anim-overpaints "frame 2 left the screen unchanged" else bst_ok giflib/anim-overpaints fi # ------------------------------------------------ 4. giftext: structure report giftext "$GRID" > "$tmp/text.out" 2>&1 check_eq giflib/giftext-rc 0 "$?" has giflib/giftext-screen "$tmp/text.out" "Screen Size - Width = 64, Height = 48." has giflib/giftext-depth "$tmp/text.out" "ColorResolution = 3, BitsPerPixel = 3, BackGround = 0" has giflib/giftext-globalmap "$tmp/text.out" "Has Global Color Map." has giflib/giftext-imagesize "$tmp/text.out" "Image Size - Left = 0, Top = 0, Width = 64, Height = 48." has giflib/giftext-noilace "$tmp/text.out" "Image is Non Interlaced." has giflib/giftext-nolocal "$tmp/text.out" "No Image Color Map." has giflib/giftext-terminate "$tmp/text.out" "GIF file terminated normally." check_out giflib/giftext-interlaced "Image is Interlaced." giftext "$ILACE" # THE COMMENT CASE IS WHAT THE bbappend's PATCH EXISTS FOR. Stock giftext tells # snprintf its destination is three bytes when the last column of its 16-wide # ASCII gutter has two, and __snprintf_chk aborts on the DECLARED bound rather # than on what gets written — so a _FORTIFY_SOURCE=3 build dies here having # printed nothing. This image builds without fortify (upstream's makefile assigns # CFLAGS outright and drops bitbake's), so this case would pass unpatched TODAY. # It is what goes red if that ever changes and the patch has been lost. giftext "$ANIM" > "$tmp/anim.txt" 2>&1 check_eq giflib/giftext-comment-rc 0 "$?" has giflib/giftext-comment-text "$tmp/anim.txt" "brintOS giflib f" has giflib/ext-comment "$tmp/anim.txt" "GIF89 comment" has giflib/ext-gcb "$tmp/anim.txt" "GIF89 graphics control" has giflib/ext-delay-10 "$tmp/anim.txt" "DelayTime: 10" has giflib/ext-delay-20 "$tmp/anim.txt" "DelayTime: 20" has giflib/ext-transparent "$tmp/anim.txt" "Transparency on: yes" has giflib/ext-trans-index "$tmp/anim.txt" "Transparent Index: 5" has giflib/ext-disposal "$tmp/anim.txt" "Disposal Mode: 2" has giflib/ext-frame2-geom "$tmp/anim.txt" "Image Size - Left = 8, Top = 8, Width = 16, Height = 16." has giflib/ext-localmap "$tmp/anim.txt" "Image Has Color Map." # giftext appends BitsPerPixel to the interlace line only for an image that # carries a local table, so this line is the local map's depth and not the # screen's — both are 3 here, deliberately, and the two are asserted apart. has giflib/ext-local-depth "$tmp/anim.txt" "Image is Non Interlaced, BitsPerPixel = 3." # ------------------------------- 5. gifbuild: the round trip the ticket asks for gifbuild -d "$GRID" > "$tmp/dump.txt" 2>/dev/null check_eq giflib/dump-rc 0 "$?" has giflib/dump-screen "$tmp/dump.txt" "screen width 64" has giflib/dump-height "$tmp/dump.txt" "screen height 48" has giflib/dump-colors "$tmp/dump.txt" "screen colors 8" has giflib/dump-map "$tmp/dump.txt" "rgb 255 255 255 is 7" gifbuild "$tmp/dump.txt" > "$tmp/rebuilt.gif" 2>/dev/null check_eq giflib/rebuild-rc 0 "$?" check_eq giflib/rebuild-size "$(size "$GRID")" "$(size "$tmp/rebuilt.gif")" # GIF IS LOSSLESS AND THE REBUILD SHOWS IT AT THE BYTE LEVEL, NOT MERELY AT THE # PIXEL LEVEL: from byte 7 on — the logical screen descriptor, the global colour # table, the image descriptor and the entire LZW stream — the rebuilt file is # identical to the original. The one byte that differs is the version signature: # every giflib writer emits GIF87a for a file carrying no 89a feature, and the # fixture is written GIF89a. Pinned from both directions rather than smoothed # over, because anyone diffing the two files will meet it. tail -c +7 "$GRID" > "$tmp/orig.body" 2>/dev/null tail -c +7 "$tmp/rebuilt.gif" > "$tmp/rebuilt.body" 2>/dev/null check giflib/rebuild-body-identical cmp "$tmp/orig.body" "$tmp/rebuilt.body" check_eq giflib/orig-signature "GIF89a" "$(head -c 6 "$GRID")" check_eq giflib/rebuild-signature "GIF87a" "$(head -c 6 "$tmp/rebuilt.gif")" check giflib/rebuild-decode-rc gif2rgb -1 -o "$tmp/rebuilt.rgb" "$tmp/rebuilt.gif" check giflib/rebuild-vs-ref cmp "$tmp/rebuilt.rgb" "$REF" # The same trip for the interlaced fixture: the flag survives the text form. gifbuild -d "$ILACE" > "$tmp/idump.txt" 2>/dev/null has giflib/dump-interlaced "$tmp/idump.txt" "interlaced" gifbuild "$tmp/idump.txt" > "$tmp/irebuilt.gif" 2>/dev/null check giflib/irebuild-decode gif2rgb -1 -o "$tmp/irebuilt.rgb" "$tmp/irebuilt.gif" check giflib/irebuild-vs-ref cmp "$tmp/irebuilt.rgb" "$REF" # --------------------------------------------------------- 6. gifclrmp: palette gifclrmp -s "$GRID" > "$tmp/cmap.txt" 2>/dev/null check_eq giflib/clrmp-rc 0 "$?" check_eq giflib/clrmp-lines 8 "$(wc -l < "$tmp/cmap.txt" | tr -d ' ')" check_eq giflib/clrmp-entry0 "0 0 0 0" \ "$(sed -n '1p' "$tmp/cmap.txt" | awk '{print $1, $2, $3, $4}')" check_eq giflib/clrmp-entry7 "7 255 255 255" \ "$(sed -n '8p' "$tmp/cmap.txt" | awk '{print $1, $2, $3, $4}')" # Feed the dumped map straight back: save/load must be a no-op on the pixels. gifclrmp -l "$tmp/cmap.txt" "$GRID" > "$tmp/cl.gif" 2>/dev/null check giflib/clrmp-load-decode gif2rgb -1 -o "$tmp/cl.rgb" "$tmp/cl.gif" check giflib/clrmp-load-vs-ref cmp "$tmp/cl.rgb" "$REF" # ------------------------------------------------------ 7. giffix: the recovery # The fixture is giflib-grid.gif cut in half, so the header and the global table # survive and the LZW stream does not. giffix writes out what it could read and # EXITS 1 while doing so — upstream reports the ignored error in the status, not # only on stderr — so a suite written to the usual convention would be red # against a correct binary. Pinned as rc=1 for that reason. giffix "$TRUNC" > "$tmp/fixed.gif" 2>"$tmp/fixed.err" check_eq giflib/giffix-rc 1 "$?" has giflib/giffix-reports "$tmp/fixed.err" "error occurred (and ignored)" if [ "$(size "$tmp/fixed.gif")" -gt 0 ] 2>/dev/null; then bst_ok giflib/giffix-output else bst_notok giflib/giffix-output "giffix wrote nothing"; fi # The repaired file is a complete, decodable GIF of the original screen size. check giflib/giffix-decode gif2rgb -1 -o "$tmp/fixed.rgb" "$tmp/fixed.gif" check_eq giflib/giffix-decode-size 9216 "$(size "$tmp/fixed.rgb")" check_out giflib/giffix-terminates "GIF file terminated normally." giftext "$tmp/fixed.gif" # The truncated original is NOT decodable, or giffix would be proving nothing. check_rc giflib/trunc-undecodable 1 gif2rgb -1 -o "$tmp/nope.rgb" "$TRUNC" # ------------------------------------------- 8. giftool: the property editor # giftool is a stdin->stdout filter, unlike the other five, and its -i takes an # argument (its getopt string is "a:b:d:f:i:n:p:s:u:x:"). giftool -i on < "$GRID" > "$tmp/ion.gif" 2>/dev/null check_eq giflib/giftool-on-rc 0 "$?" check_out giflib/giftool-on-flag "Image is Interlaced." giftext "$tmp/ion.gif" check giflib/giftool-on-decode gif2rgb -1 -o "$tmp/ion.rgb" "$tmp/ion.gif" check giflib/giftool-on-pixels cmp "$tmp/ion.rgb" "$REF" giftool -i off < "$ILACE" > "$tmp/ioff.gif" 2>/dev/null check_eq giflib/giftool-off-rc 0 "$?" check_out giflib/giftool-off-flag "Image is Non Interlaced." giftext "$tmp/ioff.gif" check giflib/giftool-off-decode gif2rgb -1 -o "$tmp/ioff.rgb" "$tmp/ioff.gif" check giflib/giftool-off-pixels cmp "$tmp/ioff.rgb" "$REF" # Deinterlacing the interlaced fixture reproduces the plain one from byte 7 on — # the same signature-only difference the gifbuild round trip shows. tail -c +7 "$tmp/ioff.gif" > "$tmp/ioff.body" 2>/dev/null check giflib/giftool-off-body cmp "$tmp/orig.body" "$tmp/ioff.body" # ---- the extension-block loss, and the three cases that bound it ------------- # ON THIS ARM giftool WRITES NO EXTENSION BLOCKS. It is the one tool here that # goes through DGifSlurp -> GifMakeSavedImage -> EGifSpew rather than the # streaming API, and on wasm32-hwjs that path drops every comment record and # every graphic control block -- silently, exit status 0, valid GIF out. The # same source on x86-64 preserves them. Filed as # linux-in-the-browser#156; no workaround is applied, because the affected # surface is exactly giftool's extension-editing options and nothing else in the # package touches that path. # # The next two cases are the ATTRIBUTED REDS. They are written to go green on # their own the day #156 is fixed -- neither asserts the broken behaviour, both # assert the correct one and carry the issue in the detail. giftool -d 42 < "$GRID" > "$tmp/delay.gif" 2>/dev/null case "$(gifbuild -d "$tmp/delay.gif" 2>&1)" in *"delay 42"*) bst_ok giflib/giftool-delay ;; *) bst_notok giflib/giftool-delay \ "GIFEXT-LOST (linux-in-the-browser#156): no graphic control block written; $(size "$GRID") in, $(size "$tmp/delay.gif") out (x86-64: 489 -> 497 carrying delay 42)" ;; esac giftool < "$ANIM" > "$tmp/gtcopy.gif" 2>/dev/null if [ "$(size "$tmp/gtcopy.gif")" = "$(size "$ANIM")" ]; then bst_ok giflib/giftool-copy-ext else bst_notok giflib/giftool-copy-ext \ "GIFEXT-LOST (linux-in-the-browser#156): a no-op giftool copy lost $(( $(size "$ANIM") - $(size "$tmp/gtcopy.gif") )) bytes -- the comment record and both graphic control blocks" fi # THE THREE CASES BELOW ARE THE OTHER HALF OF THAT REPORT AND ARE GREEN. They # are what makes the two reds above a bounded platform gap rather than "giflib # is broken here": the reader collects the blocks correctly, and the streaming # writer emits them correctly. Only the slurp->spew path loses them. If one of # these ever goes red the defect has grown and #156 needs re-scoping. giftool -f 'gcb delay=%d disposal=%x ' < "$ANIM" > "$tmp/gcb.txt" 2>/dev/null has giflib/giftool-reads-gcb1 "$tmp/gcb.txt" "gcb delay=10 disposal=0" has giflib/giftool-reads-gcb2 "$tmp/gcb.txt" "gcb delay=20 disposal=2" gifbuild -d "$ANIM" > "$tmp/adump.txt" 2>/dev/null gifbuild "$tmp/adump.txt" > "$tmp/arebuilt.gif" 2>/dev/null gifbuild -d "$tmp/arebuilt.gif" > "$tmp/aredump.txt" 2>/dev/null check_eq giflib/anim-rebuild-size "$(size "$ANIM")" "$(size "$tmp/arebuilt.gif")" has giflib/anim-rebuild-comment "$tmp/aredump.txt" "comment" has giflib/anim-rebuild-delay10 "$tmp/aredump.txt" "delay 10" has giflib/anim-rebuild-delay20 "$tmp/aredump.txt" "delay 20" # giftool has no -h: getopt rejects it, the usage line it then prints does not # match its own option set (it advertises -I, -t and -U, which do not exist, and # omits -a, -f, -n, -p and -s, which do), and it exits 1. Pinned so that a fix is # noticed rather than read as a regression. # # THE "$tmp/noh.out" 2>&1 check_eq giflib/giftool-noh-rc 1 "$?" has giflib/giftool-noh-msg "$tmp/noh.out" "invalid option" # ------------------------- 9. the narrow-local-colormap case gif2rgb refuses # giflib-narrowmap.gif is giflib-anim.gif with a FOUR-entry local colour table on # the second frame instead of an eight-entry one. It is a legal GIF and every # other tool reads it — but gif2rgb composites both frames onto one screen and # then renders the WHOLE screen through the LAST image's colour map, so the # indices frame 1 painted out of the eight-entry global table are out of range # for a four-entry local one and it exits 253 with "Image is defective". The file # is not defective; the message is gif2rgb's, and it is the most misleading # diagnostic in this package. Pinned from three directions so nobody meeting it # on a real GIF reads it as a port defect. check giflib/narrowmap-giftext giftext "$NARROW" check giflib/narrowmap-dump gifbuild -d "$NARROW" check_rc giflib/narrowmap-gif2rgb 253 gif2rgb -1 -o "$tmp/narrow.rgb" "$NARROW" gif2rgb -1 -o "$tmp/narrow.rgb" "$NARROW" > "$tmp/narrow.err" 2>&1 has giflib/narrowmap-message "$tmp/narrow.err" "Image is defective" # The control: the same file with a wide-enough local table decodes fine, which # is what makes this a colour-map-width property and not a multi-frame one. check giflib/narrowmap-control gif2rgb -1 -o "$tmp/ctl.rgb" "$ANIM" # ------------------------------------------------------------ 10. error paths printf 'this file is not a GIF at any offset whatsoever\n' > "$tmp/junk.bin" : > "$tmp/empty.bin" check_rc giflib/junk-gif2rgb 1 gif2rgb -1 -o "$tmp/junk.rgb" "$tmp/junk.bin" check_rc giflib/junk-giftext 1 giftext "$tmp/junk.bin" check_rc giflib/empty-gif2rgb 1 gif2rgb -1 -o "$tmp/e.rgb" "$tmp/empty.bin" check_rc giflib/empty-giftext 1 giftext "$tmp/empty.bin" check_rc giflib/missing-giftext 1 giftext "$tmp/no-such-file.gif" # Nothing is left behind for an input that could not be decoded — the half of # the error contract a caller checking only the exit status depends on. check giflib/junk-no-output test ! -f "$tmp/junk.rgb" rm -rf "$tmp"