# BST-TIMEOUT: 600 # ~90 xdelta3 invocations over a 100 kB fixture pair # # xdelta3 3.1.0 -- binary diff / VCDIFF (RFC 3284) differential compression. # # The evidence this suite is built on is CROSS-ARM BYTE IDENTITY, not a set of # hand-written expectations. data/xdelta3-{src,tgt}.bin are a fixed fixture pair # (src/xdelta3_mkfixture.py), data/xdelta3-ref-*.vcdiff are the deltas an x86_64 # build of the SAME RELEASE emits from them, and data/xdelta3-native.txt carries # every derived number that build reported (src/xdelta3_mknative.sh generated all # three). So the target is not asked "does this look right" but "do you emit the # same bytes as x86_64" -- which, for a delta compressor, exercises the rolling # checksum, the string matcher, instruction selection, the address cache and both # secondary coders in a single cmp. # # The seven encodings are chosen for path coverage: default and -S none prove the # default really carries no secondary compressor; -S djw and -S fgk are the two # that are compiled in; -1 and -9 are the ends of the match-effort range and # change instruction SELECTION rather than encoding; and no-source is the # compress-only path, whose data section is ~20x the others. # # Not covered here, deliberately: `xdelta3 test`, upstream's own regression # corpus, which IS in the shipped binary (REGRESSION_TEST=1, asserted below). # It takes 209 s on x86_64 and cannot be scoped -- there is no selector -- so it # is a documented on-target command rather than a gated suite. It WAS run by # hand on target: 19 of its 26 tests pass, and it then stops at test 20 # (force_behavior), the first that calls system(), which fails EAGAIN on this # platform (brintos/linux#14). So it could not be a green gate today even if it # were fast. See the ticket's test-plan entry, ยง63. . "$BST_LIB" require xdelta3 xdelta3/present NAT="$BST_DATA/xdelta3-native.txt" SRC="$BST_DATA/xdelta3-src.bin" TGT="$BST_DATA/xdelta3-tgt.bin" for f in "$NAT" "$SRC" "$TGT"; do [ -r "$f" ] || { bst_skip xdelta3/payload "missing payload $f"; exit 0; } done W=${TMPDIR:-/tmp}/bst-xdelta3.$$ mkdir -p "$W" 2>/dev/null || { bst_notok xdelta3/workdir "cannot mkdir $W"; exit 0; } cd "$W" || { bst_notok xdelta3/workdir "cannot cd $W"; exit 0; } trap 'cd /; rm -rf "$W"' EXIT # The variant table. It MUST agree with src/xdelta3_mknative.sh, which produced # the reference deltas -- the two live in the same package for that reason. # # Newline-separated and read with IFS='|', not a space-separated word list: # xdelta3 does not accept -Sdjw, only `-S djw`, so the option strings contain # spaces. The first draft used `for spec in $VARIANTS` and the shell split # `secnone:-S none` into two words -- which did not fail loudly. It silently # encoded djw and fgk with NO -S at all, and the dry-run showed both producing # the default delta while the case names still said djw and fgk. VARIANTS='default| secnone|-S none djw|-S djw fgk|-S fgk l1|-1' # -9 is NOT in that list. It is the one encoding this guest cannot produce, for # a reason that is neither a brintOS defect nor an xdelta3 defect, and it gets # its own block below so the report says which. Measured: -9 peaks at 266 MiB # of RSS to diff two 100 kB files (against 70 MiB at the default level and # 20 MiB at -1) because XD3_SMATCH_SLOW sizes its tables from the 64 MiB source # window rather than from the input, while the guest allows ~232 MiB of heap # per process (largest single malloc 120 MiB). Encoding at -9 therefore cannot # fit, in a FRESH guest as well as a loaded one -- so it is a ceiling, not the # cumulative exhaustion this runtime is also prone to. # -------------------------------------------------------------------------- # 1. Build configuration -- the feature matrix, none of which a human chose. # # Three configure answers decide what this binary CONTAINS, and each was # resolved on the arm's behalf: UNALIGNED_OK comes from an AC_TRY_RUN whose # cross-compile fallback is "no" (so unaligned access is assumed legal and the # rolling checksum reads 32-bit words directly), SECONDARY_LZMA from a header # probe, and usize_t/xoff_t from three AC_CHECK_SIZEOF results. A binary that is # silently different is one cache variable away, and nothing in the package # manifest would say so -- hence asserting the RUNTIME report rather than # reading config.h at build time. xdelta3 config > cfg.txt 2>&1 if [ -s cfg.txt ]; then bst_ok xdelta3/config-runs else bst_notok xdelta3/config-runs "xdelta3 config produced no output"; fi # grep -F throughout: these are literal lines, and two of them -- sizeof(void*) # and sizeof(size_t) -- carry regex metacharacters. As a basic regex, # `sizeof(void*)=4` matches "sizeof(voi", zero-or-more "d", ")=4", i.e. nothing # the tool ever prints, so the case went red against a CORRECT binary in the # dry-run and the "got:" field came back empty because the diagnostic grep was # broken the same way. grep '^XD3-CFG ' "$NAT" | while read -r _ kv; do key=${kv%%=*} if grep -Fqx -- "$kv" cfg.txt; then bst_ok "xdelta3/cfg-$key" else bst_notok "xdelta3/cfg-$key" "want:'$kv' got:'$(grep -F "$key=" cfg.txt | head -1)'"; fi done # The portable half of the type model. usize_t and xoff_t are 8 bytes on BOTH # arms, which on wasm32 is not the obvious outcome: XD3_USE_LARGESIZET picks the # first 8-byte type available, and with long at 4 bytes here that resolves to # unsigned long long rather than to unsigned long as it does on x86_64. Same # width, different underlying type -- which is exactly why it is asserted. grep '^XD3-SIZEOF ' "$NAT" | while read -r _ want; do id=$(printf '%s' "${want#sizeof(}" | sed 's/).*//; s/ /-/g') if grep -Fqx -- "$want" cfg.txt; then bst_ok "xdelta3/sizeof-$id" else bst_notok "xdelta3/sizeof-$id" "want:'$want' got:'$(grep -F "${want%%=*}=" cfg.txt | head -1)'"; fi done # ...and the half that MUST differ from the control. If any of these came back 8 # we would be testing a host binary, and every byte-identity case below would be # passing for the wrong reason. # # These three are the one part of this suite that is EXPECTED TO GO RED in the # x86_64 dry-run, where all three report 8. That is the assertion working, not a # defect in it -- do not "fix" them by relaxing the value. for want in 'sizeof(void*)=4' 'sizeof(long)=4' 'sizeof(size_t)=4'; do id=$(printf '%s' "${want#sizeof(}" | sed 's/).*//; s/\*/ptr/') if grep -Fqx -- "$want" cfg.txt; then bst_ok "xdelta3/wasm32-$id" else bst_notok "xdelta3/wasm32-$id" "want:'$want' got:'$(grep -F "${want%%=*}=" cfg.txt | head -1)'"; fi done # -------------------------------------------------------------------------- # 2. Encode -- and compare the emitted VCDIFF byte-for-byte with x86_64's. # # -A is load-bearing, not cosmetic: without it xdelta3 embeds the source # filename and its own version in the application header, and the delta stops # being a function of the input bytes. Upstream's own tests pass -A for this. printf '%s\n' "$VARIANTS" | while IFS='|' read -r name opts; do ref="$BST_DATA/xdelta3-ref-$name.vcdiff" # shellcheck disable=SC2086 out=$(xdelta3 -A -f $opts -e -s "$SRC" "$TGT" "d-$name.vcdiff" 2>&1); rc=$? if [ "$rc" -eq 0 ] && [ -s "d-$name.vcdiff" ]; then bst_ok "xdelta3/encode-$name" elif bst_execfail "$out"; then bst_unrun "xdelta3/encode-$name" "$out" else bst_notok "xdelta3/encode-$name" "rc=$rc $(_bst_1line "$out")"; continue; fi want=$(sed -n "s/^XD3-SIZE-$name //p" "$NAT") got=$(wc -c < "d-$name.vcdiff" | tr -d ' ') check_eq "xdelta3/size-$name" "$want" "$got" if cmp -s "d-$name.vcdiff" "$ref"; then bst_ok "xdelta3/bytes-$name" else bst_notok "xdelta3/bytes-$name" "delta differs from the x86_64 reference ($got vs $(wc -c < "$ref" | tr -d ' ') bytes)"; fi done out=$(xdelta3 -A -f -e "$TGT" d-nosrc.vcdiff 2>&1); rc=$? if [ "$rc" -eq 0 ]; then bst_ok xdelta3/encode-nosrc else bst_notok xdelta3/encode-nosrc "rc=$rc $(_bst_1line "$out")"; fi check_eq xdelta3/size-nosrc "$(sed -n 's/^XD3-SIZE-nosrc //p' "$NAT")" "$(wc -c < d-nosrc.vcdiff | tr -d ' ')" if cmp -s d-nosrc.vcdiff "$BST_DATA/xdelta3-ref-nosrc.vcdiff"; then bst_ok xdelta3/bytes-nosrc else bst_notok xdelta3/bytes-nosrc "compress-only delta differs from the x86_64 reference"; fi # -------------------------------------------------------------------------- # 3. Decode. Two cases per encoding, and they are not the same assertion: the # first closes the target's own round trip, the second decodes the delta the # x86_64 build produced. If byte identity above ever breaks, `decode-ref-*` is # what says whether the two arms are still INTEROPERABLE -- which is the # property a delta tool actually has to have. printf '%s\n' "$VARIANTS" | while IFS='|' read -r name opts; do # An absent delta is reported, never skipped past. The first draft used a bare # `continue` here, and when the -9 encode failed its two decode cases vanished # from the report entirely -- 94 cases where the dry-run had 96, with nothing # saying which two were missing or why. if [ ! -s "d-$name.vcdiff" ]; then bst_notok "xdelta3/decode-$name" "no delta to decode: the $name encode above did not produce one" bst_notok "xdelta3/decode-ref-$name" "not attempted: the $name encode above did not produce a delta" continue fi if xdelta3 -f -d -s "$SRC" "d-$name.vcdiff" "o-$name.bin" >/dev/null 2>&1 && cmp -s "o-$name.bin" "$TGT" then bst_ok "xdelta3/decode-$name" else bst_notok "xdelta3/decode-$name" "own delta did not decode back to the target file"; fi if xdelta3 -f -d -s "$SRC" "$BST_DATA/xdelta3-ref-$name.vcdiff" "r-$name.bin" >/dev/null 2>&1 && cmp -s "r-$name.bin" "$TGT" then bst_ok "xdelta3/decode-ref-$name" else bst_notok "xdelta3/decode-ref-$name" "x86_64-produced delta did not decode here"; fi rm -f "o-$name.bin" "r-$name.bin" done if xdelta3 -f -d d-nosrc.vcdiff o-nosrc.bin >/dev/null 2>&1 && cmp -s o-nosrc.bin "$TGT" then bst_ok xdelta3/decode-nosrc else bst_notok xdelta3/decode-nosrc "compress-only delta did not decode back to the target file"; fi rm -f o-nosrc.bin # -------------------------------------------------------------------------- # 3b. -9, the encoding this guest cannot produce -- and the asymmetry that # makes it worth shipping anyway. # # The outcome is pinned in the CASE NAME rather than behind a relaxed check, so # the platform's answer is readable from the report and a change of answer shows # up as a changed name (the precedent is 82-log4c's probe-mapfailed-*): # # l9-encodes the guest had the memory; byte identity is then asserted too # l9-refuses it did not, and xdelta3 said so and exited non-zero -- which # is the property that actually matters, because the failure # mode of a delta tool that runs out of memory MUST NOT be a # truncated delta or a trap. This is the measured state. # (red) any third shape: exit 0 with no delta, a signal, or a delta # that is not byte-identical. Read it by hand. out=$(xdelta3 -A -f -9 -e -s "$SRC" "$TGT" d-l9.vcdiff 2>&1); rc=$? if [ "$rc" -eq 0 ] && [ -s d-l9.vcdiff ]; then bst_ok xdelta3/l9-encodes if cmp -s d-l9.vcdiff "$BST_DATA/xdelta3-ref-l9.vcdiff"; then bst_ok xdelta3/l9-bytes else bst_notok xdelta3/l9-bytes "-9 delta differs from the x86_64 reference"; fi if xdelta3 -f -d -s "$SRC" d-l9.vcdiff o-l9.bin >/dev/null 2>&1 && cmp -s o-l9.bin "$TGT" then bst_ok xdelta3/l9-decodes else bst_notok xdelta3/l9-decodes "own -9 delta did not decode back to the target file"; fi else case "$out" in *"Cannot allocate memory"*|*"out of memory"*) bst_ok xdelta3/l9-refuses ;; *) bst_notok xdelta3/l9-refuses "rc=$rc, but not an out-of-memory refusal: $(_bst_1line "$out")" ;; esac fi rm -f d-l9.vcdiff o-l9.bin # The asymmetry, and the reason the -9 reference payload still ships: DECODING # needs none of the encoder's tables, so a -9 delta produced anywhere else # applies here perfectly. "This image cannot create a -9 delta but can apply # one" is the fact a user needs, and it is asserted rather than implied. if xdelta3 -f -d -s "$SRC" "$BST_DATA/xdelta3-ref-l9.vcdiff" r-l9.bin >/dev/null 2>&1 && cmp -s r-l9.bin "$TGT" then bst_ok xdelta3/decode-ref-l9 else bst_notok xdelta3/decode-ref-l9 "x86_64-produced -9 delta did not decode here"; fi rm -f r-l9.bin # -------------------------------------------------------------------------- # 4. printhdr -- the VCDIFF header the decoder parsed, field by field, against # the control. Redundant with cmp when everything is green; the point is what it # does when something is red, which is to name the section that moved (a # difference confined to `addr` is the address cache, to `data` the ADD path, to # `inst` instruction selection) instead of reporting "2688 bytes differ". HDRFIELDS='secondary:secondary compressor windowind:window indicator deltaind:delta indicator adler32:adler32 checksum twlen:target window length delen:delta encoding length data:data section length inst:inst section length addr:addr section length' for name in default secnone djw fgk l1 l9 nosrc; do if ! xdelta3 printhdr -f "$BST_DATA/xdelta3-ref-$name.vcdiff" "h-$name.txt" >/dev/null 2>&1; then bst_notok "xdelta3/hdr-$name" "printhdr failed"; continue fi mism= echo "$HDRFIELDS" | while IFS= read -r f; do key=${f%%:*}; label=${f#*:} want=$(sed -n "s/^XD3-HDR-$name-$key //p" "$NAT") got=$(sed -n "s/^VCDIFF $label: *//p" "h-$name.txt" | sed 's/ *$//') [ "$want" = "$got" ] || printf '%s(want=%s got=%s) ' "$key" "$want" "$got" done > "m-$name.txt" mism=$(cat "m-$name.txt") if [ -z "$mism" ]; then bst_ok "xdelta3/hdr-$name" else bst_notok "xdelta3/hdr-$name" "$(_bst_1line "$mism")"; fi done # djw and fgk differ in WHICH sections they compressed, and that is a property of # the secondary coder rather than of the delta size: djw took the instruction and # address sections, fgk only the instruction section. Asserted by name so a coder # that silently stopped engaging is a red rather than a slightly larger file. check_out xdelta3/djw-compresses-inst-and-addr "VCD_INSTCOMP VCD_ADDRCOMP" cat h-djw.txt check_out xdelta3/fgk-compresses-inst "VCD_INSTCOMP" cat h-fgk.txt # -------------------------------------------------------------------------- # 5. printdelta -- the decoder walking the instruction stream. The census covers # all three VCDIFF instruction classes; the fixture carries a 4 kB run of one # byte specifically so RUN is reached, because the first draft of it censused # RUN 0 and the class went untested. for name in default l1; do if ! xdelta3 printdelta -f "$BST_DATA/xdelta3-ref-$name.vcdiff" "pd-$name.txt" >/dev/null 2>&1; then bst_notok "xdelta3/printdelta-$name" "printdelta failed"; continue fi for k in CPY_0 CPY_1 ADD RUN; do want=$(sed -n "s/^XD3-INSTR-$name-$k //p" "$NAT") got=$(grep -c " $k " "pd-$name.txt") check_eq "xdelta3/instr-$name-$k" "$want" "$got" done done # -------------------------------------------------------------------------- # 6. recode -- re-encode an existing delta under a different secondary coder # WITHOUT the source file. The strong form of the assertion, and it holds on # x86_64: recoding the default delta to djw yields the byte-identical file that # encoding to djw directly produced. That equality is a real invariant of the # format (the instruction stream is unchanged; only the section coding differs), # so it is asserted rather than just round-tripped. if xdelta3 recode -A -f -S djw "$BST_DATA/xdelta3-ref-default.vcdiff" rc-djw.vcdiff >/dev/null 2>&1 then bst_ok xdelta3/recode-runs else bst_notok xdelta3/recode-runs "recode -S djw failed"; fi if cmp -s rc-djw.vcdiff "$BST_DATA/xdelta3-ref-djw.vcdiff" then bst_ok xdelta3/recode-equals-direct-djw else bst_notok xdelta3/recode-equals-direct-djw "recoded delta differs from the directly-encoded djw delta"; fi if xdelta3 -f -d -s "$SRC" rc-djw.vcdiff rc-out.bin >/dev/null 2>&1 && cmp -s rc-out.bin "$TGT" then bst_ok xdelta3/recode-decodes else bst_notok xdelta3/recode-decodes "recoded delta did not decode back to the target file"; fi rm -f rc-out.bin # -------------------------------------------------------------------------- # 7. stdin/stdout. xdelta3 has two I/O arms (XD3_POSIX vs XD3_STDIO) and this # build reports XD3_POSIX=1, so both the file and the pipe path go through # xd3_posix_io -- the function that calls read()/write() INDIRECTLY through a # cast function pointer whose declared usize_t parameter is 64-bit while the # real size_t here is 32-bit. WebAssembly type-checks call_indirect, so if that # call is ever emitted as a genuine indirect call it traps. Everything above # already goes through it in file mode; this covers the stream mode, where the # length is not known in advance. if xdelta3 -A -f -e -s "$SRC" < "$TGT" > d-stdio.vcdiff 2>/dev/null && cmp -s d-stdio.vcdiff "$BST_DATA/xdelta3-ref-default.vcdiff" then bst_ok xdelta3/stdio-encode-identical else bst_notok xdelta3/stdio-encode-identical "stdin/stdout encode did not match the file-mode reference delta"; fi if xdelta3 -f -d -s "$SRC" < "$BST_DATA/xdelta3-ref-default.vcdiff" > o-stdio.bin 2>/dev/null && cmp -s o-stdio.bin "$TGT" then bst_ok xdelta3/stdio-decode else bst_notok xdelta3/stdio-decode "stdin/stdout decode did not reproduce the target file"; fi rm -f o-stdio.bin d-stdio.vcdiff # -------------------------------------------------------------------------- # 8. External compression -- the fork()/pipe()/execvp()/waitpid() surface. # EXTERNAL_COMPRESSION=1 means xdelta3 spawns gzip to transparently decompress a # compressed input, so this is a process-model probe wearing a compression # tool's clothes. The pipe below is upstream's own identity test # (test_externally_compressed_io): gzip | xdelta3 -c | xdelta3 -dc | gunzip must # return the original bytes, with the middle of the pipe being real VCDIFF. if command -v gzip >/dev/null 2>&1; then if gzip -cf < "$TGT" 2>/dev/null | xdelta3 -cfq 2>/dev/null | xdelta3 -dcfq 2>/dev/null | gzip -dcf > o-pipe.bin 2>/dev/null && cmp -s o-pipe.bin "$TGT" then bst_ok xdelta3/extcomp-identity-pipe else bst_notok xdelta3/extcomp-identity-pipe "gzip|xdelta3|xdelta3|gunzip did not round-trip"; fi # -R suppresses OUTPUT recompression, so the decoder's stdout is the original # bytes rather than a re-gzipped stream: the same pipe minus the final gunzip. if gzip -cf < "$TGT" 2>/dev/null | xdelta3 -cfq 2>/dev/null | xdelta3 -Rdcfq > o-norecomp.bin 2>/dev/null && cmp -s o-norecomp.bin "$TGT" then bst_ok xdelta3/extcomp-no-recompression else bst_notok xdelta3/extcomp-no-recompression "-R pipe did not yield the original bytes"; fi # -D disables external decompression entirely, so the same input is treated as # opaque bytes. It must still round-trip -- and must NOT equal the target, # since what comes out is the gzip stream. gzip -cf < "$TGT" > t.gz 2>/dev/null if xdelta3 -Dcfq < t.gz 2>/dev/null | xdelta3 -Rdcfq > o-nodecomp.bin 2>/dev/null && cmp -s o-nodecomp.bin t.gz then bst_ok xdelta3/extcomp-D-treats-input-opaque else bst_notok xdelta3/extcomp-D-treats-input-opaque "-D pipe did not reproduce the compressed stream"; fi rm -f o-pipe.bin o-norecomp.bin o-nodecomp.bin t.gz else bst_skip xdelta3/extcomp-identity-pipe "gzip not installed on this image" fi # -------------------------------------------------------------------------- # 9. Error paths. A delta compressor that fails silently corrupts data, so the # adler32 guard is the single most important thing in the tool -- these assert # that a bad decode is REFUSED, with the message and status measured on x86_64. if xdelta3 -f -d -s "$TGT" "$BST_DATA/xdelta3-ref-default.vcdiff" bad1.bin >/dev/null 2>e1.txt then bst_notok xdelta3/reject-wrong-source "decode with the WRONG source file succeeded" else check_out xdelta3/reject-wrong-source "target window checksum mismatch" cat e1.txt; fi cp "$BST_DATA/xdelta3-ref-default.vcdiff" corrupt.vcdiff # flip one byte in the middle of the delta -- dd is on the image (util-linux/busybox) printf '\377' | dd of=corrupt.vcdiff bs=1 seek=800 conv=notrunc 2>/dev/null if xdelta3 -f -d -s "$SRC" corrupt.vcdiff bad2.bin >/dev/null 2>e2.txt then bst_notok xdelta3/reject-corrupt-delta "decode of a corrupted delta succeeded" else check_out xdelta3/reject-corrupt-delta "XD3_INVALID_INPUT" cat e2.txt; fi if xdelta3 -f -d -s "$SRC" "$SRC" bad3.bin >/dev/null 2>e3.txt then bst_notok xdelta3/reject-not-vcdiff "decode of a non-VCDIFF input succeeded" else check_out xdelta3/reject-not-vcdiff "not a VCDIFF input" cat e3.txt; fi rm -f bad1.bin bad2.bin bad3.bin corrupt.vcdiff e1.txt e2.txt e3.txt # -------------------------------------------------------------------------- # 10. Source-window memory. XD3_DEFAULT_SRCWINSZ is 64 MiB and XD3_DEFAULT_WINSIZE # 8 MiB -- both far above what this guest has to spare -- so the question is # whether those are ALLOCATED or merely upper bounds. Every case above already # ran at the defaults, so a green here says the allocation is sized to the input; # the explicit -B case is the bounded fallback a memory-tight caller would reach # for, and asserts it still produces a delta that decodes. if xdelta3 -A -f -B 524288 -e -s "$SRC" "$TGT" d-bmin.vcdiff >/dev/null 2>&1 \ && xdelta3 -f -d -s "$SRC" d-bmin.vcdiff o-bmin.bin >/dev/null 2>&1 \ && cmp -s o-bmin.bin "$TGT" then bst_ok xdelta3/bounded-source-window else bst_notok xdelta3/bounded-source-window "-B 524288 encode/decode did not round-trip"; fi rm -f d-bmin.vcdiff o-bmin.bin # 524288 is not a round number picked for looks -- it is the floor xdelta3 # enforces, and the dry-run is how that was found (a -B 65536 case went red # against a correct binary). The refusal is asserted too: a memory-tight caller # reaching for a smaller window has to be told, not quietly given a 64 MiB one. if xdelta3 -A -f -B 65536 -e -s "$SRC" "$TGT" d-btoosmall.vcdiff >/dev/null 2>e4.txt then bst_notok xdelta3/reject-undersized-B "-B below the documented minimum was accepted" else check_out xdelta3/reject-undersized-B "minimum value: 524288" cat e4.txt; fi rm -f d-btoosmall.vcdiff e4.txt # The corpus that is compiled in but not run here. Named as a case so the report # says WHY a 26-test upstream corpus is absent from a green run, rather than # leaving a reader to notice it is missing. if grep -Fqx 'REGRESSION_TEST=1' cfg.txt then bst_skip xdelta3/upstream-corpus "'xdelta3 test' is in this binary; run by hand. Measured on target: 19/26 green, then test 20 force_behavior stops on system() -- brintos/linux#14. 209 s on x86_64, no selector, so not gated here" else bst_notok xdelta3/upstream-corpus "REGRESSION_TEST is not 1: upstream's corpus is not in this binary"; fi