aboutsummaryrefslogtreecommitdiff
path: root/bin/mi5.awk
blob: 0a00d1d7c58f2aab01957c9491e541538e52286b (plain) (blame)
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
}