Skip to content

Instantly share code, notes, and snippets.

@spencerpogo
Created April 7, 2024 22:00
Show Gist options
  • Save spencerpogo/e791d92913b616b75df572422c3cf107 to your computer and use it in GitHub Desktop.
Save spencerpogo/e791d92913b616b75df572422c3cf107 to your computer and use it in GitHub Desktop.
C interpreter shell script

Shell script to interpret C

I use this script to run C snippets quickly on the commandline, for example to print constants, offsets, or sizes when working in other languages.

If you want to add it to a bin folder, it might be useful to add "$(basename "$0")" "$@" to the bottom and making symbolic links for each function.

#!/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 "$?"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment