#!/usr/bin/perl
# htroff.pl: troff-like text formatter for html output
# Copyright (C) TOYODA Eizi, 1998.  All rights reserved.
# see COPYING.TXT for terms of license.

	# number/string registers
	%NUMBER = ("font", 0, "unify", 0, "fillin", 1, "center", 0);
	%STRING = ("font", "", "lastfont", "");
	%OPTION = ();

	# environment detection
	$MSDOS = $NUMBER{"msdos"} = &MSDOS;
	$NUMBER{"jperl"} = &JPERL;
	$Japanese'NOCONV = $NUMBER{"jperl"};
	$Japanese'OUT = $MSDOS ? "Shift_JIS" : "EUC-JP";
	$RCSID = q$Id: htroff.pl,v 1.3 1999/08/28 13:10:14 toyoda Exp $;
	&initFindPath;

	# common variables and messages
	$FOREVER = 100000;
	$ME = "htroff:";
	$CO = "cannot open";
	$NF = "not found";
	$UE = "unexpected";

	# .ig support
	$IGNORE_UNTIL = undef;

	# macro support
	@REQARG = ();		# request arguments: for internal work
	@MACROARG = ();		# macro arguments: for \\$n
	$DEFINE_UNTIL = "\\.";	# failsafe
	$DEFMACRO = undef;	# if TRUE, lines are stored as macro
	%MACROS = ();		# LF separated macro lines
	%ALIAS = ('cleanup', 'nop', 'opened', 'nop');
		# request/macro aliases

	# pattern-matching request hook
	%HOOK = ('^\s*$', 'tag P');

	# conditional support
	$IFVAL = 1;		# if FALSE lines are ignored
	@IFVAL = ();		# $IFVAL of outer block is pushed
	@ELSE = ();		# if TRUE "else" lines are ignored

	# input/output streams
	@PUSHED_INPUT = ();
	@INPUT = ();
	$STRING{"<"} = $INPUT = "";
	$STRING{">"} = $OUTPUT = "STDOUT";

	# for better diag
	%LINE_COUNTER = ();
	$MACROS_DONE = 0;

	# predefined number registers
	($NUMBER{"sec"}, $NUMBER{"min"}, $NUMBER{"hour"}, $NUMBER{"dy"},
	 $NUMBER{"mo"}, $NUMBER{"yr"}, $NUMBER{"dw"}, $NUMBER{"yday"},
	 $NUMBER{"dst"}) = localtime time;
	$STRING{"month"} = ("January", "February", "March", "April",
		"May", "June", "July", "August", "September", "October",
		"November", "December")[$NUMBER{"mo"}];
	$NUMBER{"mo"}++;
	$NUMBER{"year"} = $NUMBER{"yr"} + 1900;

	&RegisterMacro("std");
	while ($_ = shift @ARGV) {
		(!/^-/ || /^-$/) && (unshift(@ARGV, $_), last);
		/^--$/ && last;
		/^-m(.*)/ && (&RegisterMacro($1), next);
		/^-o(.*)/ && (&SetOutput($1), next);
		(/^-r(.*)=/ || /^-r(.)/) && ($NUMBER{$1} = $', next);
		(/^-d(.*)=/ || /^-d(.)/) && ($STRING{$1} = $', next);
		/^-V/ && die "$RCSID\n";
		# unknown options are stored for macro use.
		/^-q(.)/ && ($OPTION{$1} = $', next);
		/^-(.)/;
		$OPTION{$1} = $';
	}
	push(@INPUT, 'end of macros');
	push(@INPUT, @ARGV);

	# event loop
	$CONTINUE = "";
	while ($_ = &getline) {
		&chop;
		$_ = "$CONTINUE$_" unless ($CONTINUE eq "");
		unless (/\\\\$/) {
			s/\\$// && ($CONTINUE = $_, next);
		}
		&ProcessLine;
		$CONTINUE = "";
	}
	&Request(".cleanup");
exit 0;

sub RegisterMacro {
	local($macroname) = @_;
	$OPTION{"-m$macroname"} = 1;
	$OPTION{"-m"} .= "-m$macroname ";
	push(@INPUT, (&FindFile("$macroname.hma") || "DATA::$macroname"));
}

# give line in $_
sub ProcessLine {
	&ExpandEscape;

	# .ig support
	if (defined $IGNORE_UNTIL && /^\.$IGNORE_UNTIL/) {
		$IGNORE_UNTIL = undef;
		return;
	}
	return if defined $IGNORE_UNTIL;

	# macro definition support
	if ($DEFMACRO && /^\0?['.]$DEFINE_UNTIL\s*$/) {	# end macro line
		$DEFMACRO = undef;
		return;
	}
	if ($DEFMACRO) {			# begin macro line
		# removing NULs.
		s/\0//g;
		$MACROS{$DEFMACRO} .= "$_\n";
		return;
	}

	# nested if support
	if (/^\0?['.]{\s/) {			# if line
		push(@IFVAL, $IFVAL);
		$IFVAL = $IFVAL && &Boolean($');
		push(@ELSE, $IFVAL);
		return;
	}
	if (/^\0?['.]}{$/ || /^\0?['.]}{\s/) {	# elsif line
		(scalar(@ELSE) == 0) && (warn("$ME $UE .}{"), return);
		$ELSE[$#ELSE] && ($IFVAL = 0, return);
		$ELSE[$#ELSE] = $IFVAL = &Boolean($');
		return;
	}
	if (/^\0?['.]}\s*$/) {			# endif line
		if (scalar @ELSE == 0) {
			warn "$ME .} without .{\n";
			return;
		}
		pop(@ELSE);
		$IFVAL = pop(@IFVAL);
		return;
	}
	$IFVAL || return;

	# normal request/macro lines
	/^['.]/ && return &Request($_);

	# pattern-matching request hook
	$STRING{"line"} = $_;
	foreach (keys %HOOK) {
		$STRING{'line'} =~ /$_/ || next;	
		$STRING{'hook'} = $_;   $STRING{'pre'} = $`;
		$STRING{'match'} = $&;  $STRING{'post'} = $';
		return &Request(".$HOOK{$_}");
	}

	&writeln($_);
	&CheckFont;
	&CheckUnify;
	&CheckCenter;
}

sub ArgSplit {
	local($_) = @_;
	local(@arg) = ();
	while (length) {
		if (/^"(([^"]|"")*)"\s*/) {
			local($match) = $1;
			$_ = $';
			$match =~ s/""/"/g;
			push(@arg, $match);
		} elsif (/^"/) {
			push(@arg, $');
			$_ = '';
		} elsif (/(\S+)\s*/) {
			push(@arg, $1);
			$_ = $';
		} else {
			last;
		}
	}
	@arg;
}

sub ExprList {
	local($rhs, $lhs);
	local(@EXPR) = (0);
	for (@_) {
		# --- numerical operators
		if ($_ eq "sub") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] -= $rhs;
		} elsif ($_ eq "add") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] += $rhs;
		} elsif ($_ eq "inc") {
			$EXPR[$#EXPR]++;
		} elsif ($_ eq "dec") {
			$EXPR[$#EXPR]--;
		} elsif ($_ eq "equiv") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] == $rhs);
		} elsif ($_ eq "posi") {
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] > 0);
		} elsif ($_ eq "nega") {
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] < 0);
		# --- string operators
		} elsif ($_ eq "length") {
			$EXPR[$#EXPR] = length $EXPR[$#EXPR];
		} elsif ($_ eq "cat") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] .= $rhs;
		} elsif ($_ eq "eq") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] eq $rhs);
		} elsif ($_ eq "lowercase") {
			$EXPR[$#EXPR] =~ tr/A-Z/a-z/;
		} elsif ($_ eq "uppercase") {
			$EXPR[$#EXPR] =~ tr/a-z/A-Z/;
		} elsif ($_ eq "grep" || $_ eq "subpat") {
			$rhs = pop(@EXPR);
			$rhs =~ s/\0//g;
			$EXPR[$#EXPR] =~ /$rhs/;
			$EXPR[$#EXPR] = $& if ($_ eq "grep");
			$EXPR[$#EXPR] = $1 if ($_ eq "subpat");
		} elsif ($_ eq "match") {
			$rhs = pop(@EXPR);
			$rhs =~ s/\0//g;
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] =~ /$rhs/);
		} elsif ($_ eq "sub" || $_ eq "gsub") {
			$rhs = pop(@EXPR);
			$rhs =~ s/\0//g;
			$lhs = pop(@EXPR);
			$lhs =~ s/\0//g;
			$EXPR[$#EXPR] =~ s/$lhs/$rhs/ if ($_ eq "sub");
			$EXPR[$#EXPR] =~ s/$lhs/$rhs/g if ($_ eq "gsub");
		} elsif ($_ eq "file") {
			$EXPR[$#EXPR] = "" unless (-f $EXPR[$#EXPR]);
		} elsif ($_ eq "dir") {
			$EXPR[$#EXPR] = "" unless (-d $EXPR[$#EXPR]);
		} elsif ($_ eq "basename") {
			$EXPR[$#EXPR] =~ /$SUBPAT/;
			$EXPR[$#EXPR] = $1;
		} elsif ($_ eq "dirname") {
			$EXPR[$#EXPR] =~ s/$SUBPAT//;
		# --- boolean operators
		} elsif ($_ eq "not") {
			$EXPR[$#EXPR] = !$EXPR[$#EXPR];
		} elsif ($_ eq "and") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] && $rhs);
		} elsif ($_ eq "or") {
			$rhs = pop(@EXPR);
			$EXPR[$#EXPR] = ($EXPR[$#EXPR] || $rhs);
		} elsif ($_ eq "defined") {
			$EXPR[$#EXPR] = defined $EXPR[$#EXPR];
		# --- constants
		} elsif ($_ eq "undef") {
			push(@EXPR, undef);
		} elsif ($_ eq "null") {
			push(@EXPR, "");
		} elsif ($_ eq "end") {
			push(@EXPR, 0);
		} elsif ($_ eq "begin") {
			push(@EXPR, $FOREVER);
		# --- system operators
		} elsif ($_ eq "option") {
			$EXPR[$#EXPR] = $OPTION{$EXPR[$#EXPR]};
		} elsif ($_ eq "env") {
			$EXPR[$#EXPR] = $ENV{$EXPR[$#EXPR]};
		} elsif ($_ eq "string") {
			$EXPR[$#EXPR] = $STRING{$EXPR[$#EXPR]};
		} elsif ($_ eq "number") {
			$EXPR[$#EXPR] = $NUMBER{$EXPR[$#EXPR]};
		} elsif ($_ eq "macro") {
			$EXPR[$#EXPR] = $MACROS{$EXPR[$#EXPR]};
		} elsif ($_ eq "alias") {
			$EXPR[$#EXPR] = $ALIAS{$EXPR[$#EXPR]};
		} elsif ($_ eq "hook") {
			$EXPR[$#EXPR] = $HOOK{$EXPR[$#EXPR]};
		} elsif ($_ eq "arg") {
			$EXPR[$#EXPR] = $REQARG[$EXPR[$#EXPR]];
		} elsif ($_ eq "args") {
			push(@EXPR, scalar(@REQARG));
		} elsif ($_ eq "dup") {
			push(@EXPR, $EXPR[$#EXPR]);
		} elsif ($_ eq "pop") {
			pop(@EXPR);
		} elsif ($_ eq "exch") {
			$rhs = pop(@EXPR);
			$lhs = pop(@EXPR);
			push(@EXPR, $rhs, $lhs);
		} elsif ($_ eq "debug") {
			$_ = "TOS=`$EXPR[$#EXPR]'\n";
			&Japanese'EUCToPrintable;
			warn $_;
		} elsif ($_ eq ";") {
			1;		# do nothing
		} else {
			s/^'//;
			push(@EXPR, $_);
		}
	}
	$EXPR[$#EXPR];
}

sub Boolean {
	local($argl) = @_;
	return 1 if ($argl =~ /^\s*$/);
	&ExprList(&ArgSplit($argl));
}

sub SetFont {
	local($name, $lines) = @_;
	local($ret) = "";
	if ($name eq "P") {
		$name = $STRING{"lastfont"};
		$lines = 0 if ($name eq "");
	}
	$STRING{"lastfont"} = $STRING{"font"};
	if ($lines <= 0) {
		return "" if ($STRING{"font"} eq "");
		$ret = "\\</$STRING{'font'}\\>";
		$STRING{"font"} = "";
		$NUMBER{"font"} = 0;
	} else {
		$ret = "\\</$STRING{'font'}\\>" if ($STRING{"font"} ne "");
		$ret .= "\\<$name\\>";
		$STRING{"font"} = $name;
		$NUMBER{"font"} = $lines;
	}
	$ret;
}

sub CheckFont {
	return if ($NUMBER{"font"} <= 0);
	return if (--$NUMBER{"font"} > 0);
	&tag("/$STRING{'font'}");
	$STRING{"font"} = "";
}

sub SetCenter {
	local($lines) = @_;
	&tag("DIV ALIGN=CENTER") if ($lines && !$NUMBER{"center"});
	&tag("/DIV") if (!$lines && $NUMBER{"center"});
	$NUMBER{"center"} = $lines;
}

sub CheckCenter {
	return if ($NUMBER{"center"} <= 0);
	&tag("BR");
	return if (--$NUMBER{"center"} > 0);
	&tag("/DIV");
}

sub CheckUnify {
	return if ($NUMBER{"unify"} <= 0);
	return if (--$NUMBER{"unify"} > 0);
	&writeln("");
}

sub Request {
	local($request) = @_;
	$request =~ s/^['.]\s*//;
	return if ($request eq "");
	local(@REQARG) = &ArgSplit($request);
	$reqname = shift @REQARG;
	$reqname = $ALIAS{$reqname} if ($ALIAS{$reqname});

	# troff built-in (primitive) requests
	if (defined $MACROS{$reqname}) {
		local(@macro) = split(/\n/, $MACROS{$reqname});
		local(@MACROARG) = @REQARG;
		foreach (@macro) {
			&ProcessLine;
		}
	} elsif ($reqname eq "ig") {
		$IGNORE_UNTIL = $REQARG[0] || "\\.";
	} elsif ($reqname eq "am") {
		$DEFINE_UNTIL = $REQARG[1] || "\\.";
		$DEFMACRO = $REQARG[0];
	} elsif ($reqname eq "de") {
		$DEFINE_UNTIL = $REQARG[1] || "\\.";
		$DEFMACRO = $REQARG[0];
		$MACROS{$DEFMACRO} = undef;
	} elsif ($reqname eq "rm") {
		$MACROS{$REQARG[0]} = undef;
	} elsif ($reqname eq "nr") {
		$numreg_name = shift(@REQARG);
		$NUMBER{$numreg_name} = shift(@REQARG);
		$NUMBER{"+$numreg_name"} = shift(@REQARG);
	} elsif ($reqname eq "nrexpr") {
		$numreg_name = shift(@REQARG);
		$NUMBER{$numreg_name} = &ExprList(@REQARG);
	} elsif ($reqname eq "ds") {
		$strreg_name = shift(@REQARG);
		$STRING{$strreg_name} = join(" ", @REQARG);
		$STRING{$strreg_name} =~ s/\0//g;
	} elsif ($reqname eq "dsexpr") {
		$strreg_name = shift(@REQARG);
		$STRING{$strreg_name} = &ExprList(@REQARG);
		$STRING{$strreg_name} =~ s/\0//g;
	} elsif ($reqname eq "as") {
		$strreg_name = shift(@REQARG);
		$STRING{$strreg_name} .= join(" ", @REQARG);
		$STRING{$strreg_name} =~ s/\0//g;
	} elsif ($reqname eq "asexpr") {
		$strreg_name = shift(@REQARG);
		$STRING{$strreg_name} .= &ExprList(@REQARG);
		$STRING{$strreg_name} =~ s/\0//g;
	} elsif ($reqname eq "bp") {
		&tag("HR");
	} elsif ($reqname eq "br") {
		&tag("BR");
	} elsif ($reqname eq "sp") {
		local($times) = $REQARG[0] || 1;
		for ($i = 0; $i < $times; $i++) {
			&tag("BR");
		}
	} elsif ($reqname eq "nf") {
		$NUMBER{"unify"} = 0;
		&tag("PRE") if $NUMBER{"fillin"};
		$NUMBER{"fillin"} = 0;
	} elsif ($reqname eq "fi") {
		&tag("/PRE") unless $NUMBER{"fillin"};
		$NUMBER{"fillin"} = 1;
	} elsif ($reqname eq "so") {
		push(@PUSHED_INPUT, $INPUT);
		$STRING{"<"} = $INPUT = $REQARG[0];
		open($INPUT, "<$INPUT") || warn "$ME <$INPUT $CO\n";
	} elsif ($reqname eq "ce") {
		&SetCenter($REQARG[0] || 1);
	} elsif ($reqname eq "ul") {
		$UNIFY_ONE_LINE = 1;
		&writeln(&SetFont("U", ((@REQARG > 0) ? $REQARG[0] : 1)));
	# --- htroff original requests
	} elsif ($reqname eq "it") {
		$UNIFY_ONE_LINE = 1;
		&writeln(&SetFont("I", ((@REQARG > 0) ? $REQARG[0] : 1)));
	} elsif ($reqname eq "bf") {
		$UNIFY_ONE_LINE = 1;
		&writeln(&SetFont("B", ((@REQARG > 0) ? $REQARG[0] : 1)));
	} elsif ($reqname eq "shift") {
		shift(@MACROARG);
	} elsif ($reqname eq "getline") {
		($_ = &getline) || return;
		&chop;
		@MACROARG = &ArgSplit($_);
	} elsif ($reqname eq "input") {
		unshift(@INPUT, $REQARG[0]);
	} elsif ($reqname eq "output") {
		&SetOutput($REQARG[0]);
	} elsif ($reqname eq "append") {
		&SetAppend($REQARG[0]);
	} elsif ($reqname eq "keep") {
		$STRING{"keep"} = $REQARG[0];
		delete $STRING{"keep"} if (@REQARG < 1);
	} elsif ($reqname eq "alias") {
		$ALIAS{$REQARG[0]} = $REQARG[1];
		delete $ALIAS{$REQARG[0]} if (@REQARG < 2);
	} elsif ($reqname eq "hook") {
		$REQARG[0] =~ s/\0//g;
		$HOOK{$REQARG[0]} = $REQARG[1];
		delete $HOOK{$REQARG[0]} if (@REQARG < 2);
	} elsif ($reqname eq "char") {
		$char_name = shift(@REQARG);
		$CHAR{$char_name} = shift(@REQARG);
	} elsif ($reqname eq "tag") {
		&tag(@REQARG);
	} elsif ($reqname eq "warn") {
		$_ = join(" ", @REQARG);
		&Japanese'EUCToPrintable;
		warn "$_\n";
	} elsif ($reqname eq "write") {
		$_ = join(" ", @REQARG);
		&Japanese'EUCToPrintable;
		&write($_);
	} elsif ($reqname eq "exit") {
		exit &ExprList(@REQARG);
	} elsif ($reqname eq "checkopt") {
		&CheckOpt($REQARG[0]);
	} elsif ($reqname eq "nop") {
		1;	# do nothing
	} else {
		warn "$ME undefined request $reqname.\n";
	}
}

sub CheckOpt {
	local($onechar) = @_;
	$NUMBER{"!"} = 0;
	foreach (keys %OPTION) {
		next if (length $_ == 1) && (index($onechar, $_) >= 0);
		next if /^-/;
		warn "$ME undefined option -$_ used\n";
		$NUMBER{"!"} = 1;
	}
}

sub initFindPath {
	# path delimiter
	$PATHDELIM = $MSDOS ? ";" : ":";
	@FINDPATH = split(/$PATHDELIM/, $ENV{"PATH"});
	unshift @FINDPATH, split(/$PATHDELIM/, $ENV{"FMAC"});
	if (-e $0) {
		# pattern for directory last part
		$SUBPAT = $MSDOS ? '[/\\\\]([^/\\\\]+)$' : '/([^/]+)$';
		($basedir = $0) =~ s/$SUBPAT//;
		unshift(@FINDPATH, $basedir);
	}
}

sub FindFile {
	local($fnam) = @_;
	local($try);
	return $fnam if -f $fnam;
	foreach (@FINDPATH) {
		$try = "$_/$fnam";
		return $try if (-e $try && -r $try);
	}
	undef;
}

sub SetOutput {
	$STRING{">"} = $OUTPUT = $_[0];
	$NUMBER{"!"} = 0;
	if (open($OUTPUT, ">$OUTPUT")) {
		select($OUTPUT);
	} else {
		warn "$ME >$OUTPUT $CO\n";
		$NUMBER{"!"} = $! + 0;
	}
}

sub SetAppend {
	$STRING{">"} = $OUTPUT = $_[0];
	$NUMBER{"!"} = 0;
	if (open($OUTPUT, ">>$OUTPUT")) {
		select($OUTPUT);
	} else {
		warn "$ME >>$OUTPUT $CO\n";
		$NUMBER{"!"} = $! + 0;
	}
}

sub NextFile {
	close($INPUT) unless ($INPUT eq "DATA");
	for (;;) {
		if (@PUSHED_INPUT) {
			return ($STRING{"<"} = $INPUT = pop(@PUSHED_INPUT));
		}
		return undef unless (@INPUT);
		$STRING{"<"} = $INPUT = shift(@INPUT);
		if ($INPUT eq 'end of macros') {
			$MACROS_DONE = 1;
			next;
		} elsif ($INPUT =~ /^DATA::(.*)/) {
			$macroname = $1;
			$STRING{"<"} = $INPUT = "DATA";
			# skip operation
			for (;;) {
				$line = <$INPUT> || last;
				return $INPUT if $line =~ /^DATA\s+$macroname/;
			}
			warn "$ME macro package $macroname $NF.\n";
		} else {
			open($INPUT, "<$INPUT") && do {
				&Request(".opened $INPUT") if $MACROS_DONE;
				$LINE_COUNTER{$INPUT} = 0;
				return $INPUT;
			};
			warn "$ME <$INPUT $CO\n";
		}
	}
}

sub getline {
	for (;;) {
		$getline = <$INPUT>;
		$NUMBER{"lines"} = $LINE_COUNTER{$INPUT}++;
		next if (($INPUT eq "DATA") && ($getline =~ /^DATA/));
		$getline = ""
			if (($INPUT eq "DATA") && ($getline =~ /^ENDDATA/));
		return $getline if ($getline);
		return undef unless (&NextFile);
	}
}

sub chop {
	s/\r?\n$//;		# in UNIX, MS-DOS newline becomes "\r\n"
	&Japanese'AnyToEUC;
}

sub MacroArgAll {
	local(@arg) = @MACROARG;
	foreach (@arg) {
		s/ /\\0/g;
	}
	join(" ", @arg);
}

# called in head of ProcessLine, so called twice or more for macros.
sub ExpandEscape {
	s/\\\\/\\\0/g;		# "\" guard: removed in &writeln
	s/\\".*//g;		# comment
	s/\\(['.])/\0$1/g;

	# macro argument substitution
	s/\\\$\#/scalar(@MACROARG)/ge;
	s/\\\$\*/join(" ", @MACROARG)/ge;
	s/\\\$\+/&MacroArgAll/ge;
	s/\\\$([1-9])/$MACROARG[$1-1]/g;

	# string register substitution
	s/\\\*\[([^]]+)]/$STRING{$1}/g;
	s/\\\*\((..)/$STRING{$1}/g;
	s/\\\*(.)/$STRING{$1}/g;

	# inline size change --- ignored
	s/\\s[-+]?[0-9]+/\0/g;

	# number register substitution 
	s/\\n\+\[([^]]+)]/$NUMBER{$1} += $NUMBER{"+$1"}/ge;
	s/\\n\[([^]]+)]/$NUMBER{$1}+0/ge;
	s/\\n\+\((..)/$NUMBER{$1} += $NUMBER{"+$1"}/ge;
	s/\\n\((..)/$NUMBER{$1}+0/ge;
	s/\\n\+(.)/$NUMBER{$1} += $NUMBER{"+$1"}/ge;
	s/\\n(.)/$NUMBER{$1}+0/ge;

}

sub write {
	local($_) = @_;

	# inline font change
	s/(\\f[IBUR])+\\fR/\\fR/g;	# optimization hack for .RI etc.
	s/(\\fP\\fP)+//g;		# optimization hack
	while (/\\f[IBURP]/) {
		if ($& eq "\\fI") {
			s/\\fI/&SetFont("I", $FOREVER)/e;
		} elsif ($& eq "\\fB") {
			s/\\fB/&SetFont("B", $FOREVER)/e;
		} elsif ($& eq "\\fU") {
			s/\\fU/&SetFont("U", $FOREVER)/e;
		} elsif ($& eq "\\fP") {
			s/\\fP/&SetFont("P", $FOREVER)/e;
		} else {
			s/\\fR/&SetFont("", 0)/e;
		}
	}

	# upward or downward shift
	$UPDOWN = 0;
	while (/\\[ud]/) {
		if ($& eq "\\u") {
			$UPDOWN++;
			if ($UPDOWN > 0) { s/\\u/\\QLSUP\\QG/; }
			else { s/\\u/\\QL\/SUB\\QG/; }
		} elsif ($& eq "\\d") {
			$UPDOWN--;
			if ($UPDOWN < 0) { s/\\d/\\QLSUB\\QG/; }
			else { s/\\d/\\QL\/SUP\\QG/; }
		}
	}
	while ($UPDOWN > 0) {
		s/$/\\QL\/SUP\\QG/;
		$UPDOWN--;
	}
	while ($UPDOWN < 0) {
		s/$/\\QL\/SUB\\QG/;
		$UPDOWN++;
	}

	# special one-char sequences
	s/\\\&/\0/g;
	s/\\\|/\0/g;
	s/\\\^//g;		# quite narrow space ... better than one SP?
	s/\\l/ /g;
	s/\\-/-/g;
	s/\\ /\\[nbsp]/g;
	s/\\0/\\[nbsp]/g;
	s/\\c$// && ($UNIFY_ONE_LINE = 1);	# continuation mark

	# defined character escape
	s/\\\((..)/$CHAR{$1}/g;

	# HTML's special characters converted to entity refs.
					s/\\</\\QL/g;	s/\\>/\\QG/g;
	s/&/&amp;/g;	s/"/&quot;/g;	s/</&lt;/g;	s/>/&gt;/g;
	s/\\QA/&/g;	s/\\QQ/"/g;	s/\\QL/</g;	s/\\QG/>/g;

	# SGML entity reference escape --- not compatible with groff
	s/\\\[(\w+)]/&$1;/g;

	# this must be ALL escape expansion
	s/\\e/\\/g;	

	s/\t/"&nbsp;" x 4/ge if $NUMBER{"fillin"};
	s/\0//g;		# remove backslash guard
	&Japanese'EUCToPrintable;
	print "$_";
}

sub writeln {
	local($line) = @_;
	if (defined $STRING{'keep'}) {
		$MACROS{$STRING{'keep'}} .= "$line\n";
	} else {
		&write($line);
		print "\n" unless ($NUMBER{"unify"} || $UNIFY_ONE_LINE);
		$UNIFY_ONE_LINE = undef;
	}
}

sub tag {
	local(@arg) = @_;
	&writeln("\\<". join(" ", @arg). "\\>");
}

sub MSDOS {
	# MS-DOS has no /dev/null.
	return 0 if ( -c '/dev/null' );
	# MS-DOS has /DEV/CON even in drive without /DEV.
	return 1 if ( -f '/DEV/CoN' && ! -d '/DEV' );
	# MS-DOS allows CON or NUL have extension.
	return 2 if ( -f '/coN.3b7' && -f '/NuL.j0Q' && -f '/nUl.!#$' );
	0;
}

sub JPERL {
	return 0 unless ("\xE1\xA2" =~ /^.$/);		# normal perl
	return -1 if ("\xA1\xA1" =~ /^.$/);		# EUC jperl
	1;						# SJIS jperl
}


package Japanese;
#
# Japanese conversion package
# usage:
#	1) set $Japanese'OUT to what encoding output used
#	2) set every input line to $_ and call &Japanese'AnyToEUC
#	3) input has no longer any kanji-origin metacharacter like "\*".
#	 so you can safely use it as regular expression.
#	4) set every output line to $_ and call &Japanese'EUCToPrintable
#
#	To turn off conversion, set a nonzero value to $Japanese'NOCONV.
#

sub HtoZ {
	# JIS X 0201 Katakana -> JIS X 0208 conversion
	tr/\xA1-\xFE/\x21-\x7E/;
	tr/\x21-\x25\x30\x5E\x5F/\xA3\xD6\xD7\xA2\xA6\xBC\xAB\xAC/;
	s/[\xA2-\xD7]/\xA1$&/g;
	tr/\x26-\x2F\x31-\x5D/r!\#%')cegC"\$&(*+\-\/13579;=?ADFHJ-NORUX[^-bdfhi-mos/;
	s/[\x21-\x73]/\xA5$&/g;
	tr/\x21-\x73/\xA1-\xF3/;
}

sub block_JtoE {
	if (s/^\(I//) {
		&HtoZ;
		return $_;
	}
	if (s/^\([\@-Z]//) {
		return $_;
	}
	s/^\$[\@B]// || return $_;
	tr/\x21-\x7E/\xA1-\xFE/;
	return $_;
}

sub StoE {
	local($_) = @_;
	if (/^[\xA1-\xDF]/) {
		&HtoZ;
		return $_;
	}
	local($hi, $lo) = unpack("CC", $_);
	$hi -= 0x40 if ($hi > 0x9F);
	$hi -= 0x30;
	$hi *= 2;
	if ($lo <= 0x9E) {
		$lo-- if ($lo >= 0x80);
		$lo += 0x61;
		$hi--;
	} else {
		$lo += 2;
	}
	if ($hi >= 0x115 || $hi == 0x114 && $lo >= 0xBD) {
		$hi -= 0x1B;
		$lo -= 0x1C;
		($lo += 0x5E, $hi--) if ($lo < 0xA1);
	}
	return pack("CC", $hi, $lo);
}

sub EtoS {
	local($c) = @_;
	local($hi, $lo) = unpack("CC", $c);
	if ($hi % 2) {
		$lo -= 0x61;
		$lo++ if ($lo >= 0x7F);
	} else {
		$lo -= 0x02;
	}
	$hi = int(($hi - 1) / 2) + 0x31; 
	$hi += 0x40 if ($hi >= 0xA0);
	return pack("CC", $hi, $lo);
}

sub AnyToEUC {
	return if $NOCONV;
	if (/\x1B[(\$][\@-Z]/) {
		$IN = "ISO-2022-JP";
		$OUT = "ISO-2022-JP" unless $OUT;
		s/\x0E/\x1B\(I/g;
		s/\x0F/\x1B\(B/g;
		@jblock = split(/\x1B/, $_);
		$result = shift @jblock;
		foreach (@jblock) {
			$result .= &block_JtoE;
		}
		$_ = $result;
	}
	if (/[\x81-\x9D][\x81-\xFE]/) {
		$IN = "Shift_JIS";
		$OUT = "Shift_JIS" unless $OUT;
	}
	if ($IN eq "Shift_JIS") {
		s/[\xA0-\xDF]|[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]/&StoE($&)/ge;
	}
	$_;
}

sub block_EtoJ {
	local($_) = @_;
	tr/\xA1-\xFE/\x21-\x7E/;
	s/^/\x1B\x24B/;
	s/$/\x1B\x28B/;
}

sub EUCToPrintable {
	return if $NOCONV;
	$OUT = "(No Conversion)" unless $OUT;
	if ($OUT eq "ISO-2022-JP") {
		s/([\xA1-\xFE][\xA1-\xFE])+/&block_EtoJ($&)/ge;
	} elsif ($OUT eq "Shift_JIS") {
		s/[\xA1-\xFE][\xA1-\xFE]/&EtoS($&)/ge;
	}
}

__END__
DATA std
.\" 
.\" STANDARD MACRO PACKAGE for htroff
.\" Copyright (C) TOYODA Eizi, 1998.  All rights reserved.
.\" see COPYING.TXT for terms of license.
.\" 
.\" INITIALIZATION
.\"
.{ -m option defined not
.checkopt "c"
.}
.\"
.\" <HTML><HEAD>...</HEAD><BODY> INITIALIZER
.\"
.alias	opened stdopened
.nr stdopened 0
.de	stdopened
.{ 'stdopened number not
.nrexpr stdopened 'stdopened number inc
.ds bodyArg ""
.{ 'c option defined
.asexpr bodyArg " BGCOLOR=\\QQ" 'c option "\\QQ" cat cat
.}
.printHeadPart "\\*<" 
.}
..
.de	printHeadPart
.tag HTML
.tag HEAD
.nrexpr unify begin
.tag TITLE
\&\\$1
.tag /TITLE
.nrexpr unify end
.tag /HEAD
.tag BODY \\*[bodyArg]
..
.\"
.\" </BODY> TERMINATOR
.\"
.alias	cleanup	stdcleanup
.ds hooter ""
.de	stdcleanup
.tag HR
.{ 'hooter string length
\\*[hooter]
.}
HTML generated using
.nrexpr unify begin
.tag A HREF='http://www.gfd-dennou.org/arch/cc-env/htroff'
htroff
.nrexpr unify end
.tag /A
at \n[dy] \*[month] \n[year] \n[hour]:\n[min]:\n[sec].
.tag /BODY
.tag /HTML
..
.\"
.\" URL LINE HOOKS
.\"
.de urllink
.nrexpr unify start
.dsexpr a 'line string '\\S* grep
.dsexpr b 'line string '\\S*\\s*(.*) subpat
.tag A HREF=\QQ\\*a\QQ
\&\\*a
.nrexpr unify end
.tag /A
.{ 'b string length
\&\\*b
.}
..
.hook "^http://" urllink
.hook "^ftp://" urllink
.hook "^file:" urllink
.\"
.\" care for <URL:...>
.\"
.de URLlink
.nrexpr unify start
.dsexpr a 'match string "<URL:([^>]+)>" subpat
\&\\*[pre]
.tag A HREF=\QQ\\*a\QQ
\&\\*a
.nrexpr unify end
.tag /A
\&\\*[post]
..
.hook "<URL:[^>]+>" URLlink
.\"
.\" RELATIVE URL
.\"
.de rellink
.dsexpr line 'line string 'relative:(.*) subpat
.urllink
..
.hook "^relative:" rellink
.\"
.\" BLANK-STARTING LINE BREAKS A LINE
.\"
.de breakline
.	br
\&\\*[line]
..
.hook "^\\s+" breakline
.\"
.\" TBL(1) SUPPORT EMULATION
.\"
.de TS
.	tag BLOCKQUOTE
.	tag TABLE BORDER
.	hook "." tblhead
..
.de TE
.	hook "."
.	hook "^\\s+" breakline
.	tag /TABLE
.	tag /BLOCKQUOTE
..
.ds tbltab "	"
.de tblhead
.{ 'line string tab\\(.\\) match
.	dsexpr tbltab 'line string tab.(.). subpat
.}{ 'line string \\\\.$ match
.	hook "." tblmain
.	hook "^\\s+"
.}
..
.de tblmain
.{ 'line string ^[=_]$ match not
.	dsexpr line 'line string 'tbltab string "\\QLTD\\QG" gsub "	" "" gsub
.	hook "."
\\QLTR\\QG\\QLTD\\QG\\*[line]\\QL/TR\\QG
.	hook "." tblmain
.\" .}{
.\" \\QLTR\\QG
.}
..
.\"
.\" BUILT-IN REQUEST EMULATION
.\"
.nr ad-div 0
.de ad
.{ 'ad-div number posi
.	tag /DIV
.	nr ad-div 0
.}
.{ 1 arg 'r eq
.	tag DIV ALIGN=RIGHT
.	nr ad-div 1
.}{ 1 arg 'c eq
.	tag DIV ALIGN=CENTER
.	nr ad-div 2
.}
..
.de if
.{	0 arg 't eq
.}{	0 arg ^'(.*)'.*' subpat ; 0 arg ^'.*'(.*)' subpat ; eq not \
	0 arg ^'.*'.*' match ; and 
.}{
.	shift
\\$*
.}
..
.\"
.\" NOP ALIASES FOR COMPATIBILITY
.\"
.alias	ti	br
.alias	ti0	br
.alias	ta	nop
.alias	in	nop
.alias	ne	nop
.alias	ft	nop	\" set font: 1st arg is in "RBIC"
.alias	hc	nop
.alias	di	nop
.alias	hy	nop
.alias	nh	nop
.\" --- groff compatible character definitions
.\"
.char	-D	Dh
.char	Sd	dh
.char	TP	Th
.char	Tp	th
.char	AE	AE
.char	ae	ae
.char	OE	OE
.char	oe	oe
.char	ss	ss
.char	,c	\[ccedil]
.char	'A	\[Aacute]
.char	'E	\[Eacute]
.char	'I	\[Iacute]
.char	'O	\[Oacute]
.char	'U	\[Uacute]
.char	'a	\[aacute]
.char	'e	\[eacute]
.char	'i	\[iacute]
.char	'o	\[oacute]
.char	'u	\[uacute]
.char	:A	\[Auml]
.char	:O	\[Ouml]
.char	:U	\[Uuml]
.char	:a	\[auml]
.char	:o	\[ouml]
.char	:u	\[uuml]
.char	rg	\[reg]
.char	bu	*
.char	em	---
.char	en	-
ENDDATA
DATA an
.\"
.\" -man MACRO PACKAGE
.\" Copyright (C) TOYODA Eizi, 1998.  All rights reserved.
.\" see COPYING.TXT for terms of license.
.\"
.\" --- initialization
.checkopt acu
.{ '! number 'maninit number not and
.warn usage: htroff -man [-a] [-u[d]] [-c<color>] files....
.exit 1
.}
.alias opened nop
.\" --- internally used macros
.de	manout
.{ 'u option 'd eq
.dsexpr manout 'htm\\$2/\\$1.htm lowercase
.}{ '\\$2 "^[2-7]" match 'u option defined not and
.dsexpr manout '\\$1-\\$2.htm lowercase
.}{
.dsexpr manout '\\$1.htm lowercase
.}
..
.\" --- externally used macros
.de	TH
.{ 'a option defined
.manout \\$1 \\$2
.warn htroff -man: output of \\$1(\\$2) written to \\*[manout]...
.output \\*[manout]
.}
.tag HTML
.tag HEAD
.nrexpr unify begin
.tag TITLE
man \\$1(\\$2)
.tag /TITLE
.nrexpr unify end
.tag /HEAD
.dsexpr body ""
.{ 'c option defined
.dsexpr body "BGCOLOR=\QQ" 'c option "\\QQ" cat cat
.}
.tag BODY \\*[body]
.nrexpr unify begin
.tag H1
\\$1(\\$2)
.tag /H1
.nrexpr unify end
.{ args 3 sub inc posi
Date: \\$3
.tag BR
.{ args 4 sub inc posi
Source: \\$4
.tag BR
.}
.{ args 5 sub inc posi
Title: \\$5
.tag BR
\\$6
.}
.}
.bp
..
.de	SH
.stopIP
.nrexpr unify begin
.tag H2
\\$*
.tag /H2
.nrexpr unify end
..
.de	SS
.stopIP
.nrexpr unify begin
.tag H3
\\$*
.tag /H3
.nrexpr unify end
..
.de	LP
.stopIP
.tag P
..
.alias	PP LP
.alias	P LP
.de	stopIP
.{ \\n(DL posi
.nrexpr DL 0
.tag /DL
.}
..
.de	IP
.{ \\n(DL posi not
.nrexpr DL 1
.tag DL
.}
.{ args posi
.tag DT
\\$1
.tag DD
.}{
.tag BR
.}
..
.de	TP
.getline
.IP "\\$+"
..
.alias	HP TP
.de	RS
.nrexpr RS \\n(RS inc
.tag BLOCKQUOTE
..
.de	RE
.{ \\n(RS posi
.tag /BLOCKQUOTE
.}{
.warn RE without RS
.}
.nrexpr RS \\n(RS dec
..
.de	I
.it 1
.{ args posi
\\$*
.}
..
.de	B
.bf 1
.{ args posi
\\$*
.}
..
.alias	SB B
.de	IB
\fI\\$1\fB\\$2\fI\\$3\fB\\$4\fI\\$5\fB\\$6\fR
..
.de	BI
\fB\\$1\fI\\$2\fB\\$3\fI\\$4\fB\\$5\fI\\$6\fR
..
.de	RI
\\$1\fI\\$2\fP\\$3\fI\\$4\fP\\$5\fI\\$6\fP
..
.de	IR
\fI\\$1\fP\\$2\fI\\$3\fP\\$4\fI\\$5\fP\\$6
..
.de	RB
\\$1\fB\\$2\fP\\$3\fB\\$4\fP\\$5\fB\\$6\fP
..
.de	BR
.{ args 2 equiv '\\$2 '^\([1-8]\w*\) match and
.dsexpr name '\\$1
.dsexpr chap '\\$2 '^\(([1-8]\w*)\) subpat
.dsexpr tail '\\$2 '^\([1-8]\w*\)(.*) subpat
.manout \\*[name] \\*[chap]
.}{
.dsexpr manout null
.}
.{ manout string file
.nrexpr unify begin
.tag A HREF=\\QQ\\*[manout]\\QQ
\\fB\\*[name]\\fR(\\*[chap])\\*[tail]
.nrexpr unify end
.tag /A
.}{
\fB\\$1\fR\\$2\fB\\$3\fR\\$4\fB\\$5\fR\\$6
.}
..
.de	TX
.dsexpr TX 'TX\\$1 string defined 'TX(\\$1) or
\\*(TX\\$2
..
.de	SM
\\$*
..
.alias	IX nop
.alias	PD nop
.alias	DT nop
.\"ds	R	\[reg]
.dsexpr	R	(Reg)
ENDDATA
DATA s
.\"
.\" -ms MACRO PACKAGE
.\" Copyright (C) TOYODA Eizi, 1998.  All rights reserved.
.\" see COPYING.TXT for terms of license.
.\"
.\" ====== initialization
.checkopt "c"
.{ '! number
.warn usage: htroff -ms [-c<color>] files...
.}
.dsexpr MO 'month string
.dsexpr DY 'month string " " cat 'dy number cat ", " cat 'year number cat
.\" --- init
.de IZ
.	nr fs 0 1
.	nr FS 0
.	nr title 0
.	nr authorlist 0
.	nr DL 0
.	nr QP 0
.	dsexpr heading undef
.	ds DS ""
.	rm footnotekeep
.	resetNH
..
.alias	RP	IZ
.alias	TM	IZ
.\" ====== termination
.dsexpr mscleanup 'cleanup alias
.alias cleanup mscleanup
.de mscleanup
.	clearPar
.	clearFootNote
.	\*[mscleanup]
..
.\" ====== macros for beginning of page
.de TL
.	tag H1 ALIGN=CENTER
.	nr title 1
..
.de stopTitle
.{ 'title number
.	tag /H1
.	nr title 0
.}
..
.de startAuthorList
.{ 'authorlist number not
.	nr authorlist 1
.	nf
.	tag DIV ALIGN=CENTER
.}
..
.de stopAuthorList
.{ 'authorlist number
.	it 0
.	tag /DIV
.	fi
.	nr authorlist 0
.}
..
.de AU
.	stopTitle
.	startAuthorList
.	tag BR
.	it 0
..
.de AI
.	startAuthorList
.	tag BR
.	it 1000
..
.de AB
.	stopAuthorList
.{ '\\$1 'no eq not
.	tag H2 ALIGN=CENTER
Abstract
.	tag /H2
.}
.	tag P
..
.de AE
.	stopAuthorList
.	tag HR
..
.\" ====== macros commonly used
.\" --- heading
.de stopHeading
.{ 'heading string defined
.	hook ""
.	tag /\\*[heading]
.	dsexpr heading undef
.}
..
.de stopHeadingHook
.	hook ""
\\*[line]
.	tag /\\*[heading]
.	dsexpr heading undef
..
.de resetNH
.	nr H1 0 1
.	nr H2 0 1
.	nr H3 0 1
.	nr H4 0 1
.	nr H5 0 1
.	nr H6 0 1
..
.de NH
.	clearPar
.	clearFootNote
.	ds NH \\$1
.{ args posi not
.	ds NH 1
.}
.shift
.{ 'NH string 0 eq
.	resetNH
.}{
.{ 'NH string S eq
.	dsexpr NH args
.	nr H1 \\$1 1
.	nr H2 \\$2 1
.	nr H3 \\$3 1
.	nr H4 \\$4 1
.	nr H5 \\$5 1
.	nr H6 \\$6 1
.	dsexpr nh \\$1.\\$2.\\$3.\\$4.\\$5.\\$6 '[0-9].*[0-9] grep
.}{
.	nrexpr H\\*(NH 'H\\*(NH number inc
.	ds nh \\n(H1.\\n(H2.\\n(H3.\\n(H4.\\n(H5.\\n(H6
.{ 'NH string 6 sub nega 
.	nr H6 0
.	ds nh \\n(H1.\\n(H2.\\n(H3.\\n(H4.\\n(H5
.}
.{ 'NH string 5 sub nega 
.	nr H5 0
.	ds nh \\n(H1.\\n(H2.\\n(H3.\\n(H4
.}
.{ 'NH string 4 sub nega 
.	nr H4 0
.	ds nh \\n(H1.\\n(H2.\\n(H3
.}
.{ 'NH string 3 sub nega 
.	nr H3 0
.	ds nh \\n(H1.\\n(H2
.}
.{ 'NH string 2 sub nega 
.	nr H2 0
.	ds nh \\n(H1
.}
.}
.	ds heading H\\*(NH
.	tag \\*[heading]
\&\\*(nh
.	hook "" stopHeadingHook
.}
..
.de CT
.	clearPar
.	clearFootNote
.	tag HR
.	tag H2
.	ds heading H2
.	hook "" stopHeadingHook
..
.de SH
.	clearPar
.	clearFootNote
.	tag H3
.	ds heading H3
.	hook "" stopHeadingHook
..
.\" --- paragraph
.de clearParWithoutDL
.	stopTitle
.	stopAuthorList
.	stopHeading
.	stopQP
.	NL
..
.de stopQP
.{ QP number
.	tag /BLOCKQUOTE
.	nr QP 0
.}
..
.de stopDL
.{ DL number
.	nr DL 0
.	tag /DL
.}
..
.de clearPar
.	clearParWithoutDL
.	stopDL
..
.de LP
.	clearPar
.	tag P
..
.alias XP LP
.de PP
.	clearPar
.	tag P
\ \ \&
..
.de QP
.	clearPar
.	tag P
.	tag BLOCKQUOTE
.	nr QP 1
..
.de IP
.	clearParWithoutDL
.{ DL number not
.	nr DL 1
.	tag DL
.}
.	tag DT
\\$1
.	tag DD
..
.\" --- relative indent
.de RS
.	tag BLOCKQUOTE
..
.de RE
.	tag /BLOCKQUOTE
..
.\" --- display
.ds DS ""
.de DS
.{ 'DS string "" eq not
.warn nested .DS \\$1 in .DS \\*(DS
.}{
.	ds DS \\$1
.{ 'DS string "" eq
.	ds DS I
.}
.	clearPar
.	hook "^\\s+"
.{ 'DS string '[IB] match
.	tag BLOCKQUOTE
.}{ 'DS string 'C eq
.	tag DIV ALIGN=CENTER
.}
.	tag PRE
.}
..
.de DE
.	tag /PRE
.{ 'DS string '[IB] match
.	tag /BLOCKQUOTE
.}{ 'DS string 'C eq
.	tag /DIV
.}
.	hook "^\\s+" breakline
.	ds DS ""
..
.de ID
.	DS I \\$1
..
.de LD
.	DS L
..
.de CD
.	DS C
..
.de BD
.	DS B
..
.\" --- physical style
.de B
.	bf 1000
.{ args posi
\&\\$*
.	bf 0
.}
..
.de I
.	it 1000
.{ args posi
\&\\$*
.	it 0
.}
..
.de UL
.	ul 1000
.{ args posi
\&\\$*
.	ul 0
.}
..
.de NL
.	it 0
.	bf 0
.	ul 0
..
.alias R NL
.\" --- footnote
.ds *	\QLSUP\QG\QLA HREF='#FN\\n+(fs'\QG(\\n(fs)\\QL/A\\QG\QL/SUP\QG
.ds footnote footnote:
.de FS
.	nrexpr FS:QP 'QP number
.	nr QP 0
.	nrexpr FS:DL 'DL number
.	nr DL 0
.	NL
.	keep footnotekeep
.\"	de footnotekeep endfootnotekeep
.	br
.{ 'FS number 'fs number sub nega
.	tag A NAME='FN\\n(fs'
(\\n(fs)
.	tag /A
.	nrexpr FS 'FS number inc
.}
..
.de FE
.	stopDL
.	stopQP
.	keep
.\"	endfootnotekeep
.	nrexpr QP 'FS:QP number
.	nrexpr DL 'FS:DL number
..
.de clearFootNote
.{ footnotekeep macro defined
.	tag BLOCKQUOTE
.	tag TABLE BORDER
.	tag TR
.	tag TD
\&\\*[footnote]
.	br
.	footnotekeep
.	tag /TABLE
.	tag /BLOCKQUOTE
.	rm footnotekeep
.}
..
.\" --- equation (fake)
.de EQ
.	ds EQ "\\$2"
.	tag BLOCKQUOTE
..
.de EN
.{ 'EQ string "" eq not
\ \ (\\*[EQ])
.}
.	tag /BLOCKQUOTE
..
.\" --- box
.de B1
.	tag TABLE BORDER
.	tag TR
.	tag TD
..
.de B2
.	tag /TD
.	tag /TR
.	tag /TABLE
..
.de BX
.	B1
\\$1
.	B2
..
.\" --- misc.
.de UX
UNIX
..
.de DA
.{ args posi
.	ds hooter	source written in \\$*, and 
.}
..
.\" --- senseless command
.alias	KS	nop	\" keep
.alias	KF	nop
.alias	KE	nop
.alias	EF	nop
.alias	OF	nop
.alias	EH	nop
.alias	OH	nop
.alias	P1	nop
.alias	MC	nop
.alias	ND	nop
.alias	SM	nop
.alias	LG	nop
.alias	1C	nop
.alias	2C	nop
.alias	TA	nop
.alias	[	nop
.alias	]-	nop
.\" --- string definition
.ds	Q	""""
.ds	U	""""
.ds	e	\(em
.ds	MO	\*[month]
.ds	DY	"\n(dy \*[month] \n[year]"
.ds	'	\\('
.ds	`	\\(`
.ds	^	\\(^
.ds	,	\\(,
.ds	:	\\(:
.ds	~	\\(~
.\" ====== do init
.IZ
ENDDATA
