-
Notifications
You must be signed in to change notification settings - Fork 1
/
unroll.sh
executable file
·122 lines (109 loc) · 3.24 KB
/
unroll.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/bin/bash
#
# vim: ft=bash :
#
# A simple shell based macro expander.
# THIS IS NOT SAFE TO UNTRUSTED INPUT!
#
# First character of the line defines what to do:
#
# # comment, ignored
# :name define macro
# !name set input for macro
# @name run macro
#
# The macro is defined as a shell function.
# The macro lines (:) are appended to each other for this.
#
# Macros with ':' in their name use input from everything before this ':',
# so you can parse input multiply with different macro commands.
#
# Input is gathered until the macro is invoked. Input which comes later
# is appended for later macro calls.
#
# Usage: unroll.sh input input input > output
STDOUT() { printf '%q' "$1"; printf ' %q' "${@:2}"; printf '\n'; }
STDERR() { STDOUT "$@" >&2; }
OOPS() { STDERR OOPS: "$@"; exit 23; }
o() { "$@" || OOPS fail $?: "$@"; }
D() { STDERR DEBUG: "$@"; }
D() { :; }
declare -A MACROS
macro_set()
{
D macro_set "$@"
read -r name cmd <<<"$1" || OOPS cannot parse macro name: "$1"
IFS='$' read -ra args <<<"$cmd" || OOPS cannot parse macro args: "$1" "$cmd"
printf -v esc %q "$args"
out=("$esc")
p=
for a in "${args[@]:1}"
do
p="$p$a"
case "$p" in
([1-9]*) n="${p%%[^0-9]*}"; p="${p#"$n"}"; out+=("\"\${$n}\"");; # $NNN => ${NNN}
(\[*\]*) n="${p%%\]*}" # $[calc]
n="${n#\[}"
p="${p#"[$n]"}"
case "$n" in
(*[^0-9$+\*/\(\)\ -]*) OOPS wrong character in expansion: "\$[$n]";; # we only support simple calculations
esac
out+=("\"\$[$n]\"")
;;
(\[*) p="$p\$"; continue;; # $[xxx$ without ]
(*) out+=("'$'");; # else
esac
printf -v esc %q "$p"
out+=("$esc")
p=
done
[ -z "$p" ] || OOPS unterminated '$[' sequence
MACROS["$name"]="${MACROS["$name"]}"$' &&\n printf %s '"${out[*]}"$' &&\n echo'
}
declare -A INPUTS
input_add()
{
D input_add "$@"
read -r name cmd <<<"$1" || OOPS cannot parse input: "$1"
INPUTS["$name"]="${INPUTS["$name"]}"$'\n'"$cmd"
}
macro_run()
{
D macro_run "$@"
read -r name <<<"$1" || OOPS invalid macro name: "$1"
IN="${INPUTS["${name%%:*}"]}"
[ -n "$IN" ] || OOPS missing input 'for' "$name"
macro="${MACROS["$name"]}"
[ -n "$macro" ] || OOPS missing macro "$name"
vars="${VARS[@]}"
macro() { . <(echo "$vars"); . <(echo ":$macro"); } || OOPS macro definition fail: "$name";
# echo "$macro" >&2
while read -ru6 line
do
[ -n "$line" ] || continue
read -ra args <<<"$line"
macro "${args[@]}" || { printf 'Args: %q\n' "${args[@]}"; echo "Macro:"; cat -n <<<":$macro"; macro "${args[@]}"; OOPS macro fail: "$name"; } >&2
done 6<<<"$IN"
}
declare -A VARS
vars_set()
{
D vars_set "$@"
read -r var def <<<"$1" || OOPS invalid variable definition: "$1"
o printf -v quote %q "$def"
VARS["$var"]="$var=$quote"
}
STDOUT // DO NOT EDIT. Automatically generated by "$0" "$@"
STDOUT // src $(sha256sum -- "$@")
while IFS= read -ru6 line
do
case "$line" in
('#'*) continue;;
(':'*) macro_set "${line#':'}";;
('!'*) input_add "${line#'!'}";;
('@'*) macro_run "${line#'@'}";;
('='*) vars_set "${line#'='}";;
('') ;;
(*) echo "$line";;
esac <&6-
done 6< <(cat -- "$@")