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