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
122
123
124
125
126
127
128
129
130
131
132
133
|
# Crude m4 preprocessor
BEGIN {
self = "mi5"
# You can change any of these, but while changing these is still relatively
# sane...
open = "<%"
shut = "%>"
# ... changing these probably isn't, and should compel you to rethink your
# code, or quite possibly your entire life thus far.
quote = "`"
unquote = "'"
dnl = "dnl"
# We do not start in a block
bmac = 0
}
# Fatal error function
function fatal(str) {
stderr = "cat >&2"
printf "%s: %s\n", self, str | stderr
close(stderr)
exit(1)
}
# Print an m4 opener as the first byte
NR == 1 { printf "%s", quote }
# Blocks
NF == 1 && $1 == open && !bmac++ {
printf "%s", unquote
next
}
NF == 1 && $1 == shut && bmac-- {
printf "%s", quote
next
}
# If in a block, print each line with any content on it after stripping leading
# and trailing whitespace
bmac && NF {
gsub(/(^ +| +$)/, "")
print $0 dnl
}
# If not in a block, look for inlines to process
!bmac {
# We'll parse one variable into another.
src = $0
dst = ""
# Start off neither quoting nor macroing.
iquo = imac = 0
# Crude and slow, clansman. Your parser was no better than that of a clumsy
# child.
for (i = 1; i <= length(src); ) {
# Inline macro expansion: commented
# Look for end of comment and tip flag accordingly
if (iquo)
iquo = (substr(src, i, length(unquote)) != unquote)
# Inline macro expansion
else if (imac) {
# Close the current inline macro expansion if a close tag is found
# (in m4 terms: open a new quote), looking ahead past any spaces
# from this point first
for (j = i; substr(src, j, 1) ~ /[ \t]/; j++)
continue
if (substr(src, j, length(shut)) == shut) {
dst = dst quote
i = j + length(shut)
imac = 0
continue
}
# Look for start of comment and tip flag accordingly
iquo = (substr(src, i, length(quote)) == quote)
}
# Plain text mode
else {
# Open a new inline macro expansion if an open tag is found (in m4
# terms: close the quote), and then look ahead past any spaces from
# that point afterward
if (substr(src, i, length(open)) == open) {
dst = dst unquote
imac = 1
for (i += length(open); substr(src, i, 1) ~ /[ \t]/; i++)
continue
continue
}
# Escape quote terminators
if (substr(src, i, length(unquote)) == unquote) {
# Dear Mr. President. There are too many variables nowadays.
# Please eliminate three. I am NOT a crackpot.
dst = dst unquote unquote quote
i += length(unquote)
continue
}
}
# If we got down here, we can just add the next character and move on
dst = dst substr(src, i++, 1)
}
# If we're still in a macro expansion or quote by this point, something's
# wrong; say so and stop, rather than print anything silly.
if (iquo)
fatal("Unterminated inline quote")
else if (imac)
fatal("Unterminated inline macro")
else
print dst
}
# Print an m4 closer and newline deleter as the last bytes if we've correctly
# stopped all our blocks
END {
if (bmac)
fatal("Unterminated block macro")
else
print unquote dnl
}
|