. "$BST_LIB" # Neither busybox nor toybox ships a unifdef applet and there is no other # unifdef on the image, so a bare `unifdef` already is this port — no # update-alternatives name to disambiguate (unlike diff/patch/tar). require unifdef unifdef/present tmp=${TMPDIR:-/tmp}/bst-unifdef.$$ src="$tmp.c"; nest="$tmp.n.c"; out="$tmp.out"; out2="$tmp.out2" # A fixed C source covering #ifdef/#else, a multi-symbol #if expression and # #ifndef, so every expectation below is a property of unifdef's own directive # evaluation rather than of the input's provenance. cat > "$src" <<'EOF' 2>/dev/null #ifdef FOO int foo = 1; #else int foo = 0; #endif int common = 42; #if defined(BAR) && defined(BAZ) int both = 1; #endif #ifndef GUARD int unguarded = 1; #endif EOF # -V prints the version banner and exits 0 — unlike the transform runs below, # whose nonzero status is meaningful (see the exit-status contract). check_out unifdef/version "unifdef-2.12" unifdef -V # unifdef's exit status IS its contract: 0 = output identical to input, # 1 = output changed, 2 = error. So every transform asserts the status # explicitly with check_rc and inspects the captured output separately — # check_out alone would fail a correct run purely on the nonzero status. check_rc unifdef/rc-changed 1 sh -c "unifdef -DFOO '$src' > '$out'" # -DFOO: the #ifdef branch is kept, the #else branch and the directives that # delimited it are removed, and text outside any conditional is untouched. check_out unifdef/def-taken "int foo = 1;" cat "$out" check_rc unifdef/else-dropped 1 grep -q "int foo = 0;" "$out" check_rc unifdef/ifdef-gone 1 grep -q "#ifdef FOO" "$out" # `#else` occurs only in the FOO block, so its absence shows the delimiting # directives went with the branch. `#endif` is deliberately NOT asserted here: # the BAR/BAZ and GUARD blocks are unresolved by -DFOO and keep their own. check_rc unifdef/else-gone 1 grep -q "#else" "$out" check_out unifdef/passthrough "int common = 42;" cat "$out" # An expression over symbols unifdef was told nothing about must survive # verbatim — it resolves only what -D/-U specify. check_out unifdef/expr-kept "#if defined(BAR) && defined(BAZ)" cat "$out" # -UFOO takes the other branch of the same conditional. check_rc unifdef/rc-undef 1 sh -c "unifdef -UFOO '$src' > '$out2'" check_out unifdef/undef-taken "int foo = 0;" cat "$out2" check_rc unifdef/undef-drops 1 grep -q "int foo = 1;" "$out2" # A multi-symbol #if resolves once every symbol in it is known. check_rc unifdef/rc-expr 1 sh -c "unifdef -DBAR -DBAZ '$src' > '$out'" check_out unifdef/expr-taken "int both = 1;" cat "$out" check_rc unifdef/expr-gone 1 grep -q "defined(BAR)" "$out" # #ifndef is the inverse: defining the symbol drops the guarded body. check_rc unifdef/rc-ifndef 1 sh -c "unifdef -DGUARD '$src' > '$out'" check_rc unifdef/ifndef-cut 1 grep -q "int unguarded = 1;" "$out" # Nothing to resolve -> output is an exact copy -> status 0, the other half of # the exit-status contract. check_rc unifdef/rc-unchanged 0 sh -c "unifdef '$src' > /dev/null" # A malformed invocation is status 2, distinct from "changed". check_rc unifdef/rc-error 2 sh -c "unifdef -Z '$src' >/dev/null 2>&1" # -s reports the controlling symbols instead of transforming, and exits 0. check_rc unifdef/rc-symbols 0 sh -c "unifdef -s '$src' > '$out'" check_out unifdef/symbols-foo "FOO" cat "$out" check_out unifdef/symbols-bar "BAR" cat "$out" check_out unifdef/symbols-grd "GUARD" cat "$out" # Nested conditionals: each level is evaluated independently. cat > "$nest" <<'EOF' 2>/dev/null #ifdef OUTER outer-yes #ifdef INNER inner-yes #else inner-no #endif #else outer-no #endif tail-line EOF check_rc unifdef/rc-nested 1 sh -c "unifdef -DOUTER -UINNER '$nest' > '$out'" check_out unifdef/nested-outer "outer-yes" cat "$out" check_out unifdef/nested-inner "inner-no" cat "$out" check_rc unifdef/nested-cut 1 grep -q "outer-no" "$out" check_out unifdef/nested-tail "tail-line" cat "$out" # Reads standard input when given no file operand. check_rc unifdef/rc-stdin 1 sh -c "unifdef -DOUTER -DINNER < '$nest' > '$out'" check_out unifdef/stdin "inner-yes" cat "$out" # Already-resolved output has nothing left to remove, so a second pass is an # exact copy and returns 0 — idempotence expressed through the status contract. check_rc unifdef/idempotent 0 sh -c "unifdef -DOUTER -DINNER '$out' > /dev/null" # -o writes the result to a named file rather than stdout. check_rc unifdef/rc-outfile 1 sh -c "unifdef -DOUTER -o '$out2' '$nest'" check_out unifdef/outfile "outer-yes" cat "$out2" rm -f "$src" "$nest" "$out" "$out2"