#!/usr/pkg/bin/perl
#
# $Id: gphelp.in 7757 2006-03-06 18:05:00Z kb $
#
# Copyright (C) 2000  The PARI group.
#
# This file is part of the PARI/GP package.
#
# PARI/GP is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation. It is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY WHATSOEVER.
#
# Check the License for details. You should have received a copy of it, along
# with the package; see the file 'COPYING'. If not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# Output extended help corresponding to a given GP command. By default,
# extract relevant information from  from the PARI manual, run TeX, then open
# an xdvi display.
#
# The manual can be compressed.
#
# Usage: gphelp keyword
#
# Command line options:
#  -k: apropos (list of relevant GP functions)
#  -detex      (-d): don't use TeX + xdvi (implicit when DISPLAY is not set).
#  -color_help (-ch) <number>: use color "number" (same scheme as in GP)
#  -color_bold (-cb) <number>: display bold in color "number"
#  -color_underline (-cu) <number>: display underlined text in color "number"
#
#  -raw   use internal format for output with @x markers, -detex is implicit
#         (for the TeX-to-pod converter)
#
#  -to_pod file		convert file to POD (should be the only args)
#
#  -to_dumbpod file	same, but without nested formating
#
# Granted environment variables (override):
#  GPTMPDIR: where temporary files will go (/tmp by default).
#  GPDOCDIR: where is manual (by default, where make install will put it).
#  GPXDVI: which 'xdvi' program to call (xdvi by default)
#
$version= "2.3.5";
$datadir= "/usr/pkg/share/pari";
# no expanded material (@key@) below
$wwwsite= "http://pari.math.u-bordeaux.fr/";

$xdvi = $ENV{GPXDVI} || "xdvi";
$xdviref = $ENV{GPXDVIREF} || "$xdvi -paper 29.7x21cm";
$gzip = "gzip";
$zcat = "$gzip -dc";
$docdir = &get_docdir();

$refcard = (@ARGV and $ARGV[-1] =~ /refcard/i);

$dumb_pod=1, $ARGV[0] = '-to_pod' if @ARGV && $ARGV[0] eq '-to_dumb_pod';
&to_pod() if @ARGV == 2 && $ARGV[0] eq '-to_pod';

