#! /usr/bin/awk -f # ASAP - AWK Simple and Awful Pre-processor (hacked As Soon As Possible) # Copyright (C) 2007 Matous Jan Fialka # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU general Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU general Public License for # more details. # # You should have received a copy of the GNU general Public License along with # this program. If not, see . # TODO: #ifdef #ifndef #if #ifnot #else #elifdef #elifndef #elif #elifnot #endif # TODO: multi-line definitions BEGIN { STDIN = "/dev/fd/0" STDOUT = "/dev/fd/1" STDERR = "/dev/fd/2" FFLUSH = 0 EOL = "\n" ERROR[001] = "no such file or directory" ERROR[130] = "#define requires at least variable" ERROR[140] = "#undef requires a variable" ERROR[150] = "#include failed open requested file" ERROR[151] = "#include requires a filename" ERROR[160] = "#eval requires an expression" ERROR[255] = "macro given invalid variable syntax" } BEGIN { NULL = "" DEFINITIONS["__ARGC"] = \ DEFINITIONS["__ARGN"] = \ DEFINITIONS["__ARGV"] = \ DEFINITIONS["__FILE"] = \ DEFINITIONS["__LINE"] = \ DEFINITIONS["__DATA"] = 1 VARIABLES["__ARGC"] = 0 VARIABLES["__ARGN"] = 0 VARIABLES["__ARGV"] = NULL for(i in ENVIRON) { DEFINITIONS["__ENV_" i] = 1 VARIABLES["__ENV_" i] = ENVIRON[i] } if((VARIABLES["__ARGC"] = alength(ARGV) - 1) > 0) { while((VARIABLES["__ARGV"] = apopbottom(ARGV)) != 0) { VARIABLES["__ARGN"]++ if(!fexists(VARIABLES["__ARGV"])) error(VARIABLES["__EXIT"] = 1, VARIABLES) preprocess(VARIABLES["__ARGV"], VARIABLES, DEFINITIONS) } } else preprocess(STDIN, VARIABLES, DEFINITIONS) exit } END { exit VARIABLES["__EXIT"] } function preprocess(file, vars, defs , text, line, files, lines, pl, temp, cont) { file = chop(file) if(!fexists(file)) error(vars["__EXIT"] = 150, vars) vars["__FILE"] = file vars["__LINE"] = 0 apush(files, vars["__FILE"]) apush(lines, vars["__LINE"]) line = temp = NULL while((!isnull(text) ? (!isnull(vars["__DATA"] = text)) : (getline vars["__DATA"] < vars["__FILE"])) > 0) { if(isnull(text)) vars["__LINE"]++ else text = NULL if(vars["__DATA"] ~ \ "^[ \\t]*\\\\?\\\\#" \ "(define|undef|include|eval)" \ "([ \\t]+|$)") { line = gensub(/^([ \t]*\\?)\\(.*)/, "\\1\\2", NULL, vars["__DATA"]) process_line(eval(line, vars, defs), vars, defs) continue } if(vars["__DATA"] ~ /^[ \t]*#define([ \t]+|$)/) { line = chop(shift(1, vars["__DATA"])) if(isnull(temp = substrto(line, " "))) error(vars["__EXIT"] = 130, vars) if(!isvariable(temp)) error(vars["__EXIT"] = 255, vars) vars[temp] = expand(shift(1, line)) defs[temp] = 1 continue } if(vars["__DATA"] ~ /^[ \t]*#undef([ \t]+|$)/) { if(isnull(line = chop(shift(1, vars["__DATA"])))) error(vars["__EXIT"] = 140, vars) if(!isvariable(line)) error(vars["__EXIT"] = 255, vars) delete vars[line] delete defs[line] continue } if(vars["__DATA"] ~ /^[ \t]*#include([ \t]+|$)/) { if(isnull(file = chop(shift(1, vars["__DATA"])))) error(vars["__EXIT"] = 151, vars) pl = vars["__LINE"] preprocess(file, vars, defs) vars["__FILE"] = atop(files) vars["__LINE"] = atop(lines) + pl continue } if(vars["__DATA"] ~ /^[ \t]*#eval([ \t]+|$)/) { if(isnull(text = shift(1, vars["__DATA"]))) error(vars["__EXIT"] = 160, vars) text = eval(text, vars, defs) continue } process_line(eval(vars["__DATA"], vars, defs), vars, defs) } close(vars["__FILE"]) return 0 } function process_line(line, vars, defs) { printf "%s", line EOL > STDOUT if(FFLUSH) fflush(STDOUT) return 0 } function eval(text, vars, defs , m, i, l, idefs) { if(!isnull(m)) { gsub(m, defs[m] ? vars[m] : m, text) return text } l = asortil(defs, idefs) for(i = l; i > 0; i--) text = eval(text, vars, defs, idefs[i]) for(i = l; i > 0; i--) if(text ~ idefs[i]) return eval(text, vars, defs, idefs[i]) return text } function error(text, r) { if(r["__EXIT"] < 128) printf "Error (argument = %d/%d): %s %s", r["__ARGN"], r["__ARGC"], ERROR[r["__EXIT"]], r["__ARGV"] EOL > STDERR else { printf "%s", r["__DATA"] EOL > STDERR printf "Error (%s: %d): %s", r["__FILE"], r["__LINE"], ERROR[r["__EXIT"]] EOL \ > STDERR } exit 1 } function isnull(value) { return((value == 0) || (value == NULL)) ? 1 : 0 } function isvariable(text) { return(text ~ /^[a-zA-Z_][a-zA-Z_0-9]+$/) } function shift(number , text, separator, array, no_text) { if(number == NULL || number <= 0) number = 1 if(text == NULL) { text = $0 no_text = 1 } if(separator == NULL) separator = FS if(separator == " ") separator = "[ \t]+" while(number-- > 0) { if(no_text) { sub("^" separator, NULL) if($2 == NULL) { $0 = NULL break } else $0 = substr($0, match($0, separator) + RLENGTH) } else { sub("^" separator, NULL, text) if(split(text, array, separator) < 2) { text = NULL break } else text = substr(text, match(text, separator) + RLENGTH) } } if(no_text) return $0 else return text } BEGIN { CHOP_LEFT = 1 CHOP_RIGHT = 2 } function chop(text , where) { where = where ? where : CHOP_LEFT + CHOP_RIGHT if(and(where, CHOP_LEFT) == CHOP_LEFT) gsub(/^[ \t]+/, NULL, text) if(and(where, CHOP_LEFT) == CHOP_LEFT) gsub(/[ \t]+$/, NULL, text) return text } function substrto(text , pattern) { if(pattern == NULL) return text if(pattern == " ") pattern = "[ \t]+" sub(pattern ".*$", NULL, text) return text } function expand(text) { gsub(/\\a/, "\x07", text) gsub(/\\b/, "\x08", text) gsub(/\\f/, "\x0c", text) gsub(/\\n/, "\x0a", text) gsub(/\\r/, "\x0d", text) gsub(/\\t/, "\x09", text) gsub(/\\v/, "\x0b", text) gsub(/\\e/, "\x1b", text) gsub(/\\d/, "\x7f", text) return text } function aempty(array , i) { for(i in array) return 0 return 1 } function alength(array , len, i) { if(!aempty(array)) { len = 0 for(i in array) len++ return len } else return 0 } function apush(array, value, i) { array[alength(array) + 1] = value return value } function apop(array, i, r) { if(!aempty(array)) { i = alength(array) r = array[i] delete array[i] return r } else return NULL } function apopbottom(array , i, r, x) { r = array[1]; i = alength(array); delete array[1]; for(x = 1; x <= i; x++) array[x-1] = array[x]; delete array[i]; return r; } function atop(array) { return apush(array, apop(array)) } function asortl(a, i , l, j, t) { l = alength(a) for(i = 2; i <= l; ++i) for(j = i; length(a[j - 1]) > length(a[j]); --j) { t = a[j] a[j] = a[j - 1] a[j - 1] = t } return l } function asortil(a, t , i, j) { j = 1 for(i in a) t[j++] = i return asortl(t) } function fexists(file , line) { if(fisstdin(file)) return 1 if((getline line < file) > 0) { close(file) return 1 } return 0 } function fisstdin(file) { if(file == "-" || file == "/dev/tty" || file == "/dev/stdin" || file == "/dev/fd/0") return 1 return 0 }