chiark / gitweb /
maint-utils/keysubst: A monstrously unpleasant sed hack.
[tripe] / maint-utils / keysubst
diff --git a/maint-utils/keysubst b/maint-utils/keysubst
new file mode 100644 (file)
index 0000000..6d7b5f1
--- /dev/null
@@ -0,0 +1,84 @@
+#! /bin/sh
+
+## This sed(1) script takes key/value pairs in tripe-admin(5) format as
+## input; it writes sh(1) variable assignments as output.  The idea is that
+## you can say
+##
+##     eval $(tripectl algs | sed "$keysubst")
+##
+## and have shell variables set from the output of the command.  The shell
+## variable names are worked out from the key names, with sequences of
+## nonalphanumerics replaced by single underscores.  Quoting is handled
+## correctly, both on input and output.
+##
+## Theory of operation: firstly, ! and @ characters in the input are replaced
+## by !e and !a respectively; this is a reversible transformation, which we
+## undo at the end.  We now know that the input does not contain @.  We use a
+## string of the form [lr][nqQ]@ as a cursor to move across the input from
+## left to right.  The letters on the left of the @ are state markers: l
+## means left of =, r means right; n means not in quotes; q means single-
+## quoted; Q means double-quoted.
+##
+## This is a particularly awful hack.  I'm so sorry.
+keysubst='
+       ## Initial transformation.
+       s/!/!e/g'$p'
+       s/@/!a/g'$p'
+
+       ## Initial state: left-hand side, not quoted
+       s/^/ln@/'$p'
+
+:loop
+       ## Reached the end: exit.
+       /@$/ b end
+
+       ## Enter or leave quoted state as necessary.
+       s/\(.\)n@'\''/\1q@/'$p'
+       s/\(.\)n@"/\1Q@/'$p'
+       s/\(.\)q@'\''/\1n@/'$p'
+       s/\(.\)Q@"/\1n@/'$p'
+       t loop
+
+       ## Leave backslashed things alone.
+       /\(..\)@\\/ {
+         s//\1@/
+         b lit
+       }
+
+       ## Unquotes spaces delimit key/value pairs.
+       s/\(rn\)@[      ]\+/'\'' ln@/'$p'
+
+       ## An = sign separates left and right sides.
+       s/l\(.\)@=/='\''r\1@/'$p'
+
+       ## Single quotes on the right-hand side need special care.
+       t loop
+       s/r\(.\)@'\'/\''\\'\'\''r\1@/'$p'
+
+       ## Sequences of alphanumerics are always easy.
+       s/\(..\)@\([[:alnum:]]\+\)/\2\1@/'$p'
+
+:lit
+       ## Convert bad left-hand-side characters to underscores.
+       t loop
+       s/l\(.\)@\(!.\|[^=![:alnum:]]\)\+/_l\1@/'$p'
+
+       ## Convert sequences of nonproblematic right-hand-side characters.
+       t loop
+       s/\(r.\)@\([^   '\''"`]\+\)/\2\1@/'$p'
+
+       ## And anything else is probably OK.
+       s/\(..\)@\(.\)/\2\1@/'$p'
+       b loop
+
+:end
+       ## Unquote the end appropriately.
+       s/r.@/'\''/'$p'
+       s/..@//'$p'
+
+       ## And undo the initial transformation.
+       s/!a/@/g'$p'
+       s/!e/!/g'$p'
+'
+
+sed "$keysubst"