#!/home/belabas/bin/solaris/perl
#
# $Id: gphelp.in,v 1.16 2000/11/03 21:00:19 karim Exp $
# 
# 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)
#
# 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.1.0";
$miscdir= "/home/belabas/GP/lib/pari";
# no expanded material (@key@) below
$wwwsite= "http://www.parigp-home.de/";
$docdir = $ENV{GPDOCDIR} || $ENV{GPHELP_DOCDIR};
$docdir = './doc' if not $docdir and -f './doc/translations';
$docdir = '../doc' if not $docdir and -f '../doc/translations';
if ($docdir =~ (/^\./)) { chomp($cwd = `pwd`); $docdir = "$cwd/$docdir"; }
$docdir ||= $miscdir;

$xdvi = $ENV{GPXDVI} || "xdvi";
$xdviref = $ENV{GPXDVIREF} || "$xdvi -paper 29.7x21cm";
$gzip = "gzip";
$zcat = "$gzip -dc";

$refcard = (@ARGV and $ARGV[-1] =~ /refcard/i);
&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";
  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
{
  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(); }
  }
  $detex = 1 if (!$detex && !$ENV{DISPLAY});
}

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

  $indent = "   ";

  chdir($docdir);
  $docfile = "usersch3.tex";
  $tmpdir = $ENV{GPTMPDIR} || $ENV{TMPDIR} || $ENV{GPHELP_TMPDIR} || "/tmp";
  $texfile = "$tmpdir/gp.help$$";

  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
{
  local($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 open_dvi_then_quit
{
  local($dvifile)= $_[0];
  local($viewer) = $_[1];
  $dvifile = "$docdir/$dvifile" if (! -f "$dvifile");
  die "could not find $dvifile" if (! -f "$dvifile");
  (!$ENV{DISPLAY} && !$ENV{GPXDVI}) && die "xdvi needs X-Windows";
  print "displaying \'$docdir/$dvifile\'.";
  system("$viewer $dvifile 2>/dev/null >/dev/null&");
  cleanexit();
}

sub treat
{
  local($help);
  $_ = $_[0];
  s/_QUOTE/'/g;
  s/_BACKQUOTE/`/g;
  s/_DOUBQUOTE/"/g;
  s/^ *"(.*)"([^"]*) *$/$1$2/;
  if (s/\@$//)
  {
    $found = 0;
    $searchall = 1;
    $help = $_;
    while (<usersch*.tex*>)
    {
      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)
  {
    open_dvi_then_quit("users.dvi",$xdvi) if (/^$/);
    open_dvi_then_quit("tutorial.dvi",$xdvi) if (/^tutorial$/);
    open_dvi_then_quit("refcard.dvi",$xdviref) if (/^refcard$/);
  }

  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);
  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
{
  local($maxlen) = 0;
  local($i,$nbcol,$current);
  local($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
{
  local($_) = $line;
  s/\n/ /g;
  return if (! /$help/);

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

sub apropos
{
  local($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();
      $current = $new; $line = "";
    }
    $line .= $_;
  }
  &apropos_check();
}

#
#  Tex Part
#
sub pretex
{
  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);

  chdir($tmpdir);
  system("tex $texfile.tex 2>/dev/null > /dev/null < /dev/null") == 0
    || die "could not process $texfile.dvi" if (! -f "$texfile.dvi");
  system("($xdvi $texfile.dvi 2>/dev/null >/dev/null; rm -f $texfile.tex $texfile.dvi $texfile.log)&");
  &handler(1);
}

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

sub handler
{
  &clean if (!$_[0]);
  $SIG{'INT'} = 'DEFAULT';
  $SIG{'QUIT'} = 'DEFAULT';
}

#
#  Detex Part
#
sub fit_loop
{
  local($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
{
  local($wi, @a);
  local($l) = -1;
  local($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 {
  local($in) = shift;
  $in =~ s/\@\[\w+\]//g;
  $in =~ s/\@[012]//g;
  ($in =~ /^\s*$/)? 1: 0;
}

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

sub format_text
{
  local($last_void) = 0;
  local($add_stuff) = 0;
  local($cols) = ($ENV{'COLUMNS'} || 80) - 1;
  local($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);
      nl("") if (!$last_void);
      $last_void = $add_stuff = 0;
      nl("\@[endbold]$indent" . &fit_line($first));
      while (@w) { nl(&fit_line($cols)); }
    }
    else
    {
      $add_stuff = 1; s/^ +//;
      @_ = split(/ /, $_);
      for (@_)
      {
	s/\Q$tr{nbrk}/ /g; push(@w, $_);
	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) = (shift,shift,shift);
  local (@tmp, $arg,$parity,$ok);
  local ($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
{
  local ($fun,$args);
  chomp;
# 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 !~ /: */));

  $fun = "\@[startbold]$fun\@[endbold]$args";
  if ($raw) { print("$fun "); } else { &TeXprint($fun); } 
# 2: start parsing the function description
  if ($_) { s/^ *://; &presubst(); }
  while (<DOC>)
  {
    last if /^\\(section|sub[sub]*sec)/i;
    &presubst();
  }
  if ($raw) { print join("\n", @text); return; }
    
  &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 
	    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 => '',
	  nbrk => 'S< >', 
	  startbold => 'B<',
	  endbold => '>',
	  startcode => 'C<',
	  startlink => 'L<',
	  endlink => '>',
	  endcode => '>',
	  startpodcode => 'C<',
	  endpodcode => '>',
	  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>',
	  empty => 'Z<>',
	  podleader => '=',
	);
}

sub basic_subst
{
  local($_) = shift;

  s/([^\\])\\\{/$1`lBrAcE'/g;
  s/([^\\])\\\}/$1`rBrAcE'/g;
  s/\A\\q?quad\s+/$tr{nbrk}$tr{nbrk}/;
  s|\\wwwsite|$wwwsite|g;
  s|\\miscdir|$miscdir|g;
  s/^\\def\\.*\{\n.*\n\}//gm;
  s/\\def\\.*//g;
  s/(\{[\w\s]+)\{\}([\s\w]+\})/$1$2/g;	# {nf{}init}

  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/(^|[^\\])%.*/$1/g;		        # comments
  # 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;

  # \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/\{ *\}//g;			# empty args
  s/(\\string)?\\_/_/g;
  s/\\([#\$&%|])/$1/g;
  s/\\(hat(?![a-zA-Z])|\^)({\\?\s*})?/^/g;
  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/\\(log|sin|cos|lim|tan|mod|sqrt|exp|ln|det|Re|Im|deg|wp)(?![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 | 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}??$tr{endbold}/g unless $to_pod;
  s/\\secref\{[^\}]*\}/Section ($tr{startbold}??$tr{endbold})/g unless $to_pod;
  s/\\label\{[^\}]*\}//g unless $to_pod;
  s/\\rightarrow(?![a-zA-Z])/C<--E<gt>>/g;
  s/\\longleftrightarrow(?![a-zA-Z])/C<E<lt>-----E<gt>>/g;

  s/\\(noindent|medskip|bigskip|smallskip|left|right)(?![a-zA-Z])[ \t]*//g;
  s/\\vfill *\\eject//g;
  s/\\(q|quad)(?![a-zA-Z])/  /g; 
  s/\\qquad(?![a-zA-Z])/    /g; 
  s/\\centerline\s*\{\s*(?:\\tt\b\s*)?(.*(\n[ \t].*)*)\}(?=\s*$)/    $1/g;
  s/\\centerline\s*\{\s*(?:\\tt\b\s*)?((?:[^{}]|\{[^{}]*\})*)\}/    $1/g;
  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/\\funs\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}/\\fun{}{$1}{$2}\\sidx{$2}/g;
  s/\\fun\s*\{([^{}]*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}/\\kbd{$1 \\key{$2}($3)}\\sidx{$2}/g;

  s/ *\\leq?(?![a-zA-Z]) *([^ ])/<=$1/g;
  s/ *\\geq?(?![a-zA-Z]) *([^ ])/>=$1/g;
  s/ *([=><]) */ $1 /g;
  s/ *<  *([=<]) */ <$1 /g;
  s/ *>  *([=>]) */ >$1 /g;
  s/ *([*+-\/^&=|:]) += */ $1= /g;
  s/ *\\Rightarrow */ ==> /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/\\obr/{/g; s/\\cbr/}/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/$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/\\kbd\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startcode}$1$tr{endcode}/g;

  s/\\key\{([^\}]*)\}/$tr{startbold}$1$tr{endbold}/g unless $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;
  if ($refcard) {
    s/\\(?:key|li)\{((?:[^{}]+(?=[{}])|\{[^{}]+\})*)\}\s*\{\}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/g if $refcard;
    s/\\(?:key|li)\{((?:[^{}]+(?=[{}])|\{[^{}]+\})*)\}\s*\{(([^{}]+(?=[{}])|\{[^{}]+\})*)\}/\n=item C<$2>\n\n$1\n/g if $refcard;
  }

  s/\\var\{([^\}]*)\}/$tr{startit}$1$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)\s*\{ *(?:\\it\b\s*)?(([^{}]+(?=[{}])|\{[^{}]+\})*)\}/$1/g;
  s/\\(text|hbox)//g;
  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/\{ *\\it */$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\{(.*?)\}/$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/`lBrAcE'/\\{/g;
  s/`rBrAcE'/\\}/g;

  s{\Q$tr{startcode}\E(?=(ftp|http)://)}{$tr{startlink}}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;
  $in =~ s/\@miscdir\b/$miscdir/g;
  if ($in =~ /\@com/)
  {
    $in = $` . $tr{endcode} . &basic_subst($') . $tr{startcode};
  }
  if ($in =~ /\@Ccom(.*)\*\//)
  {
    $in = $` . $tr{endcode} . &basic_subst($1) . $tr{startcode}
                                               . "*/" . &code_subst($');
  }
  $in;
}

sub wrap_code {
  my $in = shift;
  $in =~ s/^[ \t]+$//mg;
#  if ($in =~ /[A-Z]</ && 0) {			# No such things so early
#    $in =~ s/^(.)/\nS<  >$1/mg;
#  } else {
    $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 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}

  # \def\sectype#1#2{\subsec{Type \typ{#1} (#2s):}\sidx{#2}}
  # \def\sectypeindex#1#2#3{\subsec{Type \typ{#1} (#2):}\sidx{#3}}
  # \def\sectypes#1#2#3{\subsec{Types \typ{#1} and \typ{#2} (#3s):}\sidx{#3}}

  #  \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*{((?:[^{}]|\{[^{}]*\})*)}/\n\n$tr{podleader}head1 NAME\n\nlibPARI - $1\n\n$tr{podleader}head1 DESCRIPTION\n\n/;
  s/\\appendix\s*{((?:[^{}]|\{[^{}]*\})*)}/\n\n$tr{podleader}head1 NAME\n\nAppendix - $1\n\n$tr{podleader}head1 DESCRIPTION\n\n/;
  s/\\section\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*\{([^{}]*)\})?/$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*\{([^{}]*)\})?/\n\n/;
  s/\\parskip.*/\n/g;		# Up to end of the line
  #s/([A-Z])\</$1 < /g;		# Disambiguate with POD...
  s/\\((small|big)skip|newcolumn|noindent|(short)?copyrightnotice|hfill|break|par|leavevmode|strut|endgroup|bye)(?![a-zA-Z])[ \t]*/\n\n/g;
  s/^[ \t]*\\hskip\s*\w+//gm;

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

  # substitute non-verbatim code
  if (/\\bprog/)
  {
    s/\s*\\bprog(?:tabs[^\n]*)?(?![a-zA-Z])\s*(.*?)(\\|@)eprog\s*/wrap_code($1)/ges;
  }
  else { $_ = &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 /^\s/;
    s/</\@[lt]/g unless /^\s/;
  }
  $_ = join '', @lines;

  s/\$\$(.*?)\$\$[ \t]*/\n\nS<  >C<$1>\n\n/gs;
  s/\$([^\$]+)\$/C<$1>/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>C<$2>/mg;
  s/\\var\s*{f{}lag}/C<flag>/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/\\label\s*\{([\w:.-]*)\}/X<Label $1>/g;
  s/\\secref\s*\{([\w:.-]*)\}/L<Label $1>/g;
  s/\\begin(double)?indentedkeys/\n\n=over\n\n/g;
  s/\\end(double)?indentedkeys/\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\{((?:[^{}]|\{[^{}]*\})+)\}/\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
  s/\@\[(\w+)\]/\@!$pr{$1}/g;
  s/(\\\w+)\@!(\w)/$1 $2/g;
  s/\@!//g;
  s/\\([\{\}])/$1/g;

  print;
}

sub color
{
  local($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/\@\[end(bold|code|bcode|bi|it)\]/\e[m$ch/g;
  s/\@\[start(bold|code|bcode|bi)\]/$cb\e[1m/g;
  s/\@\[startit\]/$cu\e[4m/g;
  s/\@\[(dollar|empty|endl?word|endpodcode|startl?word|startpodcode)\]//g;
  s/\@\[pm\]//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();
}
