#!/bin/sh set -eu base="${TMPDIR-/tmp}/mcpu-cpp-command-line-macros-$$" mkdir -p "$base" trap 'rm -rf "$base"' EXIT HUP INT TERM cat > "$base/main.c" <<'EOT' OBJECT DEFAULT EMPTY marker СУММА(10, 7) АНДРЕЙ résumé ПОРЯДОК СТРОКА(АНДРЕЙ) СКЛЕИТЬ(АНД,РЕЙ) #ifdef __MCPU_CPP_VERSION__ BUILTIN_PRESENT #else BUILTIN_REMOVED #endif #if defined(КОМАНДА) && КОМАНДА == 31 CONDITIONAL_OK #else CONDITIONAL_BAD #endif EOT "$MCPU_CPP" --no-config \ -D OBJECT=17 \ -DDEFAULT \ -DEMPTY= \ '-DСУММА(лево,право)=лево + право' \ -DАНДРЕЙ=23 \ -Drésumé=29 \ -DПОРЯДОК=1 -U ПОРЯДОК -DПОРЯДОК=2 \ '-DСТРОКА(x)=#x' \ '-DСКЛЕИТЬ(a,b)=a ## b' \ -DАНДРЕЙ=23 \ -U__MCPU_CPP_VERSION__ \ -DКОМАНДА=31 \ "$base/main.c" -o "$base/out" grep '^17$' "$base/out" >/dev/null grep '^1$' "$base/out" >/dev/null grep '^ marker$' "$base/out" >/dev/null grep '^10 + 7$' "$base/out" >/dev/null test "`grep -c '^23$' "$base/out"`" -eq 2 grep '^29$' "$base/out" >/dev/null grep '^2$' "$base/out" >/dev/null grep '^"АНДРЕЙ"$' "$base/out" >/dev/null grep '^BUILTIN_REMOVED$' "$base/out" >/dev/null if grep '^BUILTIN_PRESENT$' "$base/out" >/dev/null; then exit 1; fi grep '^CONDITIONAL_OK$' "$base/out" >/dev/null if grep '^CONDITIONAL_BAD$' "$base/out" >/dev/null; then exit 1; fi # A function-like -D without '=' has the GNU meaning "replacement is 1". printf '%s\n' 'F(99)' > "$base/default-func.c" "$MCPU_CPP" --no-config '-DF(x)' "$base/default-func.c" -o "$base/default-func.out" grep '^1$' "$base/default-func.out" >/dev/null # Unicode XID continuation works on the command line as it does in source. combining=$(printf 'e\314\201') printf '%s\n' "$combining" > "$base/combining.c" "$MCPU_CPP" --no-config "-D$combining=37" "$base/combining.c" -o "$base/combining.out" grep '^37$' "$base/combining.out" >/dev/null arabic_three=$(printf '\331\243') name="ИМЯ$arabic_three" printf '%s\n' "$name" > "$base/digit.c" "$MCPU_CPP" --no-config "-D$name=43" "$base/digit.c" -o "$base/digit.out" grep '^43$' "$base/digit.out" >/dev/null # Command-line definitions pass through the same phase-3 comment cleanup as # source #define directives. printf '%s\n' 'COMMENTED' > "$base/comment.c" "$MCPU_CPP" --no-config '-DCOMMENTED=left/* comment */right' \ "$base/comment.c" -o "$base/comment.out" grep '^left right$' "$base/comment.out" >/dev/null # -dM observes the final command-line macro state. printf '%s\n' '' > "$base/empty.c" "$MCPU_CPP" --no-config -dM -DПОРЯДОК=1 -UПОРЯДОК -DПОРЯДОК=2 \ "$base/empty.c" -o "$base/macros" grep '^#define ПОРЯДОК 2$' "$base/macros" >/dev/null # '$' is accepted after the first identifier character. Single quotes protect # it from shell parameter expansion; without quotes a backslash can do the same. "$MCPU_CPP" --no-config -dD '-DАНДРЕЙ$_Y=62' -DОЛЬГА\$OLD=37 \ "$base/empty.c" -o "$base/dollar-command-line" grep '^#define АНДРЕЙ$_Y 62$' "$base/dollar-command-line" >/dev/null grep '^#define ОЛЬГА$OLD 37$' "$base/dollar-command-line" >/dev/null # '$' is not an identifier-start character. if "$MCPU_CPP" --no-config '-D$BAD=1' "$base/empty.c" -o "$base/bad" \ >"$base/dollar-start.stdout" 2>"$base/dollar-start.stderr"; then exit 1 fi grep 'macro name expected after #define' "$base/dollar-start.stderr" >/dev/null # For -D/-U an invalid tail after a valid identifier is discarded rather than # becoming part of the replacement list. The value after '=' is preserved. "$MCPU_CPP" --no-config -dD '-DАНДРЕЙ@XYZ=62' '-DABC+TAIL=17' \ '-UREMOVE@TAIL' -DREMOVE=99 "$base/empty.c" -o "$base/invalid-tail" grep '^#define АНДРЕЙ 62$' "$base/invalid-tail" >/dev/null grep '^#define ABC 17$' "$base/invalid-tail" >/dev/null grep '^#define REMOVE 99$' "$base/invalid-tail" >/dev/null if grep '^#define АНДРЕЙ @XYZ 62$' "$base/invalid-tail" >/dev/null; then exit 1 fi # Explicit '=' in an -U tail is just another invalid suffix and is discarded. "$MCPU_CPP" --no-config -DNAME=1 '-UNAME=bad' -dM "$base/empty.c" -o "$base/undef-tail" if grep '^#define NAME ' "$base/undef-tail" >/dev/null; then exit 1 fi # In -dD output command-line definitions have their own GNU-style origin # marker and must not be reported as predefined/builtin macros. "$MCPU_CPP" --no-config -dD -DАНДРЕЙ_К=62 -DОЛЬГА=37 \ "$base/empty.c" -o "$base/dD-command-line" awk ' /^#define АНДРЕЙ_К 62$/ { if( previous != "# 0 \"\"") exit 1 andrey = 1 } /^#define ОЛЬГА 37$/ { if( previous != "# 0 \"\"") exit 1 olga = 1 } /^#define __MCPU_CPP_VERSION__ / { if( previous != "# 0 \"\"") exit 1 builtin = 1 } { previous = $0 } END { if( !andrey || !olga || !builtin ) exit 1 } ' "$base/dD-command-line" # An unterminated block comment in a command-line definition is diagnosed. if "$MCPU_CPP" --no-config '-DBROKEN=left/*' "$base/empty.c" -o "$base/bad" \ >"$base/comment-bad.stdout" 2>"$base/comment-bad.stderr"; then exit 1 fi grep -- '-D: unterminated comment' "$base/comment-bad.stderr" >/dev/null # Malformed macro names are rejected by the ordinary #define/#undef parsers. if "$MCPU_CPP" --no-config -D1ABC=2 "$base/empty.c" -o "$base/bad" \ >"$base/bad.stdout" 2>"$base/bad.stderr"; then exit 1 fi grep 'macro name expected after #define' "$base/bad.stderr" >/dev/null # Only -D/-U payloads are decoded. Invalid UTF-8 and valid supplementary-plane # UTF-8 are rejected because the internal text model is strict UCS-2. bad_utf8=$(printf '\377') if "$MCPU_CPP" --no-config "-D${bad_utf8}=1" "$base/empty.c" -o "$base/bad" \ >"$base/utf8.stdout" 2>"$base/utf8.stderr"; then exit 1 fi grep -- '-D: invalid UTF-8 or non-UCS-2 character' "$base/utf8.stderr" >/dev/null non_ucs2=$(printf '\360\237\230\200') if "$MCPU_CPP" --no-config "-D${non_ucs2}=1" "$base/empty.c" -o "$base/bad" \ >"$base/nonucs2.stdout" 2>"$base/nonucs2.stderr"; then exit 1 fi grep -- '-D: invalid UTF-8 or non-UCS-2 character' "$base/nonucs2.stderr" >/dev/null if "$MCPU_CPP" --no-config "-U${bad_utf8}" "$base/empty.c" -o "$base/bad" \ >"$base/utf8u.stdout" 2>"$base/utf8u.stderr"; then exit 1 fi grep -- '-U: invalid UTF-8 or non-UCS-2 character' "$base/utf8u.stderr" >/dev/null