|
#!/usr/bin/env bash |
|
|
|
declare -a ceval_sh_tempfiles=( ) |
|
ceval_sh_cleanup() { |
|
if (( ${#ceval_sh_tempfiles[@]} )) then |
|
rm -rf -- "${ceval_sh_tempfiles[@]}" |
|
fi |
|
} |
|
trap ceval_sh_cleanup EXIT |
|
|
|
ceval_sh_cc () { |
|
local out="$(mktemp)" |
|
ceval_sh_tempfiles+=("$out") |
|
"${CC:-cc}" "$@" - -o "$out" |
|
local cc_exit="$?" |
|
printf '%s' "$out" |
|
return "$cc_exit" |
|
} |
|
|
|
ceval () { |
|
if [[ "$#" -lt 1 ]]; then |
|
printf '%s\n' "Usage: $0 [-i <standard header list>] [-I <nonstandard header list>] <code | -> [program args...]" >&2 |
|
printf '%s\n' " where header list is a comma separated list of headers names, sans .h like: stdlib,stdbool" >&2 |
|
printf '%s\n' " standard header format: <name.h>, nonstandard format: \"name.h\" (stdio is included by default)" >&2 |
|
return 1 |
|
fi |
|
local code_arg="" |
|
local -a includes=("<stdio.h>") |
|
while [[ "$#" -gt 0 ]]; do |
|
case "$1" in |
|
-i) |
|
if [[ "$#" -lt 2 ]]; then |
|
printf '%s\n' "option -i expects an argument" >&2 |
|
return 1 |
|
fi |
|
local item |
|
while IFS=, read -r item; do |
|
includes+=("<$item.h>") |
|
done <<< "$2" |
|
shift |
|
;; |
|
-I) |
|
if [[ "$#" -lt 2 ]]; then |
|
printf '%s\n' "option -I expects an argument" >&2 |
|
return 1 |
|
fi |
|
local item |
|
while IFS=, read -r item; do |
|
includes+=("\"$item.h\"") |
|
done <<< "$2" |
|
shift |
|
;; |
|
-) |
|
code_arg='-' |
|
shift |
|
break |
|
;; |
|
--) |
|
# skip and go to positional arg parsing |
|
shift |
|
break |
|
;; |
|
-*|--*) |
|
printf "unrecognized option: %s" "$1" >&2 |
|
return 1 |
|
;; |
|
*) |
|
# go to positional arg parsing |
|
break |
|
;; |
|
esac |
|
shift |
|
done |
|
if [[ -z "$code_arg" ]]; then |
|
if [[ "$#" -lt 1 ]]; then |
|
printf '%s\n' "expected positional code argument" >&2 |
|
return 1 |
|
fi |
|
code_arg="$1" |
|
shift |
|
fi |
|
|
|
#printf 'code_arg: %s\n' "$code_arg" |
|
#printf 'includes: ' |
|
#printf '%s ' "${includes[@]}" |
|
#printf '\n' |
|
#printf 'program args: ' |
|
#printf '%s ' "$@" |
|
#printf '\n' |
|
|
|
local code |
|
local exit |
|
if [[ "$code_arg" = '-' ]]; then |
|
code="$(cat -)" |
|
exit="$?" |
|
if [[ "$exit" -ne 0 ]]; then |
|
return "$exit" |
|
fi |
|
else |
|
code="$code_arg" |
|
fi |
|
|
|
# must separate the local and the assignment, because otherwise `local` will clobber |
|
# $? with 0 |
|
local out |
|
out="$( ( printf '#include %s\n' "${includes[@]}" && |
|
printf 'int main(int argc, char **argv) {\n%s\n}' "$code" ) | tee /tmp/a.c | ceval_sh_cc -x c)" |
|
exit="$?" |
|
if [[ "$exit" -ne 0 ]]; then |
|
return "$exit" |
|
fi |
|
|
|
"$out" "$@" |
|
return "$?" |
|
} |
|
|
|
cprintf () { |
|
local -a args=( ) |
|
while [[ "$#" -gt 0 ]]; do |
|
case "$1" in |
|
-i|-I) |
|
if [[ "$#" -lt 2 ]]; then |
|
printf 'option %s expects an argument\n' "$1" >&2 |
|
return 1 |
|
fi |
|
args+=("$1" "$2") |
|
shift |
|
;; |
|
--) |
|
# record and go to positional arg parsing |
|
args+=("$1") |
|
shift |
|
break |
|
;; |
|
-) |
|
printf 'reading code from stdin is not supported by this command' >&2 |
|
return 1 |
|
;; |
|
-*) |
|
# pass options on |
|
args+=("$1") |
|
;; |
|
*) |
|
# go to positional arg parsing |
|
break |
|
;; |
|
esac |
|
shift |
|
done |
|
|
|
if [[ "$#" -ne 2 ]]; then |
|
printf 'Usage: %s [options] <format specifier> <arguments>' "$0" >&2 |
|
return 1 |
|
fi |
|
|
|
local fmt="$1" |
|
shift |
|
local code="$( printf 'printf("%s"' "$fmt" && printf ', %s' "$@" && printf ');' )" |
|
|
|
ceval "${args[@]}" "$code" |
|
return "$?" |
|
} |
|
|
|
csizeof () { |
|
local -a args=( ) |
|
while [[ "$#" -gt 0 ]]; do |
|
case "$1" in |
|
-i|-I) |
|
if [[ "$#" -lt 2 ]]; then |
|
printf 'option %s expects an argument\n' "$1" >&2 |
|
return 1 |
|
fi |
|
args+=("$1" "$2") |
|
shift |
|
;; |
|
--) |
|
# record and go to positional arg parsing |
|
args+=("$1") |
|
shift |
|
break |
|
;; |
|
-) |
|
printf 'reading code from stdin is not supported by this command' >&2 |
|
return 1 |
|
;; |
|
-*) |
|
# pass options on |
|
args+=("$1") |
|
;; |
|
*) |
|
# go to positional arg parsing |
|
break |
|
;; |
|
esac |
|
shift |
|
done |
|
|
|
if [[ "$#" -ne 1 ]]; then |
|
printf 'Usage: %s [options] <type>' "$0" >&2 |
|
fi |
|
|
|
cprintf "${args[@]}" '%d\n' "sizeof($1)" |
|
return "$?" |
|
} |