--- /dev/null
+#!/bin/awk -f
+#!/opt/busybox/awk -f
+
+# Copyright 2022 - 2023 Paul Hänsch
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+function sh_escape(arg){
+ return "'" gensub(/'/, "'\"'\"'", "g", arg) "'";
+}
+
+function argsplit(line, args, LOCAL, c, n, ctx) {
+ ctx="space"; n=0;
+
+ while ( length(line) > 0 ) {
+ c = substr(line, 1, 1);
+ line = substr(line, 2);
+ if (ctx == "space" )
+ if (c ~ /[ \t]/) ctx = "space";
+ else if (c ~ /\\/) { n++; ctx = "escbare"; }
+ else if (c ~ /"/) { n++; ctx = "dquot"; }
+ else if (c ~ /'/) { n++; ctx = "squot"; }
+ else { n++; args[n] = c; ctx = "bare"; }
+ else if (ctx == "bare")
+ if (c ~ /[ \t]/) ctx = "space";
+ else if (c ~ /\\/) ctx = "escbare";
+ else if (c ~ /"/) ctx = "dquot";
+ else if (c ~ /'/) ctx = "squot";
+ else args[n] = args[n] c;
+ else if (ctx == "dquot")
+ if (c ~ /"/) ctx = "bare";
+ else if (c ~ /\\/) ctx = "escdquot";
+ else args[n] = args[n] c;
+ else if (ctx == "squot")
+ if (c ~ /'/) ctx = "bare";
+ else args[n] = args[n] c;
+ else if (ctx == "escbare") {
+ args[n] = args[n] c;
+ ctx = "bare";
+ }
+ else if (ctx == "escdquot") {
+ args[n] = args[n] c;
+ ctx = "dquot";
+ }
+ }
+}
+
+function HTML ( text ) {
+ gsub( /&/, "\\&", text );
+ gsub( /</, "\\<", text );
+ gsub( />/, "\\>", text );
+ gsub( /"/, "\\"", text );
+ gsub( /'/, "\\'", text );
+ gsub( /\\/, "\\\", text );
+ return text;
+}
+
+function macro(call, LOCAL, line, args) {
+ argsplit(call, args);
+ call="";
+
+ for (n = 1; n in args; n++) call = call sh_escape(args[n]) " ";
+
+ if (args[1] in MACROS) {
+ printf "%s", file | sh_escape(ENVIRON["MD_MACROS"]) "/" call;
+ close(sh_escape(ENVIRON["MD_MACROS"]) "/" call);
+ } else {
+ printf "%s", HTML("<<" call ">>");
+ }
+}
+
+function unhtml ( text ) {
+ gsub( /</, "<", text);
+ gsub( />/, ">", text);
+ gsub( /"/, "\"", text);
+ gsub( /'/, "'", text);
+ gsub( /\/, "\\", text);
+ gsub( /&/, "\\&", text);
+ return text;
+}
+
+function findmacro(line, LOCAL, st, len, pre, post) {
+ if ( match(line, /<code class="macro">[^\n]*<\/code>/) ) {
+ match(line, /<code class="macro">/); pre = substr( line, 1, RSTART - 1 );
+ line = substr( line, RSTART + RLENGTH);
+ match( line, /<\/code>/); post = substr(line, RSTART + RLENGTH);
+ line = substr(line, 1, RSTART - 1);
+
+ printf "%s", pre; macro( unhtml(line) ); findmacro( post );
+ } else {
+ printf "%s", line;
+ }
+}
+
+BEGIN {
+ if (ENVIRON["MD_MACROS"]) {
+ AllowMacros = "true";
+ "cd " sh_escape(ENVIRON["MD_MACROS"]) "; printf '%s/' *" |getline macro_list;
+ split(macro_list, MACROS, "/");
+ for (n in MACROS) { MACROS[MACROS[n]] = ""; delete MACROS[n]; }
+ delete MACROS[""];
+
+ file = ""; while ( getline ) { file = file $0 "\n"; }
+ findmacro( file );
+ }
+}