Files

119 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# unob is a proof-of-concept script.
# Prior to tac, scripting the layout of C code was intractable.
#
# There are uncountably many possible refinements to this script.
# Enjoy the madness of getting a perfect layout of C code via
# an automated process. Or just enjoy a significant help in
# reading obfuscated C code!
: "${PROG:=./prog}" # PROG=tac ./unob.sh prog.c
: "${KW:=c11}" # KW=ioccc.kw.freq ./unob.sh prog.c
: "${DEF:=}" # DEF="-DW=\\\"kandr2\\\" -DU=\\\"usage\ string\\\"" ./unob.sh -p prog.c
# optionally preprocess up to 3 times to handle #ifdef X #define Y
# unob prog.c # nonce
# unob -p prog.c # once
# unob -p -p prog.c # twice
# unob -p -p -p prog.c # thrice
# unob -p -p -p -p prog.c # yer out
function pp {
cpp2="cat"
cpp3="cat"
if [[ "$1" == "-p" ]]; then
shift
if [[ "$1" == "-p" ]]; then
cpp2="cpp -E"
shift
fi
if [[ "$1" == "-p" ]]; then
cpp3="cpp -E"
shift
fi
cat "$@" |
sed 's/# *include/##include/' |
eval cpp -E "$DEF" |
eval "$cpp2" |
eval "$cpp3" |
sed 's/^#[^#].*$//' |
sed 's/##inc/#inc/'
else
cat "$@"
fi
}
# For SC2016, we do want to single quote the leading part of the script,
# and we want $KW to be double quoted within the awk script.
# Unfortunately, shellcheck does not understand the "'"-ing.
#
# SC2016 (info): Expressions don't expand in single quotes, use double quotes for that.
# https://www.shellcheck.net/wiki/SC2016
# shellcheck disable=SC2016
script='
BEGIN {
last=nl="\n";
f="'"$KW"'"; while(getline <f > 0) ++kw[$0]; close(f);
if (length(kw) == 0) {
print "cannot find",f "; check KW environment variable" | "cat 1>&2"
exit
}
}
function iskw(a) { return a in kw }
function indent() { return sprintf("%.*s", n*3, " ") }
function newline() { if (!infor && last != nl) printf last=nl }
function show(a) {
if (len>80 || (len>60 && (last ~ /^;/ || (last ~ /^,/ && paren==0))))
printf "%c%s", last=nl, indent() # yes, double indent here
if (len>60 && last ~ /^\)/ && a !~ /^\)/ && paren == 0)
printf last=nl
if (last==nl) { len=0; printf "%s", indent() }
if (length(a) > 80 && a ~ /\"/) {
newline()
len = 80
max = length(a)
for (off=0; off <= max; off += len)
printf "%s%s%c%s",
substr(a, off, len),
(off+len > max) ? "" : "\\",
last=nl, indent()
len = 0
return
}
printf "%s%s", space(), a
len += length(a space())
last=a
}
function space() {
return iskw(last) ||
(last != "." &&
last ~ /[A-Za-z0-9_+-\/%^|&\]\)=:<>;]$/ && $0 !~ /[\.:;()\[\],]/) ? " " : ""
}
/^\(/ { ++paren }
/^\)/ { --paren }
/^(if|while|for|return|switch|case|default|union|struct|typedef|#)/ { newline() }
/^for$/ { newline(); infor=1 }
infor && /^;/ { ++infor; show($0 " "); next }
infor==1 && /^:/ { ++infor }
infor>1 && paren==0 { infor=0 }
/^\?/ { ++tern; ++n; newline(); show($0 " "); next }
tern && /^:/ { newline(); show($0); --tern; --n; next }
/^#/ { newline(); show($0); newline(); next }
/^;/ { show($0); newline(); next }
/^{/ { show($0); ++n; newline(); next }
/^}/ { --n; newline(); show($0); newline(); next }
{ show($0) }
'
pp "$@" | "$PROG" -t | ./tokenfix.sh | awk "$script" # sed 's/""//g'