#!/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") }