&options(); &init();
if ($#ARGV < 0) { &treat($_); cleanexit(); }

&pretex() if (!$detex);
for (@ARGV) { &treat($_); }


if ($apropos) { &apropos_final_print(); cleanexit(); }
if (!$found) { &clean if (!$detex); cleanexit(); }

&posttex() if (!$detex);

print $gpmsg if (!$detex && $fromgp);
cleanexit();

#
# Procedures
#
sub cleanexit {
  print "\e[0m" unless $to_pod;
  exit 0;
}

sub help {
  print "Usage: $0 [-k] [-detex] [-ch c1] [-cb c2] [-cu c3] keyword\n";
  print "where c1,c2,c3 denote background, bold and underline color\n";
  exit(1);
}

sub options {
  $raw = $detex = $fromgp = $apropos = 0;
  $ch = $cb = $cu = '';
  while ($_ = $ARGV[0])
  {
    last if (! /^-[a-z]/);
    shift(@ARGV);
    if ($_ eq "-fromgp")
      { $fromgp = 1; }
    elsif ($_ eq "-k")
      { $apropos = 1; }
    elsif ($_ eq "-detex" || $_ eq "-d")
      { $detex = 1; }
    elsif ($_ eq "-raw")
      { $raw = $detex = 1; }
    elsif ($_ eq "-color_help" || $_ eq "-ch")
      { $ch = &color(shift(@ARGV)); }
    elsif ($_ eq "-color_bold" || $_ eq "-cb")
      { $cb = &color(shift(@ARGV)); }
    elsif ($_ eq "-color_underline" || $_ eq "-cu")
      { $cu = &color(shift(@ARGV)); }
    else
      { &help(); }
  }
  $ch = "\e[m$ch";
  $cu .= $cu ? "\e[1m": "\e[4m";
  $cb .= "\e[1m";
  $detex = 1 if (!$detex && !$ENV{DISPLAY});
}

sub get_docdir {
  my $d = $ENV{GPDOCDIR} || $ENV{GPHELP_DOCDIR};
  if (!defined $d) {
    # work from TOPDIR/Oarch or TOPDIR too: may be uninstalled yet;
    $d = $0; $d =~ s,/gphelp,,;
    for ("$datadir", '.', '..', $d) {
      my $t = "$_/doc";
      if (-r "$t/translations") { $d = $t; last; }
    }
    $d ||= "$datadir/doc";	# Last resort
  }
  if ($d =~ /^\./) {
    eval { require Cwd;
      $d = Cwd::cwd() . "/$d";
      $d =~ s,doc/\.\./doc,doc,;
    }
  }
  return $d;
}

sub init {
  $gpmsg = "ugly_kludge_done\n";
  &inittr();

  $indent = "   ";
  # avoid Glob.pm! (for minimal Windows install)
  opendir(DIR, $docdir) || die "$docdir not found";
  @file_list = grep { /^usersch.*tex/ } readdir(DIR);
  closedir(DIR);

  chdir($docdir);
  $docfile = "usersch3.tex";
  open(IN,"translations") || die("Could not find translation file, docdir='$docdir'");
  while(<IN>)
  {
    chomp; @_ = split(/ *\@ */);
    $key = shift(@_);
    $transl{$key} = join('@',@_);
  }
  close(IN);
}

sub not_found {
  my($help) = shift;
  $help =~ s/\\\\/_B#K#S_/g;
  $help =~ s/\\(.)/$1/g;
  $help =~ s/_B#K#S_/\\/g;
  print "'$help' not found !\n";
}

sub choose_chap {
  while (s/\@([0-9])$//) { $docfile = "usersch$1.tex"; }
  if (-r $docfile) { $pipe = ""; }
  else
  {
    die "Cannot find $docfile"
      if (! -r "$docfile.z" &&
          ! -r "$docfile.gz" &&
          ! -r "$docfile.Z");
    $pipe = $zcat;
  }
}

sub safe_setsid {
  eval {
    require POSIX;
    POSIX::setsid(); # detach from terminal (^C will not kill xdvi)
  };
}

# assume we're in $docdir
sub open_viewer_then_quit {
  my $F = shift;
  my ($f, $viewer, $redirect);
  my $win32 = ($^O =~ /(cyg|ms)win/);

  $f = "$F.dvi";
  $f = "$F.dvi.gz" if (! -f "$f");
  $f = "$F.pdf"    if (! -f "$f");
  die "could not find \'$F\'" if (! -f "$f");
  $F = $f;
  $redirect = ' 2>/dev/null >/dev/null &';
  if ($f =~ /\.dvi/)
  { # DVI
    $viewer = ($f =~ /refcard/)? $xdviref: $xdvi;
  }
  elsif ($win32)
  { # PDF Win32
    @_ = split(/"/, `acro.exe`);
    ($viewer = $_[1]) =~ s,\\,/,g;

    $redirect = "";
    $F =~ s,/cygdrive/(.),$1:, ; # Reader can't cope with Cygwin paths
    $F      = "\"$F\"";
    $viewer = "\"$viewer\"";
  }
  else
  { # PDF generic
    $viewer = "acroread";
  }

  print "displaying \'$F\'.";
  print "\n" if (!$fromgp);
  safe_setsid();
  system("$viewer $F$redirect");
  cleanexit();
}

sub treat {
  my($help);
  $_ = $_[0];
  s/_QUOTE/'/g;
  s/_BACKQUOTE/`/g;
  s/_DOUBQUOTE/"/g;
  s/^ *"(.*)"([^"]*) *$/$1$2/;
  if (s/\@$//)
  {
    $found = 0;
    $searchall = 1;
    $help = $_;
    for (@file_list)
    {
      next if (!/^usersch(.*)\.tex/);

      &treat("$help\@$1");
      if ($apropos && $#list > 0 || $#sentence_list > 0)
      {
        print "\nChapter $1:\n";
        print "==========\n";
        &apropos_final_print();
      }
    }
    return not_found($help) if (!$found && !$apropos);
    $searchall = 0;
    $apropos = 0; return;
  }
  &choose_chap;

  if (!$apropos)
  {
    $_ = "users" if (/^$/);
    open_viewer_then_quit($_) if (/^(users|tutorial|refcard|libpari)$/);
  }

  if (!$apropos && $transl{$_}) { $_ = $transl{$_}; &choose_chap; }
  s/(\W)/\\$1/g;
      ($pipe && open(DOC,"$pipe $docfile |"))
  || (!$pipe && open(DOC,"$docfile")) || die "Cannot open $docfile: $!";
  return &apropos($_) if ($apropos);
  if (/^\\[<>=!]=?|[|&]{1,2}$/)
    { $_ = 'Comparison and boolean operators'; }
  $help = $_;
  while (<DOC>)
  {
    if (/^\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter){$help}/)
      { $first = $_; last; }
  }
  if (eof(DOC))
  {
    &not_found($help) if (!$searchall);
    return;
  }
  $found = 1;

  if (!$detex) { &tex(); return; }

  &detex(); print "\n" if (!$fromgp);
  do {local $/; <DOC>} if $^O eq 'os2' and $pipe; # Avoid broken pipe from zcat
  close(DOC);
}

#
#  A propos
#

sub apropos_print_list {
  $current = "";
  @_ = sort(@_);
  for (@_)
  {
    next if ($_ eq $current);
    $current = $_; print "$indent$_\n";
  }
}

sub apropos_raw_print {
  $indent = "";
  &apropos_print_list(@sentence_list);
  &apropos_print_list(@list);
}

sub apropos_final_print {
  my($maxlen) = 0;
  my($i,$nbcol,$current);
  my($cols) = ($ENV{'COLUMNS'} || 80) - 1;

  if ($raw) { &apropos_raw_print(); return; }
  @list = sort(@list);
  for (@list)
  {
    $i= length($_);
    $maxlen = $i if ($i > $maxlen);
  }
  $maxlen++; $nbcol = $cols / $maxlen;
  $nbcol =~ s/\..*//;
  $nbcol-- if  ($nbcol * $maxlen == $cols);
  $nbcol = 1 if (!$nbcol);

  $current = ""; $i = 0;
  for (@list)
  {
    next if ($_ eq $current);
    $current = $_; print($_); $i++;
    if ($i >= $nbcol)
    {
      $i=0; print "\n"; next;
    }
    print " " x ($maxlen - length($_));
  }
  print "\n" if ($i);
  if ($#sentence_list > 0)
  {
    print "\nSee also:\n" if ($#list > 0);
    $indent = "  ";
    apropos_print_list(@sentence_list);
  }
}

sub apropos_check {
  my($line, $current) = @_;
  $line =~ s/\n/ /g;
  return if ($line !~ /$help/i);

  local($_) = $current;
  s/\\b{(.)}/\\$1/;
  s/\{\}//g;
  s/\\pow/^/;
  s/\\%/%/;
  s/\\bs/\\/;
  s/\\\#/\#/g;
  s,\+\$/\$-,+/-,;
  if (/ /) { push(@sentence_list,$_); } else { push(@list,$_); }
}

sub apropos {
  my($line,$current,$new);
  $help = $_[0];
  $help='\\\\pow' if ($help eq '\^');
  $help='\\\\til' if ($help eq '\~');
  @sentence_list = @list = "";
  while (<DOC>)
  {
    if (/^\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter){/)
    {
      $new = &get_match($_,'{','}');
      &apropos_check($line, $current);
      $current = $new; $line = "";
    }
    $line .= $_;
  }
  &apropos_check($line, $current);
}

#
#  Tex Part
#
sub tex_mktmp {
  my $basedir = $ENV{GPHELP_TMPDIR} || $ENV{GPTMPDIR} || $ENV{TMPDIR} || "/tmp";
  $tmpdir = "$basedir/gp.help$$";
  mkdir $tmpdir, 0755 || die("Cannot create temporary directory");
  $texfile = "$tmpdir/gp.help";
}

sub pretex {
  &tex_mktmp;
  open(TEX,">$texfile.tex") || die "Couldn't open $texfile.tex";
  print TEX << "EOT";
\\def\\fromgphelp\{\}
\\input $docdir/parimacro.tex
EOT
}

sub tex {
  print TEX "$first";
  while (<DOC>)
  {
    last if /^\\(section|sub[sub]*sec)/i;
    print TEX;
  }
  close(DOC);
}

sub posttex {
  print TEX "\\vfill\\eject\\bye";
  close(TEX) || die "Error closing '$texfile.tex': $!";

  chdir($tmpdir);
  $out = `tex $texfile.tex 2>&1 < /dev/null`;
  $? == 0 && -f "$texfile.dvi"
    || die "could not create '$texfile.dvi': status=$?, $out";
  safe_setsid();
  system("($xdvi $texfile.dvi 2>/dev/null >/dev/null; rm -f $texfile.tex $texfile.dvi $texfile.log;rmdir $tmpdir)&");
}

sub clean {
  unlink("$texfile.tex","$texfile.dvi","$texfile.log");
  rmdir("$tmpdir");
}

#
#  Detex Part
#
sub fit_loop {
  my($i);
  return if ($miss > 9);
  while ($miss > 0)
  {
#  print "1:$miss ";print @_;print "\n";
    for (@_) { $miss-- if (s/([?!\.;])$/$1 /);
       return if ($miss == 0);
    }
#  print "2:$miss ";print @_;print "\n";
    for (@_) { $miss-- if (s/([?!\.;]) $/$1  /);
       return if ($miss == 0);
    }
#  print "3:$miss ";print @_;print "\n";
    for (@_) { $miss-- if (s/([\),])$/$1 /);
       return if ($miss == 0);
    }
#  print "4:$miss ";print @_;print "\n";
    $i = 0;
    for (@_)
    {
      if (!$i) { $i = 1; next; }
      $miss-- if (s/^\(/ (/);
       return if ($miss == 0);
    }
#  print "5:$miss "; print @_;print "\n";
    for (@_) { $miss--; s/$/ /;
       return if ($miss == 0);
    }
  }
}

sub fit_line {
  my($wi, @a);
  my($l) = -1;
  my($rem) = $_[0];
  for (@l)
  {
     $l2 = $l; $l += ($_ + 1);
     last if ($l > $rem);
     $wi++;
  }
  $miss = $rem - $l2;
  splice(@l, 0, $wi);
  @a = splice(@w, 0, $wi-1); &fit_loop(@a);
  push(@a, shift(@w)); return join(' ', @a);
}

# empty output line
sub is_void {
  my($in) = shift;
  $in =~ s/\@\[\w+\]//g;
  $in =~ s/\@[012]//g;
  ($in =~ /^\s*$/)? 1: 0;
}

sub nl { push(@f_text, shift); }

sub format_text {
  my($last_void) = 0;
  my($add_stuff) = 0;
  my($init) = 1;
  my($cols) = ($ENV{'COLUMNS'} || 80) - 1;
  my($first) = $cols - length($indent);

  for (@text)
  {
    if (s/^\@1//)       # start verbatim
    {
      nl(&fit_line($first)) if (@w);
      nl("") if (!$last_void && !is_void($_)); # add empty line
      nl("$indent$_");
    }
    elsif (s/^\@0//)    # verbatim lines
    {
      nl("$indent$_");
    }
    elsif (s/^\@2//)       # end verbatim
    {
      nl("$indent$_");
      $last_void = is_void($_);
    }
    elsif (is_void($_))    # line is empty
    {
      next if (!$add_stuff);
      if ($init) {
        nl(&fit_line($first));
      } else {
        nl("") if (!$last_void);
        nl("\@[endbold]$indent" . &fit_line($first));
      }
      while (@w) { nl(&fit_line($cols)); }
      $init = $last_void = $add_stuff = 0;
    }
    else
    {
      $add_stuff = 1; s/^ +//;
      @_ = split(/\s+/, $_);
      for (@_)
      {
	s/\Q$tr{nbrk}/ /g; push(@w, $_);
        # these codes will be replaced by 1 character
        s/\@\[(obr|cbr|ouml|uuml|agrav|eacute|ldollar|pm|lt|gt|\{|\})]/\@/g;
        # the rest will be replaced by zero-width characters
	s/\@\[\w+\]//g;    push(@l, length($_));
      }
    }
  }
}

# argument has the form s1${open}s2${close}s3
# Return 's2'. Set $remainder to 's3'.
sub get_match {
  local ($_, $open, $close) = @_;
  my (@tmp, $arg,$parity,$ok);
  my ($obr) = 1;
  $parity = ($open eq $close);
  /$open/; $_ = $'; # remove everything before (and including) first $open

  while ($_) {
    @tmp = split(/($open|$close)/);
    while ($#tmp >= 0) {
      $_ = shift(@tmp);
      $obr++ if (/^$open$/);
      if ($parity && $obr == 2) { $ok = 1; last }
      $obr-- if (/^$close$/);
      if (!$obr) { $ok = 1; last }
      $arg .= $_;
    }
    last if ($ok);
    $_ = <DOC>;
  }
  $remainder = join('',@tmp);
  return $arg;
}

sub detex {
  my($fun,$args);
# 1: get the function "prototype"
  $fun = &get_match($_,'{','}');
  # name
  $fun = &basic_subst($fun);
  $_ = $remainder;
  # args as $(x)$ in \subsecidx{sin}$(x)$:
  if (/^ *\$/)
  {
    $args = &basic_subst(&get_match($_,'\$','\$'));
    $_ = $remainder;
  }
  $_ = <DOC> if (!&basic_subst($_));
  # dft value as (...) in \subsecidx{echo} (default \kbd{0})
  if (/^ *\(/)
  {
    $args .= ' (' . &basic_subst(&get_match($_,'\(','\)')) . ')';
    $_ = $remainder;
  }
  $args .= ":" if ($args !~ /: *$/ && ($args || $fun !~ /: */));

  push(@text, "\@[startbold]$fun\@[endbold]$args");
  push(@text, "");
# 2: parse the function description
  if ($_) { s/^ *://; &presubst(); }
  while (<DOC>)
  {
    last if /^\\(section|sub[sub]*sec)/i;
    &presubst();
  }
  if ($raw) { print join("\n", @text); return; }
#  for (@text) { print("AA{$_}BB\n"); } # DEBUG
  &format_text();
  for (@f_text) { &TeXprint($_); }
}

# We use the special char @ to transmit special sequences
sub inittr {
  @ou = qw( dollar nbrk startbold endbold startcode endcode
	    obr cbr uuml ouml agrave eacute
	    startpodcode endpodcode startlink endlink
	    startbcode endbcode startbi endbi startit endit
	    startword endword startlword endlword pm empty gt lt podleader );

  @tr{@ou} = map "\@[$_]", @ou;
  $tr{dollar} = '$' if $to_pod;

  %pr = ( dollar => '',
	  ldollar => '$',	# literal dollar
	  nbrk => 'S< >',
	  startbold => 'B<',
	  endbold => '>',
	  startcode => 'C<',
	  startlink => 'L<',
	  endlink => '>',
	  endcode => '>',
	  obr => '{',
	  cbr => '}',
	  startpodcode => 'C<',
	  endpodcode => '>',
	  ( $dumb_pod
	    ? (startbcode => 'B<',
	       endbcode => '>',
	       startbi => 'B<',
	       endbi => '>',)
	    : (startbcode => 'B<C<',
	       endbcode => '>>',
	       startbi => 'B<I<',
	       endbi => '>>')),
	  startit => 'I<',
	  endit => '>',
	  startword => 'F<',
	  endword => '>',
	  startlword => ' F<',
	  endlword => '> ',
	  pm => 'F<+->',
	  "gt" => 'E<gt>',
	  "lt" => 'E<lt>',
	  ouml => 'E<ouml>',
	  uuml => 'E<uuml>',
	  eacute => 'E<eacute>',
	  agrave => 'E<agrave>',
	  empty => 'Z<>',
	  podleader => '=',
	);
}

sub indent_equally { my $in = shift; $in =~ s/^[ \t]*/    /mg; $in}

sub basic_subst {
  local($_) = shift;

  s/(\S)[ \t]*\n[ \t]+/$1\n/gm;
  s/([^\\])\\\{/$1$tr{obr}/g;
  s/([^\\])\\\}/$1$tr{cbr}/g;
  s/([^\\])\\-/$1/g;
  s/\A\\q?quad(?![a-zA-Z])\s*/$tr{nbrk}$tr{nbrk}/;
  s|\\wwwsite|$wwwsite|g;
  s/^\\def\\.*\{\n.*\n\}//gm;
  s/\\def\\.*//g;
  s(\\footnote\s*\{?\*+\}?\s*\{\s*((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\})
    {$tr{startbold}FOOTNOTE$tr{endbold}$tr{lt}$tr{lt}$tr{lt} $1 $tr{gt}$tr{gt}$tr{gt}}g;
  s/(\{[\w\s]+)\{\}([\s\w]+\})/$1$2/g;	# {nf{}init}
  s(\\op(?![a-zA-Z])\s*)({\\it op\\/})g;	# {nf{}init}
  s/\\emacs\b//;
  s/\\unix\b//;
  s/\\(leavevmode|strut)(?![a-zA-Z])\s*//g;
  s/ \\funno \s*
     { \s* ((?:[^{}]|\{[^{}]*\})*) } \s*
     { \s* ((?:[^{}]|\{[^{}]*\})*) } \s*
     { \s* ((?:[^{}]|\{[^{}]*\})*) }
   /\\noindent{\\tt $1 \$\\key{$2}\$($3)}/gx;
  s/\\funs\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}/\\fun{}{$1}{$2}/g;
  s/\\fun\s*\{([^{}]*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}/\\kbd{$1 \\key{$2}($3)}\\sidx{$2}/g;

  s/\\\\(?=[a-zA-Z])/\\bs /g;
  s/\\b{}\\b{}/\\bs\\bs /g;
  s/\\\\/\\bs/g;
  s/(\'\'|\`\`)/"/g unless $to_pod;     # (english) double quotes
  # asymptotic or isomorphic (~) [beware of ties]
  s/(^|[^\\]) +~/$1~/;
  s/~ */~/;
  s/(^|[^\\])~/$1$tr{nbrk}/g;           # ties
  s/\\(simeq|sim|approx)(?![a-zA-Z])/ ~ /g;
  s/\\til(?![a-zA-Z])/~/g;		# ~ (transpose)
  s/\\(~|tilde)/~/g;

  s/\\(equiv)(?![a-zA-Z])/ = /g;
  s/\\`a/$tr{agrave}/; s/\\`{a}/$tr{agrave}/;
  s/\\"o/$tr{ouml}/;   s/\\"{o}/$tr{ouml}/;
  s/\\"u/$tr{uuml}/;   s/\\"{u}/$tr{uuml}/;
  s/\\'e/$tr{eacute}/; s/\\'{e}/$tr{eacute}/;

  s/(^|[^\\])%.*/$1/g;		        # comments
  s/\\vadjust\s*\{\s*\\penalty\s*\d+\s*\}//g;

  # We do not strip %\n, thus:
  s/\\kbd{\n\s*/\\kbd{/g;
  s/\$\\bf(\b|(?=[\d_]))\s*([^\$]+)\$/\$$tr{startbcode}$1$tr{endbcode}\$/g;
  s/\$/$tr{dollar}/g;		        # math mode
  s/\t/ /g; s/\\,//g; s/\\[ ;]/ /g;     # various spaces
  s/\\\///g;			# italic correction
  s/^&+//g;			# tab marks
  s/([^\\])&+/$1 /g;		# tab marks
  s/\\TeX\{\}/TeX/g;
  s/\\TeX(\W)/TeX$1/g;
  s/ *\\circ\b */ o /g;
  s/\\d?frac{\s*((?:[^{}]|\{[^{}]*\})*)}{\s*((?:[^{}]|\{[^{}]*\})*)}/($1)\/($2)/g;
  s(\\d?frac\s*(\d)\s*(\d))(($1/$2))g;
  s[{\s*(\w)\s*\\over(?![a-zA-Z])\s*(\w)\s*}]{($1/$2)}g;
  s[{\s*((?:[^{}]|\{[^{}]*\})*)\\over(?![a-zA-Z])\s*((?:[^{}]|\{[^{}]*\})*)}][($1)/($2)]g;

  # \def\synt#1#2{\syn{#1}{\tt #2}}
  # \def\syn#1#2{\synx{#1}{#2}{#1}}
  s/\\synt?\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\synx{$1}{$2}{$1}/g;
  # \def\synx#1#2#3{\sidx{#3}The library syntax is $\key{#1}({#2})$}
  # Often used with embedded {}.
  s/\\synx\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{((?:[^{}]|\{[^{}]*\})*)\}/\\sidx{$3}The library syntax is $tr{startbold}$1$tr{endbold}$tr{startpodcode}($2)$tr{endpodcode}/;

  # May be used with an empty arg
  s/\\typ\{([^\}]*)\}/$tr{startcode}t_$1$tr{endcode}/g;

  s/(\\string)?\\_/_/g;
  s/\\([#\$&%|])/$1/g;
  s/\\(hat(?![a-zA-Z])|\^)({\\?\s*})?/^/g;
  s/^(\@\[podleader\]head\d *)\\pow(?![a-zA-z])( *)/$1^$2/gm;
  s/ *\\pow(?![a-zA-z]) */^/g;

  s/\\neq?(?![a-zA-Z])/ != /g;
  s/\\enspace(?![a-zA-Z])/ /g;
  s/\\times(?![a-zA-Z]) */ x /g;
  s/\\infty(?![a-zA-Z]) */ oo /g;
  s/ *\\(bmod|mod) */ mod /g;
  s/ *\\pmod(?![a-zA-Z]) *\{\s*((?:[^{}]|\{[^{}]*\})*)\}/ (mod $1)/g;
  s/ *\\cdot(?![a-zA-Z]) */./g;		# Maybe " . "?
  s/ *(\\|\@)[lc]?dots(?![a-zA-Z]) */.../g;
  s/\\(Cl|log|sin|cos|lim(proj)?|tan|mod|sqrt|exp|ln|det|Re|Im|deg|wp|cap|oplus)(?![a-zA-Z])/$tr{startlword}$1$tr{endlword}/g;
  s/\\pi(?![a-zA-Z])/$tr{startword}Pi$tr{endword}/g;
  s/\\(Alpha | Beta | Chi | Delta | Epsilon | Phi | Gamma | Eta | Iota
      | vartheta | Kappa | Lambda | Mu | Nu | Omicron | Pi | Theta | Rho
      | Sigma | Tau | Ypsilon | varsigma | Omega | Xi | Psi | Zeta
      | alpha | beta | chi | delta | epsilon | varepsilon | phi | gamma | eta
      | iota | varphi | kappa | lambda | mu | nu | omicron | pi | theta | rho
      | sigma | tau | ypsilon | varpi | omega | xi | psi | zeta
      | int | expr | seq | args | gcd | sum | prod | Re | infty )
      (?![a-zA-Z])/$tr{startword}$1$tr{endword}/xg;
  s/ *\\in(?![a-zA-Z]) */ belongs to /g;
  s/\\pm(?![a-zA-Z])/$tr{pm}/g;
  s/ *\\mid(?![a-zA-Z]) */ | /g;

  s/\\idxtyp\{([^{}]*)\}/\\sidx{t_$1}/g;
  s/\\ref\{([^\}]*)\}/[$tr{startbold}Label: $1$tr{endbold}]/g unless $to_pod;
  s/\\secref\{([^\}]*)\}/Section [$tr{startbold}Label: $1$tr{endbold}]/g unless $to_pod;
  s/\\label\{[^\}]*\}//g unless $to_pod;

  s/\\(noindent|medskip|bigskip|smallskip|left|right)(?![a-zA-Z])[ \t]*//g;
  s/\\vfill *(\\eject)?//g;
  s/\\(q|quad)(?![a-zA-Z])\s*/  /g;
  s/\\qquad(?![a-zA-Z])\s*/    /g;

  s/\\centerline\s*\{\s*(?:\\tt\b\s*)?(.*(\n[ \t].*)*)\}(?=\s*$)/indent_equally($1)/ge;
  s/\\centerline\s*\{\s*(?:\\tt\b\s*)?((?:[^{}]|\{[^{}]*\})*)\}/ indent_equally($1)/ge;

  s/\\big\b//g;

  s/\\settabs.*//;
  s/^\\\+/$tr{nbrk}/gm;
  s/\\\+//g;
  s/\\cr(?![a-zA-Z])//g;
  s/\\B(?![a-zA-Z])/\\kbd{BIL}/g;

  s/ *([=><]) */ $1 /g;
  s/ *<  *([=<]) */ <$1 /g;
  s/ *>  *([=>]) */ >$1 /g;
  s/ *([*+-\/^&=|:]) += */ $1= /g;
  s/ *! *= */ != /g;

  s/ *\\Rightarrow */ ==$tr{gt} /g;
  s/\\rangle(?![a-zA-Z])\s*/$tr{startcode}$tr{gt}$tr{endcode}/g;
  s/\\langle(?![a-zA-Z])\s*/$tr{startcode}$tr{lt}$tr{endcode}/g;
  s/\\rightarrow(?![a-zA-Z])\s*/$tr{startcode}--$tr{gt}$tr{endcode}/g;
  s/\\longleftrightarrow(?![a-zA-Z])\s*/$tr{startcode}$tr{lt}-----$tr{gt}$tr{endcode}/g;
  s/\\mapsto(?![a-zA-Z])\s*/$tr{startcode}|---$tr{gt}$tr{endcode}/g;
  s/ *\\geq?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{gt}=$tr{endcode} $1/g;
  s/ *\\leq?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{lt}=$tr{endcode} $1/g;
  s/ *\\gg?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{gt}$tr{gt}$tr{endcode} $1/g;
  s/ *\\ll?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{lt}$tr{ll}$tr{endcode} $1/g;

  s/\\(vers|PARIversion)(?![a-zA-Z])/$tr{startbold}$version$tr{endbold}/;
  s/\\([QRCFZNapdf])(?![a-zA-Z])/$tr{startbi}$1$tr{endbi}$2/g;
  s/\\([QRCFZN])\1(?![a-zA-Z])/$tr{startbi}$1$tr{endbi}$2/g;
  s/\\Bbb\b\s*(\w)/$tr{startbi}$1$tr{endbi}/g;

  s/\\([oc]br)/$tr{$1}/g;
  s/\\quo(?![a-zA-Z])/\"/g;
  s/(^|\s)\{(\w+)\}/$1$2/g;

  s/\\p(?![a-zA-Z])/$tr{startbold}p$tr{endbold}$1/g;
  s/\\point\{([^\}]*)\}/$tr{startbold}* $1$tr{endbold}/g;
  s/(\\bullet|\\item)/$tr{startbold}*$tr{endbold}/g;
  s/\\misctitle\{([^\}]*)\}/$tr{startbold}$1$tr{endbold}/g;
  s/\\subsec\{([^\}]*)\}/$tr{startbold}$1$tr{endbold}/g unless $to_pod;
  s/\\teb\{([^\}]*)\}/\\sidx{$1}$tr{startbold}$1$tr{endbold}/g;
  s/\\tet\{([^\}]*)\}/\\sidx{$1}$tr{startcode}$1$tr{endcode}/g;
  s/\\tev\{([^\}]*)\}/\\sidx{$1}$tr{startit}$1$tr{endit}/g;
  s/\\\$/$tr{ldollar}/g;
  s/\\kbd\s*\{\s*</\\kbd{$tr{lt}/g if $to_pod;
  s/\\kbd\s*\{\s*>/\\kbd{$tr{gt}/g if $to_pod;
  s/\\kbd\s*\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startcode}$1$tr{endcode}/g;

  s/\\key\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g unless $refcard;
  s/\\goth\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g;

  if ($refcard) {
    s/\\(?:key|li)\{((?:[^{}]+(?=[{}])|\{[^{}]*\})*)\}\s*\{\}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/g;
    s/\\(?:key|li)\{((?:[^{}]+(?=[{}])|\{[^{}]*\})*)\}\s*\{(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/\n=item $tr{startcode}$2$tr{endcode}\n\n$1\n/g;
  }

  s/\\(floor|ceil|round|binom)\{/$1\{/g;
  s/\\(var|emph)\{([^\}]*)\}/$tr{startit}$2$tr{endit}/g;
  s/\\fl(?![a-zA-Z])/$tr{startit}flag$tr{endit}/g;
  s/\\b{([^}]*)}/$tr{startcode}\\$1$tr{endcode}/g;
  s/\\kbdsidx/\\sidx/g;
  s/\\sidx\{[^\}]*\}//g unless $to_pod;
  s/\\[a-zA-Z]*idx\{([^\}]*)\}/$1/g unless $to_pod;
  s/\\(text|hbox|Big)//g;
  s/^([ \t]+)\{ *\\(it|sl|bf|tt)\b/S<$1>{\\$2/gm;
  s/\{ *\\(it|sl) *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startit}$2$tr{endit}/g;
  s/\{ *\\bf *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g;
  s/\{ *\\tt *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startpodcode}$1$tr{endpodcode}/g;
  $seek=1 if (s/\\emph{ */$tr{startit}/g);
  if ($seek) { $seek=0 if (s/\}/$tr{endit}/) }
  s/\\(backslash|bs)\{(\w)\}/\\$2/g;
  s/\\(backslash|bs)(?![a-zA-Z]) */\\/g;

  s/\@com(.*)$/$tr{startcode}$1$tr{endcode}/g;

  # Last resort:
  s/\\kbd\s*\{(.*?)\}/$tr{startcode}$1$tr{endcode}/g;
  s/^([ \t]{3,})\Q$tr{startcode}\E(.*)\Q$tr{endcode}\E/$1$2/gmo if $to_pod;
  # Last resort:
  s/^([ \t]{3,})\Q$tr{startcode}\E(.*?)\Q$tr{endcode}\E/$1$2/gmso if $to_pod;
  # Remove leading spaces unless have embedded wrapped code:
  s/^[ \t]+//gm if $to_pod and /^\S/ and not /^[ \t]*\n[ \t]/m;
  s/\{ *\}//g;			# empty args

  s{\Q$tr{startcode}\E((ftp|http)://.*?)\Q$tr{endcode}\E}{$tr{startlink}$1$tr{endlink}}go if $to_pod;
  $_;
}

sub presubst {
  chomp;
  if ($in_prog && /(\\|\@)eprog/)
  {
    $in_prog = 0;
    $_ = "\@2" . &code_subst($`) . $tr{endcode};
    push(@text, $_);
    $_ = &basic_subst($');
  }
  elsif ($in_prog || s/\\bprog(tabs.*)?//g)
  {
    $in_prog++;  # = 1 on the \bprog line
                 # code should start on the next line
    $_ = &code_subst($_);
    s/^/\@1$tr{startcode}/ if ($in_prog == 2);
    s/^/\@0/ if ($in_prog > 2);
  }
  else { $_ = &basic_subst($_); }
  push(@text, $_);
}

sub code_subst {
  my $in = shift;
  $in =~ s/\@dots\b/.../g;
  if ($in =~ /\@com(.*)/)
  {
    if ($to_pod) {
      $in = $` . &basic_subst($1) . code_subst($');
    } else {
      $in = $` . $tr{endcode} . &basic_subst($1) . $tr{startcode} . code_subst($');
    }
  }
  if ($in =~ /\@Ccom(.*)\*\//)
  {
    if ($to_pod) {
      $in = $` .		&basic_subst($1) . "*/" . &code_subst($');
    } else {
      $in = $` . $tr{endcode} . &basic_subst($1) . $tr{startcode}
                                                 . "*/" . &code_subst($');
    }
  }
  $in;
}

sub wrap_code {
  my $in = shift;
  $in =~ s/^[ \t]+$//mg;
  $in = &code_subst($in);
  $in =~ s/^(.)/  $1/mg;
  $in =~ s/\s*\Z//;
#  $in =~ s/\\kbd\{((?:[^{}]|\{[^{}]*\})*)\}/$1/g if $to_pod;
  $in =~ s/\$([^\$\n]*)\$/$1/g if $to_pod;
  "\n\n$in\n\n";
}

sub indexify {
  my $in = shift;
  $in =~ s/(^|and\s+)(\w+)(\$?\()/$1\\idx{$2}$3/g;
  $in =~ s/^(\\b\{\w+\})(?!\S)/\\idx{$1}/g;
  $in;
}

sub for_index {
  my $in = shift;
  1 while $in =~ s/\Q$tr{startcode}\E(.*?)\Q$tr{endcode}\E/$1/go;
  $in;
}

sub strip_trail { my $in = shift; $in =~ s/\s+\Z//; $in }

# This subroutine works in paragraph mode
sub TeXprint_topod {
  s/\A\s+//;
  s/^\\def\\.*\{\n.*\n\}//gm;
  s/\\def\\.*//g;		# Repeated in basic_subst, as the next one
  s/(\{[\w\s]+)\{\}([\s\w]+\})/$1$2/g;	# {rnf{}llgram}

  s/\\vbox\s*\{\s*\\bprog/\\bprog/g;
  s/([\\\@])eprog\s*\}/$1eprog/g;

  #  \n is below to prevent splitting on ' '
  #  We also remove ':'
  s/\\sectype\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\subsec{Type \\typ{$1} (${2}s)}\n\\sidx{$2}/g;
  s/\\sectypeindex\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\subsec{Type \\typ{$1} (${2}s)}\n\\sidx{$3}/g;
  s/\\sectypes\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\subsec{Type \\typ{$1} and \\typ{$1} (${3}s)}\n\\sidx{$3}/g;

  # Try to guard \label/\sidx (removing possible '.')
#  This somehow breaks index...
#  s/(\\(?:section|subsec(?:ref|idx|op)?(unix)?)\s*{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)})\.?\s*\\(label|sidx)/$1\n\\$2/;
  s/(\\(?:section|subsec(?:ref|idx|op)?)\s*{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)})\.?\s*\\(label|sidx)/$1\n\\$2/;

  # last if /\\subsec[\\{}ref]*[\\\${]$help[}\\\$]/o;
  s/\\chapter\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/\n\n$tr{podleader}head1 NAME\n\nlibPARI - $1\n\n$tr{podleader}head1 DESCRIPTION\n\n/;
  s/\\appendix\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/\n\n$tr{podleader}head1 NAME\n\nAppendix - $1\n\n$tr{podleader}head1 DESCRIPTION\n\n/;
  s/\\section\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/"\n\n$tr{podleader}head1 " . indexify($1) . "\n\n"/e;

  # Try to delimit by :
  s/\\subsec(?:ref|idx|op)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}((\W*default:)?[^\n:]*):\s*/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
  s/\\subsec(?:ref|idx|op)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^\n:]*):\s*/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
  s/\\subsubsec(?:ref|idx|op)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^:]*):\s*/"\n\n$tr{podleader}item " . indexify("$1$3") . "\n\n"/e;
#  s/\\subsubsec\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}\s*$/"\n\n$tr{podleader}item " . indexify("$1") . "\n\n"/e;
  s/\\subsubsec\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}(.*)$/"\n\n$tr{podleader}item " . indexify("$1") . "$3\n\n"/me;
#  s/\\subseckbd\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}:\s*/"\n\n$tr{podleader}head2 " . indexify("$1") . "\n\n"/e;
  s/\\subseckbd\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^:]*):\s*/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
  # Try to delimit by ' '
  s/\\subsec(?:ref|idx|op)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}(\S*)\s+/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
  s/\\subsec(?:ref|title|idx|op)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]*})+)}:?\s*/"\n\n$tr{podleader}head2 " . indexify("$1") . "\n\n"/e;

  # This is to skip preface in refcard:
  /\Q$tr{podleader}\Ehead1|\\title(?![a-zA-Z])\s*\{/o and $seen_start = 1
    or $seen_start or return;	# Skip now!

  s/\\title\s*\{([^{}\s]*)(\s+([^{}]*))?\}(\s*\\centerline\s*\{([^{}]*)\})?\s*/$tr{podleader}head1 NAME\n\n$1 - $3.  $5\n\n/ and $seen_title++
    unless $seen_title;
  s/\\title\s*\{([^{}\s]*)(\s+([^{}]*))?\}(\s*\\centerline\s*\{([^{}]*)\})?\s*/\n\n/;
  s/\\parskip.*/\n/g;		# Up to end of the line
  #s/([A-Z])\</$1 < /g;		# Disambiguate with POD...

  # Duplicate removal of settabs, since they may contain \hskip
  s/\\settabs.*//;
  s/^[ \t]*\\hskip\s*\w+/$tr{nbrk}/g;
  s/[ \t]*\\hskip\s*\w+/\n$tr{nbrk}/g;
  1 while s/ \\
	     ( (small|big)skip | newcolumn | noindent
	     | (short)?copyrightnotice | hfill | break | par
	     | leavevmode | strut | endgroup | bye
             )
	     (?![a-zA-Z])[ \t]*(\n\s*)
	     /\n\n/gx;

  s/'(\W)'/{\\tt '$1'}/g;
  s/(\\\w+)(\\hbox)/$1 $2/g;
  s/\\hbox\s*\{(?:\\it\b\s*)?((?:\\[\{\}]|[^{}]|\{[^{}]*\})*)\}/$1/g;

  # substitute non-verbatim code
  $acc = '';
  pos = 0;
  while ( s/\A(.*?)\\bprog//s ) {
    $acc .= basic_subst(strip_trail($1));
    $_ .= <DOC> until /(\\|@)eprog\b/ or eof(DOC);
    $acc .= wrap_code($1) if s/\A(?:tabs[^\n]*)?(?![a-zA-Z])[ \t]*\n?(.*?)(\\|@)eprog\s*//s;
  }
  $_ = $acc . basic_subst($_);

#  s/\\kbd\{/\{\\tt /g;		# startcode
#  s/\\typ\{/\{\\tt t_/g;	# startcode

  s/\$\s*(\@\[startbi\][A-Z]\@\[endbi\])\s*\$/$1/g;
#  s/\\p(\b|(?=[\d_]))/B<p>/g;
  #s/\$\\bf\b\s*([^\$]+)\$/C<B<$1>>/g;

  @lines = split /^$/m, $_;
  for (@lines) {
    s/>/\@[gt]/g unless /^\n*[ \t]/;
    s/</\@[lt]/g unless /^\n*[ \t]/;
  }
  $_ = join '', @lines;

  s/\$\$(.*?)\$\$\s*/\n\nS<  >$tr{startcode}$1$tr{endcode}\n\n/gs;
  s/\$([^\$]+)\$/$tr{startcode}$1$tr{endcode}/g;

  s/\\s(?:ref|idx){\s*([^{}]*)}/"X<" . for_index($1) . ">"/ge; #
  s/\\(?:ref|idx){\s*([^{}]*)}/"X<" . for_index($1) . ">$1"/ge;

# Conflict between different versions of PARI and refcard:
# s/\\(?:key|li)\s*{(.*)}\s*{(.+)}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/msg;
# s/\\(?:key|li)\s*{(.*)}\s*{}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/mgs;
# s/\\(key|var)(?![a-zA-Z])\s*{(\w+)}/C<$2>/mg;
  s/\\var\s*{X<(\w+)>(\w+)}/X<$1>$tr{startcode}$2$tr{endcode}/mg;
  s/\\var\s*{f{}lag}/$tr{startcode}flag$tr{endcode}/mg;

  s/\\metax(?![a-zA-Z])\s*{(.*)}\s*{\s*(\w+)(?=C\<)(.*)}[ \t]*\n/\n\n=item C<L<$2>$3>\n\n$1\n\n/mg;
  s/\\metax(?![a-zA-Z])\s*{(.*)}\s*{(.*)}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/mg;
  s/C\<\{\}=/C\<=/g;
  s/\\fl(?![a-zA-Z])/I<flag>/g;
  s/\\file(?![a-zA-Z])/F<file>/g;
  s/\\(unix|emacs)\b\s*(\{?)(\s*\\(no)?indent)?\s*/X<\U$1>$2/g;
  s/\A\\label\s*\{([\w:.-]*)\}([ \t]*\n\s*(?=[^\s=]))?/X<Label $1>/g;
  s/\\label\s*\{([\w:.-]*)\}/X<Label $1>/g;
  s/\\secref\s*\{([\w:.-]*)\}/L<Label $1>/g;
  s/\\begin(double)?indentedkeys\s*/\n\n=over\n\n/g;
  s/\\end(double)?indentedkeys\s*/\n\n=back\n\n/g;
  # begin/end group appear in very special context only
  s/\\begingroup\W.*//s;		# Eat to the end
  s/\n{3,}/\n\n/g;
  s/\\subsec\{((?:[^{}]|\{[^{}]*\})+)\}\s*/\n\n=back\n\nB<$1>\n\n=over\n\n/g; # In refcard
  # for refcard:
  s/{\\rm(?![a-zA-Z])\s*([^{}]*)}/$1/g;
  s/\\Z<>/\\/g;			# Optimize for readability

  # Now replace the POD stuff
  # Start with cosmetic stuff:
  $in_code = 0;
  s/(\@\[((start)|end)code\])/ ($3 ? $in_code++ : --$in_code) ? "" : $1 /ge;

  if ($dumb_pod) {
    my @stack;
    s /(\@\[((start)|end)(\w+)\])/
      if ($3) {			# Start
	push @stack, $4;
	(@stack > 1 ? "\@[end$stack[-2]]" : '') . $1
      } else {			# end
	pop @stack;
	$1 . (@stack ? "\@[start$stack[-1]]" : '')
      }
      /ge
  }
  1 while s/\@\[start(\w+)\](\s*)\@\[end\1\]/$2/g;

  s/\@\[(\w+)\]/\@!$pr{$1}/g;
  s/(\\\w+)\@!(\w)/$1 $2/g;
  s/\@!//g;
  s/\\([\{\}])/$1/g;

  # Normalize the spacing
  s/\n{3,}/\n\n/;
  s/\A([ \t]*\n)+//;
  # Single label is not healthy...
  print "\n" if $last_glued and /\A=/; # POD markup needs a new paragraph
  $last_glued = s/((\A|\n\n)(X<[^<>]+>)+)[ \t]*\n\n/$1\n/;

  print;
}

sub color {
  my($a);
  $_ = $_[0];
  if (/[^0-9]/ || $_ < 0 || $_ > 17)
    { print "bad color in gphelp: $_\n"; return ""; }
  if ($_ < 8) { $a = $_ + 30; } else { $a = $_ + 82; }
  return "\e[0;${a}m";
}

sub TeXprint {
  local($_) = $_[0];
  s/\@\[obr\]/{/g;
  s/\@\[cbr\]/}/g;
  s/\@\[ouml\]/"o/g;
  s/\@\[uuml\]/"u/g;
  s/\@\[agrave\]/`a/g;
  s/\@\[eacute\]/'e/g;
  s/\@\[ldollar\]/\$/g;
  s/\@\[end(bold|code|bcode|bi|it)\]/$ch/g;
  s/\@\[start(bold|code|bcode|bi)\]/$cb/g;
  s/\@\[startit\]/$cu/g;
  s/\@\[(dollar|empty|endl?word|endpodcode|startl?word|startpodcode)\]//g;
  s/\@\[pm\]/±/g;
  s/\@\[lt\]/</g;
  s/\@\[gt\]/>/g;
  s/\\([\{\}])/$1/g;
  s/\@\[nbrk\]/ /g; print "$_\n";
}

sub to_pod {
  $to_pod = $ARGV[1];
  inittr();
  $parifile = $to_pod;
  %compress = ('.gz', 'gzip -cd',
	       '.z', 'gzip -cd',
	       '.Z', 'zcat',
	      );
  foreach $suffix (keys %compress) {
    ($patt = $suffix) =~ s/(\W)/\\$1/;
    if ($to_pod =~ /$patt$/) { $pipe = $compress{$suffix}; last; }
  }
  if ($pipe) {
    open(DOC,"$pipe $parifile |") ||
      die "Cannot open pipe $pipe from $parifile: $!, stopped";
  } else {
    open(DOC,$parifile) || die "Cannot find file $parifile: $!, stopped";
  }
  $/='';			# Paragraph mode
  while (<DOC>) {
    &TeXprint_topod();
  }
  if ($pipe) {
    close(DOC) || die "Cannot close pipe `$pipe $parifile': $!, stopped";
  } else {
    close(DOC) || die "Cannot close file $parifile: $!, stopped";
  }
  cleanexit();
}
