#!/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 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'