+#!/bin/awk -f
+#
+# Copyright © 2023 Nick Bowler
+#
+# Usage: bake-config.awk config.h some_header.h
+#
+# Looks for #define and #undef lines in config.h to identify configuration
+# macros, then looks for uses of those macros in some_header.h and substitutes
+# them accordingly. The result is a header file that does not directly depend
+# on config.h, which can be installed normally.
+#
+# Presently, two kinds of substitions are made.
+#
+# - For a #define in config.h, any occurrence of that macro is substituted with
+# its replacement text.
+#
+# - For an #undef in config.h, any occurrence of that macro in an #if or #elif
+# directive is replaced with a literal 0.
+#
+# Almost no attempt is made to understand C syntax so the replacement is very
+# simplistic, and won't work well in many cases. Inspect the output!
+#
+# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+# This is free software: you are free to do what the fuck you want to.
+# There is NO WARRANTY, to the extent permitted by law.
+
+BEGIN {
+ filenum = 0;
+ idclass = "[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0-9_]";
+}
+
+FNR == 1 { filenum++; }
+filenum == 1 && $1 == "#define" && $2 !~ /[(]/ {
+ key = $2;
+ $1 = $2 = "";
+ sub(/^ */, "", $0);
+ config[key] = $0 " /* " key " */";
+}
+
+# Autoconf comments out #undef lines in config.h, so we're looking for
+# /* #undef FOO */
+filenum == 1 && $2 == "#undef" {
+ undef[$3] = 0 " /* " $3 " */";
+}
+
+filenum == 2 {
+ CHANGED = 0;
+ tok = $0;
+
+ # Mark identifier boundaries to avoid replacing substrings. Use ` as
+ # it is not valid in C code (outside of string/character literals).
+ gsub(idclass"+", "`&`", tok);
+
+ # Replace all occurrences of configuration macros normally.
+ tok = do_replace(tok, config);
+
+ # Replace explicitly undefined configuration macros in #if/#elif
+ if ($0 ~ /^[ \t]*#[ \t]*(el)?if[ \t]/)
+ tok = do_replace(tok, undef);
+
+ if (CHANGED) {
+ gsub(/`/, "", tok);
+ print tok;
+ } else {
+ print $0;
+ }
+}
+
+function do_replace(s, config, result, t)
+{
+ while (match(s, "`" idclass "+`")) {
+ t = substr(s, RSTART+1, RLENGTH-2);
+ if (t in config) {
+ result = result substr(s, 1, RSTART - 1) config[t];
+ CHANGED = 1;
+ } else {
+ result = result substr(s, 1, RSTART + RLENGTH - 1);
+ }
+
+ s = substr(s, RSTART + RLENGTH);
+ }
+
+ return result s;
+}