# 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 = "" 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 }