#!/bin/awk -f
BEGIN {
fname="\"$fname\"" # constants so I don't have to write it out
# dirname="\"$dirname\""
or_die="|| exit $?"
if ("RSFILTER" in ENVIRON) {
# clean the filter file
printf "" >ENVIRON["RSFILTER"]
}
func_num=0
has_cond=""
}
function escaped(str) {
gsub(/\\/, "\\\\", str)
# gsub(/\n/, "\\n", str)
gsub(/([\\\t"`${}\[\]])/, "\\\\&", str)
return str
}
function quoted(str) {
if(str ~ "^[a-zA-Z0-9_./]+$")
return str
gsub(/'/, "'\\''", str)
return "'" str "'"
# return "\"" escaped(str) "\""
}
function print_i(str) { # print with indent
print indent str
}
function print_d(str) { # print or die
print indent str " " or_die
}
function print_b(str) { # print begin block
print indent str
indent = indent "\t"
}
function print_m(str) { # print mid-block deindented
print substr(indent, 2) str
}
function print_e(str) { # print end block
indent = substr(indent, 2)
print indent str
}
function print_cond(statement, suffix) { # TODO
if(has_cond == "") {
print_b("if ! $check_only; then")
print_d("{ "statement"\n}"suffix)
print_e("fi")
} else {
if(suffix) {
funcs["do_" has_cond] = "{ "statement"\n}"suffix
} else {
funcs["do_" has_cond] = statement
}
print_d("req " has_cond)
has_cond = ""
}
}
function print_rsfilter(str) {
if ("RSFILTER" in ENVIRON) {
print str >>ENVIRON["RSFILTER"]
# } else {
# print str >>"/dev/stderr"
}
}
# take tab-delimited token from statement variable and return it
function get_till_tab( result) {
if(match(statement, /\t/)) {
result = substr(statement, 1, RSTART-1)
statement = substr(statement, RSTART+1)
} else {
result = statement
statement = ""
}
return result
}
function get_argument(whole_statement, result) {
if(whole_statement) {
result = statement
statement = ""
} else {
result = get_till_tab()
}
return result
}
function shellfunc(fun, flags) {
print_d("flags "quoted(flags)"; "fun)
}
function process_statement() {
while(statement) {
command = get_till_tab()
if(!command) continue
cchar = substr(command, 1, 1)
crest = substr(command, 2)
# check before the command is processed if we have dangling "?"
if(cchar != "!" && has_cond != "") {
printf "%s:%s: ? is not followed by ! (superfluous condition): %s\n", FILENAME, FNR, \
cchar crest " '" statement "'" >"/dev/stderr"
if(!WARN_ONLY) {
exit 1
}
}
# set current fname all subsequent operations will be performed on
if(cchar == "/") {
curpath = crest
# fix up the path
if(match(crest, "/+$")) {
crest = substr(crest, 1, length(crest) - RLENGTH)
}
crest = "./" crest
print ""
print_i("fname=" quoted(crest))
match(crest, /.*\//)
# print_i("dirname=" quoted(substr(crest, 1, RLENGTH-1)))
continue
}
if(cchar == "+") {
print_rsfilter("+ /" curpath crest)
continue
}
if(cchar == "-") {
print_rsfilter("- /" curpath crest)
continue
}
if(cchar == "o") {
print_i("o "crest)
continue
}
if(cchar == "m") {
print_i("m "crest)
continue
}
if(cchar == "u") {
print_i("umask "crest)
continue
}
# remove
if(cchar == "r") {
shellfunc("r", crest)
continue
}
# create file
if(cchar == "f") {
shellfunc("f", crest)
continue
}
# symbolic link
if(cchar ~ /[lL]/) {
shellfunc("l "quoted(get_argument(cchar == "L")), crest)
continue
}
# directory
if(cchar == "d") {
shellfunc("d", crest)
continue
}
# Cat, Copy, Content; eats rest of statement and puts it into the file
# Binary, Base64; decodes the arguments and changes file content
# heXdump; decodes the arguments and replaces file content
if(cchar ~ /[bBcCX]/) {
shellfunc("f 1", crest)
content = get_argument(cchar ~ /[BCX]/)
func_name = "fn" func_num++
if(cchar ~ /[bB]/) {
funcs[func_name] = "\tbase64 <<<"quoted(content)
} else if(cchar ~ /[X]/) {
funcs[func_name] = "\txxd -r <<<"quoted(content)
} else {
# unless disabled with the N flag, append newline at the end of
# last line, if not already present
printf_fmt = ( \
crest ~ /n/ || (crest !~ /N/ && content !~ /\n$/) \
) ? "%s\\n" : "%s"
if(printf_fmt == "%s" || length(content) < 1800) {
# TODO: split into several when needed
funcs[func_name] = "\tprintf '"printf_fmt"' "quoted(content)
} else {
funcs[func_name] = "\tcat <<<"quoted(content)
}
}
if(crest ~ /a/) {
print_cond(func_name, ">>"fname)
} else if(has_cond == "") {
print_i("c " func_name)
} else {
print_cond("c "func_name)
}
continue
}
# run shell command
if(cchar == "!") {
# use as Filter
if(crest ~ /f/) {
if(crest ~ /c/) {
shellfunc("f", crest)
} else {
shellfunc("req type_or_missing f", crest)
}
print_cond("\n" \
"\t{ "statement"\n} <"fname" >"fname".tmp.$$ " or_die "\n" \
"\tc cat "fname".tmp.$$\n" \
"\trm "fname".tmp.$$ " or_die "\n")
# use file as Input
} else if(crest ~ /i/) {
print_cond(statement, "<"fname)
# use file as Output
} else if(crest ~ /o/) {
shellfunc("req type_or_missing f", crest)
print_cond(statement, ">"fname)
# Append to file
} else if(crest ~ /a/) {
shellfunc("req type_or_missing f", crest)
print_cond(statement, ">>"fname)
# do nothing special with file
} else {
print_cond(statement)
}
statement = ""
continue
}
# shell condition
if(cchar == "?") {
has_cond = func_num++
if(crest ~ /i/) {
funcs["check_" has_cond] = ( \
"{ "statement"\n} <"fname \
)
} else {
funcs["check_" has_cond] = statement
}
statement = ""
continue
}
# if none above matched
printf "%s:%s: unrecognised statement: %s\n", FILENAME, FNR, \
cchar crest " '" statement "'" >"/dev/stderr"
if(!WARN_ONLY) {
exit 1
}
statement = ""
}
}
function parse_line(line) {
if(!line) { # empty line, ignore
return
}
if(line ~ /^#/) { # comment, ignore
return
}
if(line ~ /^\t/) { # continuation, append to statement
statement = statement "\n" substr(line, 2)
} else { # new statement
process_statement()
statement = line
}
}
function print_functions() {
for( func_name in funcs ) {
print func_name "(){\n" funcs[func_name] "\n}\n"
}
}
BEGIN {
print_b("check_main() {")
}
{ parse_line($0) }
END {
process_statement()
print_e("}")
print_b("do_main() {")
print_i("true")
print_e("}")
print ""
print_functions()
print_i("req main")
}