213 lines
5.1 KiB
Plaintext
213 lines
5.1 KiB
Plaintext
# first pass: macro expansion and .if
|
|
# We support macros, conditionals (of three quite limited forms), and macro
|
|
# argument substitution.
|
|
BEGIN {
|
|
curmacro = ""
|
|
macros[""] = 0 # just to make it an array
|
|
macrolen[""] = 0 # just to make it an array
|
|
macrotext[0] = "" # just to make it an array
|
|
args[""] = "" # just to make it an array
|
|
ntext = 1 # first slot in macrotext; cannot be 0
|
|
nroffset = 0 # offset between NR and "real" line numbers
|
|
inname = "?" # input filename
|
|
sp = 0 # stack "pointer" (number of stacked macros)
|
|
maxsp = 25 # limit on nesting depth
|
|
macrostack[sp] = "" # to make it an array
|
|
nleftstack[sp] = "" # to make it an array
|
|
ptrstack[sp] = "" # to make it an array
|
|
nargstack[sp] = "" # to make it an array
|
|
argstack[sp] = "" # to make it an array
|
|
condstack[sp] = "" # to make it an array
|
|
}
|
|
/^\.\^#/ { # filename and line no of next line: .^# no fn
|
|
nroffset = (NR+1) - $2
|
|
inname = $3
|
|
print
|
|
next
|
|
}
|
|
/^\.de/ { # macro start
|
|
curmacro = "." $2
|
|
macros[curmacro] = ntext
|
|
macrostart = ntext
|
|
next
|
|
}
|
|
curmacro != "" && $0 !~ /^\.\.$/ { # macro text - \\ becomes \
|
|
if ($0 !~ /\\/) # quick case, no backslashes
|
|
line = $0
|
|
else {
|
|
line = ""
|
|
for (n = 1; n <= length; n++) {
|
|
if (substr($0, n, 2) == "\\\\")
|
|
n++
|
|
line = line substr($0, n, 1)
|
|
}
|
|
}
|
|
macrotext[ntext++] = line
|
|
next
|
|
}
|
|
curmacro != "" && $0 ~ /^\.\.$/ { # macro end
|
|
macrolen[curmacro] = ntext - macrostart
|
|
curmacro = ""
|
|
print ".^#", NR - nroffset + 1, inname
|
|
next
|
|
}
|
|
$0 ~ /^\./ && ( macros[$1] != 0 || $0 ~ /^\.(i[ef]|el)/ ) {
|
|
# something that needs attention
|
|
print ".^=", NR - nroffset, inname
|
|
line = $0
|
|
nleft = 0
|
|
macro = "<none>"
|
|
nargs = 0
|
|
|
|
while (line != "") {
|
|
# conditionals; note that 1-n is !n (awk doesn't have !)
|
|
invert = 0
|
|
if (line ~ /^\.i[ef] !/)
|
|
invert = 1
|
|
prevcond = cond
|
|
cond = 0
|
|
if (line !~ /^\.(i[ef]|el)/) { # not conditional
|
|
cond = 1
|
|
iflen = 0
|
|
} else if (line ~ /^\.i[ef] !?\\n\(\.\$[<=>][0-9] /) {
|
|
# arithmetic comparison on arg count
|
|
iflen = length(".if .n(.$=x ") + invert
|
|
n = substr(line, iflen-1, 1) + 0
|
|
op = substr(line, iflen-2, 1)
|
|
if (op == "=" && nargs == n)
|
|
cond = 1
|
|
else if (op == "<" && nargs < n)
|
|
cond = 1
|
|
else if (op == ">" && nargs > n)
|
|
cond = 1
|
|
} else if (line ~ /^\.i[ef] !?'\\\$[0-9]'[^']*' /) {
|
|
# string equality on argument
|
|
iflen = length(".if '.$n'") + invert
|
|
n = substr(line, iflen-1, 1)+0
|
|
if (n <= nargs)
|
|
s1 = args[n]
|
|
else
|
|
s1 = ""
|
|
i = index(substr(line, iflen+1), "'")
|
|
s2 = substr(line, iflen+1, i-1)
|
|
iflen += i+1
|
|
if (s1 == s2)
|
|
cond = 1
|
|
} else if (line ~ /^\.i[ef] !?[nt] /) {
|
|
# nroff vs troff
|
|
iflen = length(".if n ") + invert
|
|
if (substr(line, iflen-1, 1) == "n")
|
|
cond = 1
|
|
} else if (line ~ /^\.el /) {
|
|
cond = 1 - prevcond
|
|
iflen = length(".el ")
|
|
} else {
|
|
line = ".tm unknown .if/.ie form: " line
|
|
cond = 1
|
|
iflen = 0
|
|
}
|
|
if (invert)
|
|
cond = 1 - cond
|
|
if (cond && iflen > 0) # trim true conditional off
|
|
line = substr(line, iflen+1)
|
|
|
|
# do argument substitution, if necessary
|
|
if (cond && line ~ /\\\$/) {
|
|
orig = line
|
|
line = ""
|
|
for (pos = index(orig, "\\$"); pos > 0; \
|
|
pos = index(orig, "\\$")) {
|
|
if (pos > 1)
|
|
line = line substr(orig, 1, pos-1)
|
|
c = substr(orig, pos+2, 1)
|
|
if (c ~ /[0-9]/ && c+0 <= nargs)
|
|
line = line args[c+0]
|
|
orig = substr(orig, pos+3)
|
|
}
|
|
line = line orig # the remnant
|
|
}
|
|
|
|
# is it an nroff command?
|
|
if (cond && line ~ /^\./) {
|
|
cmd = substr(line, 1, 3)
|
|
while (cmd ~ / $/)
|
|
cmd = substr(cmd, 1, length(cmd)-1)
|
|
} else
|
|
cmd = ""
|
|
|
|
# deal with it
|
|
if (!cond)
|
|
nop = 0 # nothing
|
|
else if (cmd == "" || macros[cmd] == 0)
|
|
print line # not a nested macro
|
|
else if (sp >= maxsp)
|
|
print ".tm macros nested too deeply (" sp " levels)"
|
|
else { # nesting
|
|
# stack old one
|
|
sp++
|
|
nleftstack[sp] = nleft
|
|
ptrstack[sp] = ptr
|
|
macrostack[sp] = macro
|
|
nargstack[sp] = nargs
|
|
condstack[sp] = cond
|
|
for (i = 1; i <= nargs; i++)
|
|
argstack[sp ":" i] = args[i]
|
|
|
|
# start new one, mostly pulling arguments apart
|
|
macro = cmd
|
|
nleft = macrolen[macro]
|
|
ptr = macros[macro]
|
|
cond = 0
|
|
argno = 1
|
|
pos = length(macro) + 1
|
|
for (;;) {
|
|
while (substr(line, pos, 1) ~ /[ \t]/)
|
|
pos++
|
|
if (pos > length(line))
|
|
break # NOTE BREAK OUT
|
|
arg = ""
|
|
if (substr(line, pos, 1) == "\"") {
|
|
pos++
|
|
while (substr(line, pos, 1) ~ /[^"]/) {
|
|
arg = arg substr(line, pos, 1)
|
|
pos++
|
|
}
|
|
pos++
|
|
} else
|
|
while (substr(line, pos, 1) ~ /[^ \t]/) {
|
|
arg = arg substr(line, pos, 1)
|
|
pos++
|
|
}
|
|
args[argno++] = arg
|
|
}
|
|
nargs = argno - 1
|
|
}
|
|
|
|
# clean up any completed macros
|
|
while (nleft <= 0 && sp > 0) {
|
|
nleft = nleftstack[sp]
|
|
ptr = ptrstack[sp]
|
|
macro = macrostack[sp]
|
|
nargs = nargstack[sp]
|
|
cond = condstack[sp]
|
|
for (i = 1; i <= nargs; i++)
|
|
args[i] = argstack[sp ":" i]
|
|
sp--
|
|
}
|
|
|
|
# finally, get next line
|
|
if (nleft > 0) {
|
|
line = macrotext[ptr++]
|
|
nleft--
|
|
} else
|
|
line = "" # signal loop to terminate
|
|
}
|
|
|
|
print ".^#", NR - nroffset + 1, inname
|
|
next
|
|
}
|
|
{
|
|
# it's ordinary
|
|
print
|
|
}
|