#!/usr/local/bin/perl
#
# r2b : convert a refer database to a BiBTeX database
# Copyright 1992, Dana Jacobsen (jacobsd@cs.orst.edu)
#
# version 0.1.1   17 Apr 92  jacobsd  Wrote original version
# version 0.2.0   20 Apr 92  jacobsd  Added tib support
# version 0.3.0   21 Apr 92  jacobsd  Rewrote heuristics
# version 0.4.0   22 Apr 92  jacobsd  Revamped the rofftotex stuff
# version 0.5.2   24 Apr 92  jacobsd  some cleanup
# version 0.6.0   25 Apr 92  jacobsd  understands names
# version 0.6.1   26 Apr 92  jacobsd  cleanup
# version 0.6.2   27 Apr 92  jacobsd  added support for a few more fields
# version 0.6.3   27 Apr 92  jacobsd  little more tib support
# version 0.6.4   27 Apr 92  jacobsd  added Roman-8 chars and more options
# version 0.6.5   27 Apr 92  jacobsd  integrated error routine
# version 0.7.0    2 May 92  jacobsd  added groff chars and fixed bugs
# version 0.7.1    2 May 92  jacobsd  fixed a few more things
#
# todo: final debugging for release
#       write man page
#
# All bug-fixes, suggestions, flames, and compliments gladly accepted.
#

$maxflength = 950;   # Bibtex doesn't want lines longer than this.
$maxllength = 14;    # maximum length of the text in a label (plus decade)
$prcontents = 0;     # print the contents (%Y) field.
$toterrors = 0;

$roffconv   = 1;   # -n
$tibfmt     = 0;   # -tib
$deroffonly = 0;   # -deroff-only
$nameconv   = 1;   # -noname-conv
$capprotect = 1;   # -nocap-protect = 0. -cap-protect = 2.

while (@ARGV) {
  $_ = shift @ARGV;
  /^--$/  && do { push(@files, @ARGV); undef @ARGV; next; };
  /^-n$/  && do { $roffconv = 0;    next; };
  /^-tib/ && do { $tibfmt = 1;      next; };
  /^-der/ && do { $deroffonly = 1;  next; };
  /^-non/ && do { $nameconv = 0;    next; };
  /^-noc/ && do { $capprotect = 0;  next; };
  /^-cap/ && do { $capprotect = 2;  next; };
  push (@files, $_);
}

print "%\n";
print "% converted from refer format by refer-to-bibtex 0.7.1\n";
print "%\n\n";

foreach $infile (@files) {
 open (IN, $infile) || ((warn "Can't open $infile: $!\n"), next);
 $linenum = 0;
 $linenum    = 0;
 $lastfield  = 0;
 $errors     = 0;

 while (<IN>) {
  chop;
  $linenum++;
  if ($deroffonly) { print &dorofftotex($_), "\n"; next; }

  /^\s*$/ && do { if ($lastfield) {
                    &doentry();
                    undef(%entry);
                    undef($lastfield);
                  }
                  next;
                };

  /^[^%]/ && do { if ($lastfield) {
                    if ( ($lastfield eq X) || ($lastfield eq Y) ) {
                      $entry{$lastfield} .= "\n" . $_;
                    } else {
                      $entry{$lastfield} .= " " . $_;
                    }
                  } else {
                    print STDERR "line $linenum:";
                    print STDERR "Line without field identifier: \n$_\n";
                    $errors++;
                  }
                  next;
                };

  $lastfield = 0;
  if (substr($_, 3, 1) eq '#')   { next; }     # comment
  $field = substr($_, 1, 1);
  if ($field eq '%')             { next; }     # comment

  #   Convert some lowercase fields to O.  Bibtex really doesn't have
  # any way of dealing with tib's lower case ``translated'' fields.
  #   Most of the lower case fields in my experience are usually typos.
  #   Abstracts, contents, and comments seem to be non-standard.  I have
  # assumed %X for abstract and %Y for contents.
  #   Refer, tib, and bib seem to have their own styles, and usually
  # people add on fields anyway.  This is reaching the limits of what
  # I can handle even by hand-translating...

  ($field =~ /^[or]$/) && do { $field = "O"; };

  $lastfield = $field;
  $rest = substr($_, 3);

  if ($field eq "\\") {
    print q/@preamble{ "/, substr($_, 1), qq/" }\n/;
    next;
  }

  if ( ($field eq A) || ($field eq Q) || ($field eq E) ) {
    $entry{$field} .= " and " . $rest;
  } elsif ( ($field eq X) ) {
    $entry{$field} .= "\n\n" . $rest;
  } else {
    $entry{$field} .= " " . $rest;
  }

  $allindents = "ABCDEGHIJKLMNOPQRSTUVXYZ*$";
  if (index($allindents, $field) == -1) {
    &anerror("Unknown field identifier: $_");
  }
 }

 if (%entry) {
  &doentry();
 }

 foreach $type (sort keys(%number)) {
  printf STDERR "%5d %s\n", $number{$type}, $type;
  $totalentries += $number{$type};
 }

 print STDERR "$totalentries entries, ";

 if (!$errors) { $errors = "no"; }
 print STDERR "$errors error";
 $errors == 1 ? print STDERR "\n" : print STDERR "s\n";
 $toterrors += $errors;
}

exit $toterrors;




##########################################
#
sub doentry {

# do some processing on each field
#   A, Q, and E : clean up " and " stuff
# refer format seems to be very non-standard about some things..
#   Look for "* Edition" in O and replace with an edition field
#   Look for Thesis in O and move to R

  foreach $field (keys(%entry)) {
    $entry{$field} =~ s/^\s+//;
    $entry{$field} = &dorofftotex($entry{$field});
    if (($field eq A) || ($field eq Q) || ($field eq E)){
      $entry{$field} =~ s/^and //;
      1 while $entry{$field} =~ s/  / /g;
    }
    if ($field eq O) {
      $_ = $entry{O};
      if ( ($edition) = /(\w+) edition/i ) {
        $entry{Ed} = $edition;
        delete $entry{O};
      }
      if ( (!$entry{R}) && (($edition) = /thesis/i) ) {
        $entry{R} = $entry{O};
        delete $entry{O};
      }
    }
    # titles:  cap-protect = 0, leave them alone.
    #          cap-protect = 1, protect multi-cap sequences, and singles. (def)
    #          cap-protect = 2, protect all capitals.
    if ($field eq T) {
      if ($capprotect == 1) {
        1 while $entry{$field} =~
                 s/([^{\\\w]|^)([A-Z]+)([^{}\\\w]|$)/$1{$2}$3/g;
        $entry{$field} =~ s/^{([A-Z])}/$1/;
      } elsif ($capprotect == 2) {
        $entry{$field} =~ s/([A-Z]+)/{$1}/g;
      }
      s/(^|[^\\])~/$1\\ /g;          # convert ties (~) to literal space (\ )
    }
    if (length($entry{$field}) > $maxflength) {
      $entry{$field} = substr($entry{$field}, 0, $maxflength-3);
      $entry{$field} .= "...";
      &anerror("field %$field longer than $maxflength characters.");
    }
  }

  # set date fields
  &parsedate();

  # convert names to BiBTeX format as best we can
  if ($entry{A}) {
    $entry{Key_A} = &parsename($entry{A}, A);
    $entry{A} = $fname;
    $aeditors = $editors;
    $acauthor = $corpauthors;
  }
  if ($entry{E}) {
    $entry{Key_E} = &parsename($entry{E}, E);
    $entry{E} = $fname;
  } elsif ($aeditors) {
    $entry{E}     = $entry{A};
    $entry{Key_E} = $entry{Key_A};
    delete $entry{Key_A};
    delete $entry{A};
  }
  if ($entry{Q} || $entry{I}) {
    if ($entry{Q}) {
      ($entry{Key_Q}) = split(/[\s~]/, $entry{Q});
    } else {
      ($entry{Key_I}) = split(/[\s~]/, $entry{I});
    }
    if ($acauthor) {
      ($entry{Key_A}) = split(/[\s~]/, $entry{A});
      $entry{Key_A} =~ s/^{([^}]*)}?.*$/$1/;
    }
  } elsif ($acauthor) {
    $entry{Q}     = $entry{A};
    ($entry{Key_Q}) = split(/[\s~]/, $entry{Q});
    $entry{Key_Q} =~ s/^{([^}]*)}?.*$/$1/;
    delete $entry{Key_A};
    delete $entry{A};
  }

  # set or generate key
  &genkey();

  # determine the Entry Type
  # This is where the heuristics come into play.  We need to examine what
  # fields we were given, and sometimes examine the field contents, to
  # determine what type of entry this is.

  if ($entry{J}) {
    $type = 'article';
    $_ = $entry{J};
    if (/^proc\w*\.\s/i || /proceeding/i || /conference/i || /workshop/i) {
      $type = 'inproceedings';
      if (!$entry{B}) {
        $entry{B} = $entry{J};
        delete $entry{J};
      }
    }
  } elsif ($entry{B}) {
    if ($entry{A} || $entry{Q}) {
      $type = 'incollection';
      $_ = $entry{B};
      if (/^proc\w*\.\s/i || /proceeding/i || /conference/i || /workshop/i) {
        $type = 'inproceedings';
      }
    } else {
      $type = 'proceedings';
    }
  } elsif ($entry{R}) {
    $type = 'techreport';
    $_ = $entry{R};
    ($reptype, $repnumber) = /(.+)\s+(\S+)$/;
    $_ = $repnumber;
    if (!/\d/) {
      $reptype = $entry{R};
      undef $repnumber;
    }
    $_ = $reptype;
    s/^{\\[rbi][mft] //;        # just in case someone changed the font
    tr/A-Za-z//cd;              # only A-z are left
    if (/^phd/i) {
      $type = 'phdthesis';
      $reptype = "Ph.{D}. Thesis";
    }
    if (/^master/i || /^m[as]thes/i) {
      $type = 'mastersthesis';
      $reptype = "Master's Thesis";
    }
    if (/^phd/i || /^master/i || /^m[as]thes/i) {
      if ($entry{R} =~ /thesis/i) {
        ($repnumber) = $entry{R} =~ /thesis\W*(.*)$/i;
      }
      if ($entry{R} =~ /dissert/i) {
        $reptype =~ s/Thesis/Dissertation/;
        ($repnumber) = $entry{R} =~ /dissert\w*\W*(.*)$/i;
      }
    }
    /^draft/i     && ($type = 'unpublished');
    /^unpublish/i && ($type = 'unpublished');

    if (!$entry{N}) {
      $entry{N} = $repnumber;
    }
    if ( (!$entry{Q}) && ($entry{I}) ) {
      $entry{Q} = $entry{I};
      delete $entry{I};
    }
  } elsif ($entry{I}) {
    $type = 'book';
  } else {
    $type = 'misc';
  }

  $number{$type}++;

  # if we have an institution but no author, the Inst. is the author
  if ( ($entry{Q}) && (!$entry{A}) ) {
    $entry{A} = "{" . $entry{Q} . "}";
  }

  # if there is no address, but a "header" field, assume H stands for "held in"
  if ( ($entry{H}) && (!$entry{C}) ) {
    $entry{C} = $entry{H};
    delete $entry{H};
  }
  # set institution to be the corporate author
  $entry{Ins} = $entry{Q};


  # Change things around for each types
  $_ = $type;

  /^mastersthesis/ && do { $entry{Sch} = $entry{Ins}; delete $entry{Ins}; };
  /^phdthesis/     && do { $entry{Sch} = $entry{Ins}; delete $entry{Ins}; };
  /^unpublished/   && do { $entry{O} .= $entry{Ins}; delete $entry{Ins}; };

  # Syntax checking

  /^article/       && (&syntax(A, T, J, Yr));
  /^book/          && (&syntax(AE, T, I, Yr));
  /^incollection/  && (&syntax(A, T, B, I, Yr));
  /^inproceedings/ && (&syntax(A, T, B, Yr));
  /^mastersthesis/ && (&syntax(A, T, Sch, Yr));
  /^phdthesis/     && (&syntax(A, T, Sch, Yr));
  /^proceedings/   && (&syntax(BT, Yr));
  /^techreport/    && (&syntax(A, T, Ins, Yr));
  /^unpublished/   && (&syntax(A, T, O));

  # set up the entry output string

  $ent = '';
  $ent .= "@$type\{$key,\n";

  if ($entry{Key}) { $ent .= "   key = \{$entry{Key}\},\n"; }
  if ($entry{A})   { $ent .= "   author = \{$entry{A}\},\n"; }
# if ($entry{Q})   { }
  if ($entry{Ins}) { $ent .= "   institution = \{$entry{Ins}\},\n"; }
  if ($entry{Sch}) { $ent .= "   school = \{$entry{Sch}\},\n"; }
  if ($entry{E})   { $ent .= "   editor = \{$entry{E}\},\n"; }
  if ($entry{T})   { $ent .= "   title = \{$entry{T}\},\n"; }
  if ($entry{B})   {
    if ($entry{T}) {
                     $ent .= "   booktitle = \{$entry{B}\},\n";
    } else {
                     $ent .= "   title = \{$entry{B}\},\n";
    } }
  if ($entry{J})   { $ent .= "   journal = \{$entry{J}\},\n"; }
  if ($entry{R})   { $ent .= "   type = \{$reptype\},\n"; }
  if ($entry{V})   { $ent .= "   volume = \{$entry{V}\},\n"; }
  if ($entry{S})   { $ent .= "   series = \{$entry{S}\},\n"; }
  if ($entry{N})   { $ent .= "   number = \{$entry{N}\},\n"; }
  if ($entry{Ed})  { $ent .= "   edition = \{$entry{Ed}\},\n"; }
  if ($entry{P})   { $ent .= "   pages = \{$entry{P}\},\n"; }
  if ($entry{I})   { $ent .= "   publisher = \{$entry{I}\},\n"; }
  if ($entry{C})   { $ent .= "   address = \{$entry{C}\},\n"; }
  if ($entry{Mo})  { $ent .= "   month = \{$entry{Mo}\},\n"; }
  if ($entry{Yr})  { $ent .= "   year = \{$entry{Yr}\},\n"; }
  if ($entry{X})   { $ent .= "   abstract = \{$entry{X}\},\n"; }
  if ($entry{'$'}) { $ent .= "   price = \{$entry{'$'}\},\n"; }
  if ($entry{'*'}) { $ent .= "   copyright = \{$entry{'*'}\},\n"; }
  if ($entry{K})   { $ent .= "   keywords = \{$entry{K}\},\n"; }
  if ($entry{M})   { $ent .= "   mrnumber = \{$entry{M}\},\n"; }
  if ($entry{U})   { $ent .= "   annote = \{$entry{U}\},\n"; }
  if ($entry{G})   { $ent .= "   note = \{$entry{G}\},\n"; }
  if ($entry{H})   { $ent .= "   note = \{$entry{H}\},\n"; }
  if ($entry{O})   { $ent .= "   note = \{$entry{O}\},\n"; }
  if ($entry{Z})   { $ent .= "   note = \{$entry{Z}\},\n"; }
  if ($entry{Y})   { if (!$prcontents) { $entry{Y} = "(not listed)"; }
                     $ent .= "   contents = \{$entry{Y}\},\n"; }

  substr($ent, -2, 1) = '';
  $ent .= "\}\n\n";

  &printerrors();
  print $ent;
}

##########################################

#
# date looks like                     month     year
# ----------------------------------- --------- -----
# 1984                                          1984
# August 1984                         August    1984
# 21 August 1984                      August    1984
# August 18-21, 1984                  August    1984
# 18-21 August 1991                   August    1991
# July 31-August 4, 1984 1984         July      1984
# February 1984 (revised May 1991)    February  1991
# Winter 1990                         Winter    1990
# to appear                                     to appear
#
sub parsedate {
  $date = $entry{D};

  $date =~ tr/A-Z/a-z/;
  $date =~ tr/a-z0-9\-//cd;    # delete non-ANs (not -, so 18-21 is not a year)
  $_ = substr($date, -4, 4);
  while (/\d\d\d\d/) {                           # are last 4 the year?
    $entry{Yr} = $_;
    $date = substr($date, 0, length($date) - 4);
    $_ = substr($date, -4, 4);
  }
  $entry{YrKey} = $entry{Yr} ? $entry{Yr} : "????";
  $entry{Decade} = substr($entry{YrKey}, 2, 2);
  if (length($date) == 0) { return; }
  $date =~ tr/a-z//cd;                           # delete any remaining nums
  if (length($date) == 0) { return; }
                   # did use:  ($entry{Mo}) = split(/-/,$date);
                   # but this screws us up: "February 1991 (revised May 1991)"
  $_ = $date;
  /jan/ && do { $entry{Mo} = "January"; };
  /feb/ && do { $entry{Mo} = "February"; };
  /mar/ && do { $entry{Mo} = "March"; };
  /apr/ && do { $entry{Mo} = "April"; };
  /may/ && do { $entry{Mo} = "May"; };
  /jun/ && do { $entry{Mo} = "June"; };
  /jul/ && do { $entry{Mo} = "July"; };
  /aug/ && do { $entry{Mo} = "August"; };
  /sep/ && do { $entry{Mo} = "September"; };
  /oct/ && do { $entry{Mo} = "October"; };
  /nov/ && do { $entry{Mo} = "November"; };
  /dec/ && do { $entry{Mo} = "December"; };

  if (!$entry{Mo}) {
    if (!$entry{Yr}) {
      $entry{Yr} = $entry{D};
      $entry{YrKey} = "????";
    }
    else {
      $date = $entry{D};
      $date =~ tr/A-Za-z, //cd;
      $date =~ s/ $//g;
      $entry{Mo} = $date;
    }
  }
  $entry{Decade} = substr($entry{YrKey}, 2, 2);
}

##########################################
# key is Author's last name followed by last 2 digits of year.
# in corporate author's case, key is first word and first 2 digits.
# order is L, A, Q, E, I, "Anonymous"
# in case of conflict, ascending letters are added to the end
#
sub genkey {
  local($noadd) = @_;
  local($name);

  if ($entry{L}) {
    $key = $entry{L};
  } else {
    $name = $entry{Key_A} || $entry{Key_Q} || $entry{Key_E}
            || $entry{Key_I} || $noadd || "Anonymous";

    $name = sprintf("%.${maxllength}s", $name);
    $key = $name . $entry{Decade};

    if ($allkeys{$key}) {
      $key .= 'a';
      while ($allkeys{$key}) {
        substr($key,-1,1)++;
      }
    }
  }
  $key =~ s/,//g;
  if ($noadd) {
    return($key);
  }

  $allkeys{$key} = $key;
  if ($name eq "Anonymous") {
    $entry{Key} = $key;
  }
}


##########################################
# parsename parses names into BiBTeX format
#
# This uses heuristics to parse a name into First, von, Last, and Jr
# parts.  It handles multiple names (John doe, jane doe) on a line.
# It does not handle names in "last, first" format.
# it returns a key (last name of author or editor, first name of corp).
# It sets $fname to the full bibtex name.
# It sets $editors, $authors, or $corpauthors if it thinks the name is one.
sub parsename {
  local($allnames, $ntype) = @_;
  local($firstn, $vonn, $lastn, $jrn);
  local(@names, $keyn, $oname, $nname, $rest);

  undef $fname;
  $editors = $authors = $corpauthors = 0;
  if (!($allnames =~ /\s/)) {
    $corpauthors = 1;
  }
  @names = split(/ and /, $allnames);
  if (!$nameconv) {
    $fname = $allnames;
    $_ = shift @names;
    if ($corpauthors) {
      ($name) = /^\s*(\S*)/;
    } else {
      ($name) = /(\S*)\s*$/;
    }
    return $name;
  }
  while (@names) {
    $oname = $name = shift @names;
    $firstn = $vonn = $lastn = $jrn = '';

    $name =~ s/\s+([sj]r[\.]?|edi?t?o?r?s?[\.]?|I+)(,|$)/, $1/i;
    $name =~ s/,,/,/;
    ($nname, $jrn) = split(/,/, $name, 2);
    $nname =~ s/\s+$//;
    $jrn  =~ s/^[\s~]+//;
    if ($jrn =~ /\s/) {
      ($jrn, $rest) = $jrn =~ /([sj]r[\.]?|edi?t?o?r?s?[\.]?|I+)?,?\s*(.*)$/i;
      unshift(@names, $rest);
    }
    $jrn =~ s/([^\\])~/$1 /g;
    ($firstn) = $nname =~ /^((\S* )*)/;
    $nname = substr($nname, length($firstn));
    $lastn = $nname;
    $lastn =~ s/([^\\])~/$1 /g;
    $firstn =~ s/([^\\])~/$1 /g;
    while ($firstn =~ / [a-z]+ $/) {
      ($rest) = $firstn =~ / ([a-z]+ )$/;
      $vonn = $rest . $vonn;
      $firstn = substr($firstn, 0, length($firstn) - length($rest));
    }
    while ($lastn =~ /^[a-z]+ /) {
      ($rest) = $lastn =~ /^([a-z]+ )/;
      $vonn .= $rest;
      $lastn = substr($lastn, length($rest));
    }

    if ($jrn) {
      if ($jrn =~ /^(et\s*al)|(others)$/i) {
        undef $jrn;
        unshift(@names, "others");
      }
      if ($jrn =~ /^edi?t?o?r?s?[\.]?$/i) {
        undef $jrn;
        $editors = 1;
      }
      if ($jrn =~ /^inc[\.]?$/i) {
        $lastn .= ", " . $jrn;
        undef $jrn;
        $corpauthors = 1;
      }
    }
    if ($lastn =~ /^(et\s*al)|(others)$/i) {
      $lastn = "others";
    }
    if ($lastn =~ /\s/) {
      $lastn = "{" . $lastn . "}";
    }

    if (!$keyn) {
      if ($corpauthors) {
        ($keyn) = $lastn =~ /^(\S+)/;
      } else {
        ($keyn) = $lastn;       # =~ /(\S+)$/;  # if you want last of Last
      }
      $keyn =~ tr/A-Za-z0-9\/\-//cd;
    }

    if ($jrn) {
      $fname .= " and " . $vonn . $lastn . ", " . $jrn . ", " . $firstn;
    } else {
      $fname .= " and " . $firstn . $vonn . $lastn;
    }
  }
  $fname =~ s/^ and\s+//;
  $fname =~ s/\s+$//;
  1 while $fname =~ s/  / /g;
  if ($ntype eq A) {
    if ($corpauthors) {
      &anerror("Corporate Author (%Q) in %A.");
    } elsif ($editors) {
      &anerror("Editors (%E) in %A.");
    }
  } elsif ($ntype eq Q) {
    if ($editors) {
      &anerror("Editors (%E) in %Q.");
    }
  } elsif ($ntype eq E) {
    if ($corpauthors) {
      &anerror("Corporate Author (%Q) in %E.");
    }
  }

  return $keyn;
}



##########################################
# syntax does syntax checking
#
sub syntax {
  foreach $field (@_) {
    if ($field eq AE) {
      if ( (!$entry{A}) && (!$entry{E}) ) {
        &anerror("Missing A and E (Author and Editor) fields.");
      }
    } elsif ($field eq BT) {
      if ( (!$entry{B}) && (!$entry{T}) ) {
        &anerror("Missing T (Title) field.");
      }
    } else {
      if (!$entry{$field}) {
        &anerror("Missing $field field.");
      }
    }
  }
}



##########################################
# stores error information until it gets printed
#
# This allows us to fully process the entry so we can print out
# valid key information without having to go through ugly gyrations.
#
sub anerror {
  local($err) = @_;

  push(@errorstring, $err);
  $errors++;
}



##########################################
# prints out stored error information
#
sub printerrors {
  local($klen, $errst);

  if (@errorstring) {
    $klen = $maxllength;  # a little short, but most labels aren't this long
    foreach $_ (@errorstring) {
      $errst .= sprintf("%-${klen}s (%5d): %s\n", $key, $errline, $_);
    }
    print STDERR $errst;
    undef @errorstring;
  }
  $errline = $linenum+1;
}



##########################################
# converts *roff characters to TeX characters
#
# Note:  I detest troff and do not use it, so I may have easily messed up
#        on some of these.  I have tried the standard things out and they
#        seem to work.  Send corrections if you have them.
#
# Is there a better way to do this?  (i.e. eval)
#
sub dorofftotex {
  local($_) = @_;

  if (!$roffconv) { return $_; }

  study;   # presumably this will help us.

  # tib: refer format, TeX formatting.  What a weird combination...
  #      This should probably be set up to read a configuration file into
  #      a variable then use eval.  This is really ugly, but it seems to
  #      catch a lot of the common cases.  If there is such a beast as a
  #      "detibify" program, then this won't be necessary.
  if ($tibfmt) {
    1 while s#\\egroup(.*)\\bgroup#{\\Reffont $1}#g;
    s/\\Citefont//g;
    s/\\ACitefont//g;
    s/\\Authfont//g;
    s/\\Titlefont//g;
    s/\\Tomefont/\\sl/g;
    s/\\Volfont//g;
    s/\\Flagfont//g;
    s/\\Reffont/\\rm/g;
    s/\\Smallcapsfont/\\sevenrm/g;
    s/\\Flagstyle//g;            # This should be smarter

    if (/\|/) {
      s/\|JAN\|/January/g;       # yes, the parsedate routine can handle
      s/\|FEB\|/February/g;      # these most of the time, but sometimes
      s/\|MAR\|/March/g;         # they're put in the middle of non-date
      s/\|APR\|/April/g;         # strings, so we'd better convert them.
      s/\|MAY\|/May/g;
      s/\|JUN\|/June/g;
      s/\|JUL\|/July/g;
      s/\|AUG\|/August/g;
      s/\|SEP\|/September/g;
      s/\|OCT\|/October/g;
      s/\|NOV\|/November/g;
      s/\|DEC\|/December/g;

      s/\|UNIV\|/University/g;
      s/\|DEPT\|/Department/g;
      s/\|DCS\|/Department of Computer Science/g;
      s/\|CSD\|/Computer Science Department/g;
      s/\|TR\|/Technical Report/g;

      s/\|COMPJ\|/The Computer Journal/g;
      s/\|JACM\|/Journal of the ACM/g;
      s/\|CACM\|/Communications of the ACM/g;
      s/\|SGPLN\|/Sigplan Notices/g;
      s/\|SIAJC1\|/SIAM Journal on Computing/g;
      s/\|ACTAI2\|/Acta Informatica/g;
      if (s/\|LNCS\|/Lecture Notes in Computer Science/g) {
        $entry{I} .= " " . "Springer-Verlag";
        $entry{C} .= " " . "New York--Heidelberg--Berlin"; }
      if (s/\|IFBSV\|/Inf. Fachb./g) {
        $entry{I} .= " " . "Springer-Verlag";
        $entry{C} .= " " . "New York--Heidelberg--Berlin"; }
      s/\|SCICP\|/Science of Computer Programming/g;
      s/\|SP&E\|/Software---Practice and Experience/g;
      s/\|POPL\|/ACM Symp. on Principles of Progr. Languages/g;
      s/\|TOPLAS\|/ACM Trans. Progr. Languages and Systems/g;

      if (s/\|Addison\|/Addison Wesley/g) {
        $entry{C} .= " " . "Reading, MA"; }
      if (s/\|PrHall\|/Prentice Hall/g) {
        $entry{C} .= " " . "Englewood Cliffs, NJ"; }
      if (s/\|NHoll\|/North-Holland/g) {
        $entry{C} .= " " . "Amsterdam"; }
      if (s/\|Cambridge\|/Cambridge University Press/g) {
        $entry{C} .= " " . "New York"; }
      if (s/\|Springer\|/Springer-Verlag/g) {
        $entry{C} .= " " . "New York--Heidelberg--Berlin"; }

      s/\|TWEINF\|/Onderafdeling der Informatica, Tech. Hogeschool Twente/g;
      s/\|TUMINF\|/Institut f{\"u}r Informatik, Tech. University M{\"u}nchen/g;
      if (s/\|IBMTJW\|/IBM T.J. Watson Research Center/g) {
        $entry{C} .= " " . "Yorktown Heights, NY"; }
      if (s/\|INRIA\|/INRIA/g) {
        $entry{C} .= " " . "Rocquencourt"; }
      if (s/\|IRIAL\|/IRIA-Laboria/g) {
        $entry{C} .= " " . "Rocquencourt"; }
    }
    return $_;
  }

  s#_#_U#g;   # _ will be the escape character

  # don't do troff character conversion if there aren't any backslashes
  # in the string.  Hopefully this will save a little work.
  if (/\\/) {

    # to make commands, we need command characters, but we don't want
    # any of the command characters that they use to be passed through
    # or we'll end up with invalid input.  So, _ is the escape character.

    # _U is _
    # _B is a backslash
    # _I is a literal backslash
    # _C is {\
    # _L is {
    # _R is }
    # _l is <
    # _g is >
    # _T is ~
    # _A is ^
    # _D is $
    # _M is $\
    # _V is |
    # _E is ${}^
    # _H is \hbox{
    # _h is \leavevmode
    # _c is a special continuation character for long lines

    # I'm not sure I quite get this -- refer strips off one \ for most
    # characters it seems.  But other times it doesn't.  Argh!  I'll
    # go ahead and replace \\ with \ to handle this.  It shouldn't ever
    # come up that this is bad since \e and \(rs are a real backslashes.

    s#\\\\#\\#g;                             #  \\    -> \

    # font changes
    # if one uses \fP, everything is fine -- otherwise we need to get complex
    $fbraces = 0;
    $fbraces += s#\\f[1R]#_Crm #g;           #  \f1   -> {\rm
    $fbraces += s#\\f[2I]#_Cit #g;           #  \f2   -> {\it
    $fbraces += s#\\f[3B]#_Cbf #g;           #  \f3   -> {\bf
    $fbraces -= s#\\fP#_R#g;                 #  \fP   -> }
    while ($fbraces) {                       # too many {'s
      if ($fbraces < 0) {
        $nchanges = s#_R##;
        &anerror("Used \\fP with no previous font.");
        $fbraces += $nchanges;
      } else {
        $nchanges = s#(_Cit )(.*)_Crm #$1$2_R#;
        if (!$nchanges)
          { $nchanges = s#(_Cbf )(.*)_Crm #$1$2_R#; }
        if (!$nchanges)
          { $nchanges = s#(_C\w\w )(.*)_C\w\w #$1$2_R#; }
        if (!$nchanges) {
          $_ .= "_R";                        # couldn't get it, so stick a } on
          $fbraces--;
          &anerror("Problems with font changing.  Suggest using \\fP.");
        }
        $fbraces -= ($nchanges * 2);
      }
    }

    # other troff special characters
    # some of these aren't available as standard TeX, so I made up replacements.
    # Perhaps they should be def'ed in a preamble and used that way, but I
    # doubt most files use \(rg, \(ct, and such, so why waste resources.
    # If you're really concerned about eth, thorn, yogh, or ogonek, go get
    # the cmoer fonts -- they do the characters right.

    s#\\\(bu#_Mbullet_D#g;                   # \(bu  -> $\bullet$
    s#\\\(ci#_Mbigcirc_D#g;                  # \(ci  -> $\bigcirc$
    s#\\\(sq#_MBox_D#g;                      # \(sq  -> $\Box$
    s#\\\(ct#_h_Brm_Brlap/c_R#g;             # \(ct  -> \hbox{\rm\rlap/c}
    s#\\\(rg#_h_Braise.6em_H_Booalign_L_L_Bmathhexbox20D_R_Bcrcr\n_Bhfil_Braise.07ex_Hr_R_Bhfil_R_R_R#g;
    s#\\\(co#_h_Braise.6em_H_Bcopyright_R_R#g;
    s#\\\(lh#_MLongleftarrow_D#g;            # \(lh  -> $\Longleftarrow$ #wrong!
    s#\\\(rh#_MLongrightarrow_D#g;           # \(rh  -> $\Longrightarrow$
    s#\\\(dg#_Bdag #g;                       # \(dg  -> \dag
    s#\\\(dd#_Bddag #g;                      # \(dd  -> \ddag
    s#\\\(sc#_BS #g;                         # \(sc  -> \S
    s#\\\(br#_D_V_D#g;                       # \(br  -> $|$
    s#\\\(fm#_E_Bprime_D#g;                  # \(fm  -> ${}^\prime$
    s#\\\(de#_E_Bcirc_D#g;                   # \(de  -> ${}^\circ$
    s#\\\-#---#g;                            # \-    -> --
    s#\\\(em#--#g;                           # \(em  -> --
    s#\\\(hy#-#g;                            # \(hy  -> -
    s#\\\(ru#_Cvrule width1.2ex height0.1ex depth0ex_R#g;
    s#\\\(ul#_Cvrule width1.2ex height-.3ex depth.4ex_R#g;
    s#\\\(aa#_C'_L _R_R#g;                   # \(aa  -> {\'{ }}
    s#\\'#_C'_L _R_R#g;                      # \'    -> {\'{ }}
    s#\\\(ga#_C`_L _R_R#g;                   # \(ga  -> {\`{ }}
    s#\\`#_C`_L _R_R#g;                      # \`    -> {\`{ }}
    s#\\\(sl#/#g;                            # \(sl  -> /
    s#\\e#_I#g;                              # \e    -> $\backslash$
    s#\\0#_T#g;                              # \0    -> ~
    s#\\ #_B #g;                             # '\ '  -> '\ '
    s#\\\^#_D_B,_D#g;                        # \^    -> $\,$
    s#\\\|#_D_B:_D#g;                        # \|    -> $\:$
    s#\\\(fi#fi#g;                           # \(fi  -> fi
    s#\\\(fl#fl#g;                           # \(fl  -> fl
    s#\\\(ff#ff#g;                           # \(ff  -> ff
    s#\\\(Fi#ffi#g;                          # \(Fi  -> ffi
    s#\\\(Fl#ffl#g;                          # \(Fl  -> ffl

    s#\\\(pl#_D+_D#g;                        # \(pl  -> $+$
    s#\\\(mi#_D-_D#g;                        # \(mi  -> $-$
    s#\\\(mu#_Mtimes_D#g;                    # \(mu  -> $\times$
    s#\\\(di#_Mdiv_D#g;                      # \(di  -> $\div$
    s#\\\(\+\-#_Mpm_D#g;                     # \(+-  -> $\pm$
    s#\\\(no#_Mneg_D#g;                      # \(no  -> $\neg$
    s#\\\(\*\*#_Mast_D#g;                    # \(**  -> $\ast$
    s#\\\(eq#_D=_D#g;                        # \(eq  -> $=$
    s#\\\(>=#_Mgeq_D#g;                      # \(>=  -> $\geq$
    s#\\\(<=#_Mleq_D#g;                      # \(<=  -> $\leq$
    s#\\\(==#_Mequiv_D#g;                    # \(==  -> $\equiv$
    s#\\\(~=#_Msimeq_D#g;                    # \(~=  -> $\simeq$
    s#\\\(ap#_Msim_D#g;                      # \(ap  -> $\sim$
    s#\\\(!e#_Mneq_D#g;                      # \(!e  -> $\neq$
    s#\\\(\->#_Mrightarrow_D#g;              # \(->  -> $\rightarrow$
    s#\\\(<\-#_Mleftarrow_D#g;               # \(<-  -> $\leftarrow$
    s#\\\(ua#_Muparrow_D#g;                  # \(ua  -> $\uparrow$
    s#\\\(da#_Mdownarrow_D#g;                # \(da  -> $\downarrow$
    s#\\\(cu#_Mcup_D#g;                      # \(cu  -> $\cup$
    s#\\\(ca#_Mcap_D#g;                      # \(ca  -> $\cap$
    s#\\\(sb#_Msubset_D#g;                   # \(sb  -> $\subset$
    s#\\\(sp#_Msupset_D#g;                   # \(sp  -> $\supset$
    s#\\\(ib#_Msubseteq_D#g;                 # \(ib  -> $\subseteq$
    s#\\\(ip#_Msupseteq_D#g;                 # \(ip  -> $\supseteq$
    s#\\\(if#_Minfty_D#g;                    # \(if  -> $\infty$
    s#\\\(es#_Memptyset_D#g;                 # \(es  -> $\emptyset$
    s#\\\(is#_Mint_D#g;                      # \(is  -> $\int$
    s#\\\(pd#_Mpartial_D#g;                  # \(pd  -> $\partial$
    s#\\\(sr#_Msurd_D#g;                     # \(sr  -> $\surd$
    s#\\\(gr#_Mnabla_D#g;                    # \(gr  -> $\nabla$
    s#\\\(pt#_Mpropto_D#g;                   # \(pt  -> $\propto$
    s#\\\(mo#_Min_D#g;                       # \(mo  -> $\in$
    s#\\\(or#_Mmid_D#g;                      # \(or  -> $\mid$
    s#\\\((\d)(\d)#_D$1_Bover$2_D#g;         # \(14  -> $1\over4$

    s#\\\(m\.#_Mcdot_D#g;                    # \(m.  -> $\cdot$
    s#\\\(!s#_Mnot_Bsubset_D#g;              # \(!s  -> $\not\subset$
    s#\\\(an#_Mwedge_D#g;                    # \(an  -> $\wedge$
    s#\\\(lo#_Mvee_D#g;                      # \(lo  -> $\vee$
    s#\\\(tf#_D_H._R_Braise.9ex_H._R_H._R_D#g;# \(tf -> .:.
    s#\\\(cm#_Mni_D#g;                       # \(cm  -> $\ni$
    s#\\\(fa#_Mforall_D#g;                   # \(fa  -> $\forall$
    s#\\\(te#_Mexists_D#g;                   # \(te  -> $\exists$
    s#\\\(!m#_Mnotin_D#g;                    # \(!m  -> $\notin$
    s#\\\(a\+#_Moplus_D#g;                   # \(a+  -> $\oplus$
    s#\\\(ax#_Motimes_D#g;                   # \(ax  -> $\otimes$
    s#\\\(ag#_Mangle_D#g;                    # \(ag  -> $\angle$
    s#\\\(rn#_Moverline_L _R_D#g;            # \(rn  -> $\overline{ }$
    s#\\\(<<#_Mll_D#g;                       # \(<<  -> $\ll$
    s#\\\(>>#_Mgg_D#g;                       # \(>>  -> $\gg$
    s#\\\(<>#_Mleftrightarrow_D#g;           # \(<>  -> $\leftrightarrow$
    s#\\\(//#_D/_D#g;                        # \(//  -> $/$
    s#\\\(L<#_Mlangle_D#g;                   # \(L<  -> $\langle$
    s#\\\(R>#_Mrangle_D#g;                   # \(R>  -> $\rangle$
    s#\\\(dm#_Mdiamond_D#g;                  # \(dm  -> $\diamond$
    s#\\\(lt#_Mlbrace_D#g;                   # \(lt  -> $\lbrace$
    s#\\\(rt#_Mrbrace_D#g;                   # \(rt  -> $\rbrace$
    s#\\\(lb#_Mlfloor_D#g;                   # \(lb  -> $\lfloor$
    s#\\\(rt#_Mrfloor_D#g;                   # \(rt  -> $\rfloor$
    s#\\\(lk#_Mlbrace_D#g;                   # \(lk  -> $\lbrace$
    s#\\\(rk#_Mrbrace_D#g;                   # \(rk  -> $\rbrace$
    s#\\\(lf#_Mlfloor_D#g;                   # \(lf  -> $\lfloor$
    s#\\\(rf#_Mrfloor_D#g;                   # \(rf  -> $\rfloor$
    s#\\\(lc#_Mlceil_D#g;                    # \(lc  -> $\lceil$
    s#\\\(rc#_Mrceil_D#g;                    # \(rc  -> $\rceil$

    s#\\\(bv#_Cmbox_Cboldmath_Mmid_D_R_R#g;  # \(bv  -> {\mbox{\boldmath$\mid$}}
    s#\\\(bx#_Cvrule width.5em height.6em depth-.1em_R#g;
    s#\\\(cf#^#g;                            # \(cf  -> ^
    s#\\\(al#_Maleph_D#g;                    # \(al  -> $\aleph$
    s#\\\(If#_MIm_D#g;                       # \(If  -> $\Im$
    s#\\\(Rf#_MRe_D#g;                       # \(Rf  -> $\Re$
    s#\\\(ws#_Mwp_D#g;                       # \(ws  -> $\wp$
    s#\\\(mt#_E_Bprime_D#g;                  # \(mt  -> ${}^\prime$
    s#\\\(sd#_E_L_Bprime_B!_Bprime_R_D#g;    # \(sd  -> ${}^{\prime\!\prime}$
    s#\\\(pa#_BP#g;                          # \(pa  -> \P
    s#\\\(Cc#_Mclubsuit_D#g;                 # \(Cc  -> $\clubsuit$
    s#\\\(Cd#_Mdiamondsuit_D#g;              # \(Cd  -> $\diamondsuit$
    s#\\\(Ch#_Mheartsuit_D#g;                # \(Ch  -> $\heartsuit$
    s#\\\(Cs#_Mspadesuit_D#g;                # \(Cs  -> $\spadesuit$
    s#\\\(bt#_Mperp_D#g;                     # \(bt  -> $\perp$
    s#\\\(<:#_MLeftarrow_D#g;                # \(<:  -> $\Leftarrow$
    s#\\\(:>#_MRightarrow_D#g;               # \(:>  -> $\Rightarrow$
    s#\\\(io#_MLeftrightarrow_D#g;           # \(io  -> $\Leftrightarrow$
    s#\\\(u=#_MUparrow_D#g;                  # \(u=  -> $\Uparrow$
    s#\\\(d=#_MDownarrow_D#g;                # \(d=  -> $\Downarrow$
    s#\\\(r1#_Mrightleftharpoons_D#g;        # \(r1  -> $\rightleftharpoons$
    s#\\\(r2#_Mleftharpoondown_D#g;          # \(r2  -> $\leftharpoondown$
    s#\\\(cr#_Mhookleftarrow_D#g;            # \(cr  -> $\hookleftarrow$
    s#\\\(AL#_M_D#g;                # \(AL  ->
    s#\\\(DL#_M_D#g;                # \(DL  ->

    s#\\\(\*a#_Malpha_D#g;                   # \(*a  -> $\alpha$
    s#\\\(\*b#_Mbeta_D#g;                    # \(*b  -> $\beta$
    s#\\\(\*c#_Mxi_D#g;                      # \(*c  -> $\xi$
    s#\\\(\*d#_Mdelta_D#g;                   # \(*d  -> $\delta$
    s#\\\(\*e#_Mvarepsilon_D#g;              # \(*e  -> $\varepsilon$
    s#\\\(\*f#_Mphi_D#g;                     # \(*f  -> $\phi$
    s#\\\(\*g#_Mgamma_D#g;                   # \(*g  -> $\gamma$
    s#\\\(\*h#_Mtheta_D#g;                   # \(*h  -> $\theta$
    s#\\\(\*i#_Miota_D#g;                    # \(*i  -> $\iota$
    s#\\\(\*k#_Mkappa_D#g;                   # \(*k  -> $\kappa$
    s#\\\(\*l#_Mlambda_D#g;                  # \(*l  -> $\lambda$
    s#\\\(\*m#_Mmu_D#g;                      # \(*m  -> $\mu$
    s#\\\(\*n#_Mnu_D#g;                      # \(*n  -> $\nu$
    s#\\\(\*o#_Do_D#g;                       # \(*o  -> $o$
    s#\\\(\*p#_Mpi_D#g;                      # \(*p  -> $\pi$
    s#\\\(\*q#_Mpsi_D#g;                     # \(*q  -> $\psi$
    s#\\\(\*r#_Mrho_D#g;                     # \(*r  -> $\rho$
    s#\\\(\*s#_Msigma_D#g;                   # \(*s  -> $\sigma$
    s#\\\(\*t#_Mtau_D#g;                     # \(*t  -> $\tau$
    s#\\\(\*u#_Mupsilon_D#g;                 # \(*u  -> $\upsilon$
    s#\\\(\*w#_Momega_D#g;                   # \(*w  -> $\omega$
    s#\\\(\*x#_Mchi_D#g;                     # \(*x  -> $\chi$
    s#\\\(\*y#_Meta_D#g;                     # \(*y  -> $\eta$
    s#\\\(\*z#_Mzeta_D#g;                    # \(*z  -> $\zeta$
    s#\\\(ts#_Mvarsigma_D#g;                 # \(ts  -> $\varsigma$
    s#\\\(\*C#_MXi_D#g;                      # \(*C  -> $\Xi$
    s#\\\(\*D#_MDelta_D#g;                   # \(*D  -> $\Delta$
    s#\\\(\*F#_MPhi_D#g;                     # \(*F  -> $\Phi$
    s#\\\(\*G#_MGamma_D#g;                   # \(*G  -> $\Gamma$
    s#\\\(\*H#_MTheta_D#g;                   # \(*H  -> $\Theta$
    s#\\\(\*L#_MLambda_D#g;                  # \(*L  -> $\Lambda$
    s#\\\(\*P#_MPi_D#g;                      # \(*P  -> $\Pi$
    s#\\\(\*Q#_MPsi_D#g;                     # \(*Q  -> $\Psi$
    s#\\\(\*R#_Crm P_R#g;                    # \(*R  -> {\rm P}
    s#\\\(\*S#_MSigma_D#g;                   # \(*S  -> $\Sigma$
    s#\\\(\*U#_Crm Y_R#g;                    # \(*U  -> {\rm Y}
    s#\\\(\*W#_MOmega_D#g;                   # \(*W  -> $\Omega$
    s#\\\(\*Y#_Crm H_R#g;                    # \(*Y  -> {\rm H}
    s#\\\(\*(\w)#_Crm $1_R#g;                # \(*_  -> {\rm _}

    # from the Berkeley -ms macros

    s#\\\*\-#--#g;                           # \*-   -> --
    s#\\\*Q#``#g;                            # \*Q   -> ``
    s#\\\*U#''#g;                            # \*U   -> ''
    s#\\\*\(BU#_Mbullet_D#g;                 # \*(BU -> $\bullet$
    s#\\\*\(EM#--#g;                         # \*(EM -> --

    s#(\w)\\\*'#_C'$1_R#g;                   # e\*'  -> {\'e}
    s#(\w)\\\*\^#_C_A$1_R#g;                 # e\*^  -> {\^e}
    s#(\w)\\\*~#_C_T$1_R#g;                  # e\*~  -> {\~e}
    s#(\w)\\\*v#_Cv_L$1_R_R#g;               # e\*v  -> {\v{e}}
    s#(\w)\\\*_U#_C=$1_R#g;                  # e\*_  -> {\=e}
    s#([Oo])\\\*/#_C$1_R#g;                  # o\*/  -> {\o}
    s#(\w)\\\*`#_C`$1_R#g;                   # e\*`  -> {\`e}
    s#(\w)\\\*,#_Cc_L$1_R_R#g;               # e\*,  -> {\c{e}}
    s#(\w)\\\*:#_C"$1_R#g;                   # e\*:  -> {\"e}
    s#(\w)\\\*\.#_Cd_L$1_R_R#g;              # e\*.  -> {\d{e}}
    s#([Aa])\\\*o#_C$1$1_R#g;                # a\*o  -> {\aa}

    s#\\\*([!?])#$1`#g;                      # \*?   -> ?`
    s#\\\*8#_Css_R#g;                        # \*8   -> {\ss}
    s#\\\*3#_h_Blower.5ex_H3_R_R#g;          # \*3   -> \hbox{\lower.5ex 3}
    s#\\\*\(Th#_hI_Bhskip-.6ex_Braise.5ex_H_Mscriptscriptstyle_Bsupset_D_R_R#g;
    s#\\\*\(th#_h_Clower.3ex_H_Blarge l_R_R_Bhskip-.52ex o_R#g;
    s#\\\*D\-#_h_Booalign_L_LD_R_Bcrcr_Bhskip.2ex_Braise.25ex_H-_R_Bhfil_R_R#g;
    s#\\\*d\-#_h_Booalign_L_Mpartial_D_Bcrcr_Bhskip.55ex_Braise.7ex_H-_R_Bhfil_R_R#g;
    s#\\\*\(([AO])e#_C$1E_R#g;               # \*(Ae -> {\AE}
    s#\\\*\(([ao])e#_C$1e_R#g;               # \*(ae -> {\ae}
    s#\\\*q#_Cc_Lo_R_R#g;                    # \*q   -> {\c{o}}

    # from the -mm macros

    s#\\\*\(Tm#_E_Crm_Buppercase_LTM_R_R_D#g;# \*(Tm -> ${}^{\rm\uppercase{TM}}$

    # International (Roman-8) symbols

    s#\\\(\.\.#_C"_B _R#g;                   # \(..  -> {\"\ }
    s#\\\(([AEIOUYaeouy]):#_C"$1_R#g;        # \(A:  -> {\"A}
    s#\\\(([AEIOUaeouy])'#_C'$1_R#g;         # \(A'  -> {\'A}
    s#\\\(([AEIOUaeouy])`#_C`$1_R#g;         # \(A`  -> {\`A}
    s#\\\(([AEIOUaeouy])\^#_C_A$1_R#g;       # \(A^  -> {\^A}
    s#\\\(i:#_C"_Bi_R#g;                     # \(i:  -> {\"\i}
    s#\\\(i(['`])#_C$1_Bi_R#g;               # \(i'  -> {\'\i}
    s#\\\(i\^#_C_A_Bi_R#g;                   # \(i^  -> {\^\i}
    s#\\\(([ANOano])~#_C_T$1_R#g;            # \(A~  -> {\~A}
    s#\\\(([Cc]),#_Cc_L$1_R_R#g;             # \(c,  -> {\c{c}}
    s#\\\(([Ss])v#_Cv_L$1_R_R#g;             # \(sv  -> {\v{s}}
    s#\\\(([Oo])/#_C$1_R#g;                  # \(O/  -> {\O}
    s#\\\(ss#_Css_R#g;                       # \(ss  -> {\ss}
    s#\\\(L\-#_Cpounds_R#g;                  # \(L-  -> {\pounds}
    s#\\\(L=#_Cpounds_R#g;                   # \(L=  -> {\pounds}   # (Wrong!)
    s#\\\(Y=#_h_Brm_Brlap=Y_R#g;             # \(Y=  -> \hbox{\rm\rlap=Y}
    s#\\\(I([!?])#$1`#g;                     # \I!   -> !`
    s#\\\((AE|ae|OE|oe)#_C$1_R#g;            # \(AE  -> {\AE}
    s#\\\(([Aa])o#_C$1$1_R#g;                # \(Ao  -> {\AA}
    s#\\\(TH#_hI_Bhskip-.6ex_Braise.5ex_H_Mscriptscriptstyle_Bsupset_D_R_R#g;
    s#\\\(th#_h_Clower.3ex_H_Blarge l_R_R_Bhskip-.52ex o_R#g;
    s#\\\(D\-#_h_Booalign_L_LD_R_Bcrcr_Bhskip.2ex_Braise.25ex_H-_R_Bhfil_R_R#g;
    s#\\\(d\-#_h_Booalign_L_Mpartial_D_Bcrcr_Bhskip.55ex_Braise.7ex_H-_R_Bhfil_R_R#g;
    s#\\\(([ao])_U#_E_Cb_Cscriptsize $1_R_R_D#g;

    # This is really ugly, but it looks ok.  The "Scandinavian currency sign"
    # is made with a bold \circ rlap'ed with 8 .'s.  Big, long, and ugly,
    # but the result is not too bad.

    s#\\\(ox#_h_Booalign_Cmbox_Cboldmath_Mcirc_D_R_Bcrcr\n_Bhskip-.04ex_Braise.78ex_H._R_Bhfil_Bcrcr_Bhskip-.04ex_Braise.08ex_H._R_Bhfil_Bcrcr\n_c#g;
    s#_c#_Bhskip.7ex_Braise.78ex_H._R_Bhfil_Bcrcr_Bhskip.7ex_Braise.08ex_H._R_Bhfil_Bcrcr\n_c#g;
    s#_c#_Bhskip-.14ex_Braise.89ex_H._R_Bhfil_Bcrcr_Bhskip-.14ex_Braise-.02ex_H._R_Bhfil_Bcrcr\n_c#g;
    s#_c#_Bhskip.8ex_Braise.89ex_H._R_Bhfil_Bcrcr_Bhskip.8ex_Braise-.02ex_H._R_Bhfil_Bcrcr_R_R#g;

    # All the symbols from groff chars.tr that aren't listed above
    #   What the heck is this??  not only is a\*: an a umlaut, but so is
    #   \(a: and also \(:a !  God, I wish troff would get it together!
    #   \(ao is Ao in Roman-8, and an  o in groff.
    #   \(Cs is Cards Spades in Roman-8 and Currency Scandanavian in groff.
    #   How do I know which they meant??

    s#\\\(bs##g;                             # \(bs  -> (not implemented)
    s#\\\(%0#_h%_Bhskip-.16ex_Blower.15ex_H_Bscriptsize 0_R_R#g;
    s#\\\(f/#/#g;                            # \(f/  -> /
    s#\\\(ha#_h_Braise.3em_H_Mscriptstyle_Bwedge_D_R_R#g;
    s#\\\(ti#_Msim_D#g;                      # \(ti  -> $\sim$
    s#\\\(\-D#_h_Booalign_L_LD_R_Bcrcr_Bhskip.2ex_Braise.25ex_H-_R_Bhfil_R_R#g;
    s#\\\(Sd#_h_Booalign_L_Mpartial_D_Bcrcr_Bhskip.8ex_Braise.7ex_H-_R_Bhfil_R_R#g;
    s#\\\(TP#_hI_Bhskip-.6ex_Braise.5ex_H_Mscriptscriptstyle_Bsupset_D_R_R#g;
    s#\\\(Tp#_h_Clower.3ex_H_Blarge l_R_R_Bhskip-.52ex o_R#g;
    s#\\\(IJ#_LI_Bhskip-.2ex J_R#g;          # \(IJ  -> {I\hskip-.2ex J}
    s#\\\(ij#_Li_Bhskip-.2ex j_R#g;          # \(ij  -> {i\hskip-.2ex j}
    s#\\\('([ACEIOUaceou])#_C'$1_R#g;        # \('A  -> {\'A}
    s#\\\(:([AEIOUYaeouy])#_C"$1_R#g;        # \(:A  -> {\"A}
    s#\\\(\^([AEIOUaeou])#_C_A$1_R#g;        # \(^A  -> {\^A}
    s#\\\(`([AEIOUaeou])#_C`$1_R#g;          # \(`A  -> {\`A}
    s#\\\((['`])i#_C$1_Bi_R#g;               # \('i  -> {\'\i}
    s#\\\(\^i#_C_A_Bi_R#g;                   # \(^i  -> {\^\i}
    s#\\\(:i#_C"_Bi_R#g;                     # \(:i  -> {\"\i}
    s#\\\(~([ANOano])#_C_T$1_R#g;            # \(~A  -> {\~A}
    s#\\\(v([CcSsZz])#_Cv_L$1_R_R#g;         # \(vs  -> {\v{s}}
    s#\\\(,([Cc])#_Cc_L$1_R_R#g;             # \(,c  -> {\c{c}}
    s#\\\(/([OoLl])#_C$1_R#g;                # \(/O  -> {\O}
    s#\\\(o([Aa])#_C$1$1_R#g;                # \(oA  -> {\AA}
    s#\\\(a"#_CH_L _R_R#g;                   # \(a"  -> {\H{ }}
    s#\\\(a\-#_C=_L _R_R#g;                  # \(a-  -> {\={ }}
    s#\\\(a\.#_C._L _R_R#g;                  # \(a.  -> {\.{ }}
    s#\\\(a\^#_C_A_L _R_R#g;                 # \(a^  -> {\^{ }}
    s#\\\(ab#_Cu_L _R_R#g;                   # \(ab  -> {\u{ }}
    s#\\\(ac#_Cc_L _R_R#g;                   # \(ac  -> {\c{ }}
    s#\\\(ad#_C"_L _R_R#g;                   # \(ad  -> {\"{ }}
    s#\\\(ah#_Cv_L _R_R#g;                   # \(ah  -> {\v{ }}
    s#\\\(a~#_C_T_L _R_R#g;                  # \(a~  -> {\~{ }}
    s#\\\(ho#_Cc_L _R_R#g;                   # \(ho  -> {\c{ }}  # (wrong!)
    s#\\\(\.([ij])#_C$1_R#g;                 # \(.i  -> {\i}
    s#\\\(Do#$#g;                            # \(Do  -> $
    s#\\\(Po#_Cpounds_R#g;                   # \(Po  -> {\pounds}
    s#\\\(Ye#_h_Brm_Brlap=Y_R#g;             # \(Ye  -> \hbox{\rm\rlap=Y}
    s#\\\(Fo#_Mscriptscriptstyle_Bll_D#g;    # \(Fo  -> $\scriptscriptstyle\ll$
    s#\\\(Fc#_Mscriptscriptstyle_Bgg_D#g;    # \(Fc  -> $\scriptscriptstyle\gg$
    s#\\\(fo#_Mscriptscriptstyle_l_D#g;      # \(fo  -> $\scriptscriptstyle<$
    s#\\\(fc#_Mscriptscriptstyle_g_D#g;      # \(fc  -> $\scriptscriptstyle>$
    s#\\\(r([!?])#$1`#g;                     # \(r!  -> !`
    s#\\\(OK#_Cmbox_Cboldmath_Msurd_D_R_R#g; # \(OK  ->{\mbox{\boldmath$\surd$}}
    s#\\\(Of#_E_Cb_Cscriptsize a_R_R_D#g;    # \(Of  -> ${}^{\scriptsize a}}$
    s#\\\(Om#_E_Cb_Cscriptsize o_R_R_D#g;    # \(Om  -> ${}^{\scriptsize o}}$
    s#\\\(S(\d)#_E$1_D#g;                    # \(S1  -> ${}^1$
    s#\\\(lA#_MLeftarrow_D#g;                # \(lA  -> $\Leftarrow$
    s#\\\(rA#_MRightarrow_D#g;               # \(rA  -> $\Rightarrow$
    s#\\\(hA#_MLeftrightarrow_D#g;           # \(hA  -> $\Leftrightarrow$
    s#\\\(dA#_MDownarrow_D#g;                # \(dA  -> $\Downarrow$
    s#\\\(uA#_MUparrow_D#g;                  # \(uA  -> $\Uparrow$
    s#\\\(vA#_MUpdownarrow_D#g;              # \(vA  -> $\Updownarrow$
    s#\\\(va#_Mupdownarrow_D#g;              # \(va  -> $\updownarrow$
    s#\\\(ba#_Chskip.4ex_Bvrule width.2ex height1.7ex depth0ex_R#g;
    s#\\\(bb#_h_Bhskip.4ex_H_Booalign_Cvrule width.2ex height.5ex depth.4ex_Bcrcr_Bhfil_Braise.8ex_H_Bvrule width.2ex height.9ex depth0ex_R_Bhfil_R_R_R#g;
    s#\\\(tm#_E_Crm_Buppercase_LTM_R_R_D#g;  # \(tm  -> ${}^{\rm\uppercase{TM}}$
    s#\\\(ps#_BP#g;                          # \(ps  -> \P
    s#\\\(en#-#g;                            # \(en  -> -
    s#\\\(lB#_L_R[#g;                        # \(lB  -> {}[
    s#\\\(rB#]#g;                            # \(rB  -> ]
    s#\\\(lC#{#g;                            # \(lC  -> {
    s#\\\(rC#}#g;                            # \(rC  -> }
    s#\\\(la#_Mlangle_D#g;                   # \(la  -> $\langle$
    s#\\\(ra#_Mrangle_D#g;                   # \(ra  -> $\rangle$
    s#\\\(lq#``#g;                           # \(lq  -> ``
    s#\\\(rq#''#g;                           # \(rq  -> ''
    s#\\\(oq#`#g;                            # \(oq  -> `
    s#\\\(at#@#g;                            # \(at  -> @
    s#\\\(sh#\##g;                           # \(sh  -> #
    s#\\\(rs#_I#g;                           # \(rs  -> $\backslash$
    s#\\\(3d#_D_H._R_Braise.9ex_H._R_H._R_D#g;# \(3d -> .:.
    s#\\\(~~#_Mapprox_D#g;                   # \(~~  -> $\approx$
    s#\\\(!=#_Mneq_D#g;                      # \(!=  -> $\neq$
    s#\\\(=~#_Mcong_D#g;                     # \(=~  -> $\cong$
    s#\\\(AN#_Mwedge_D#g;                    # \(AN  -> $\wedge$
    s#\\\(OR#_Mvee_D#g;                      # \(OR  -> $\vee$
    s#\\\(Ah#_Maleph_D#g;                    # \(Ah  -> $\aleph$
    s#\\\(Im#_MIm_D#g;                       # \(Im  -> $\Im$
    s#\\\(Re#_MRe_D#g;                       # \(Re  -> $\Re$
    s#\\\(md#_Mcdot_D#g;                     # \(md  -> $\cdot$
    s#\\\(nm#_Mnotin_D#g;                    # \(nm  -> $\notin$
    s#\\\(pp#_Mperp_D#g;                     # \(pp  -> $\perp$
    s#\\\(c\*#_Motimes_D#g;                  # \(c*  -> $\otimes$
    s#\\\(c\+#_Moplus_D#g;                   # \(c+  -> $\oplus$
    s#\\\(\-h#_Mhbar_D#g;                    # \(-h  -> $\hbar$
    s#\\\(CL#_Mclubsuit_D#g;                 # \(CL  -> $\clubsuit$
    s#\\\(SP#_Mspadesuit_D#g;                # \(SP  -> $\spadesuit$
    s#\\\(HE#_Mheartsuit_D#g;                # \(HE  -> $\heartsuit$
    s#\\\(DI#_Mdiamondsuit_D#g;              # \(DI  -> $\diamondsuit$
    s#\\\(CR#_Mhookleftarrow_D#g;            # \(CR  -> $\hookleftarrow$
    s#\\\(st#_Mni_D#g;                       # \(st  -> $\ni$
    s#\\\(/_U#_Mangle_D#g;                   # \(/_  -> $\angle$
    s#\\\(\-\+#_Mmp_D#g;                     # \(-+  -> $\mp$
    s#\\\(nc#_Mnot_Bsupset_D#g;              # \(nc  -> $\not\supset$
    s#\\\(ne#_Mnot_Bequiv_D#g;               # \(ne  -> $\not\equiv$


    # misc

    s#\\d([^\\]*)\\u#_Braisebox_L.5em_R_L$1_R#g;
    s#\\u([^\\]*)\\d#_Braisebox_L-.5em_R_L$1_R#g;
    s#\\z(.)#_Brlap_L$1_R#g;                 # \z|_   -> L

    s#\\&##g;                                # \&     ->

  }   # done with troff special chars

  # protect TeX characters
  s/\\/_I/g;
  s/#/\\#/g;
  s/\$/\\$/g;
  s/%/\\%/g;
  s/&/\\&/g;
  s/{/\\{/g;
  s/}/\\}/g;
  s/\|/$|$/g;
  s/</\$<$/g;
  s/>/\$>$/g;
  s/\^/\\^{}/g;
  s/~/\\~{}/g;

  # now convert our escaped characters back to their real selves
  s/_B/\\/g;
  s/_I/\$\\backslash$/g;
  s/_C/{\\/g;
  s/_L/{/g;
  s/_R/}/g;
  s/_l/</g;
  s/_g/>/g;
  s/_T/~/g;
  s/_A/^/g;
  s/_D/$/g;
  s/_M/\$\\/g;
  s/_V/|/g;
  s/_E/\${}^/g;
  s/_H/\\hbox{/g;
  s/_h/\\leavevmode\\hbox{/g;
  s/_U/\\_/g;
  s/\n\n/\\par\n/g;           # this is for fields that want paragraphs
  return $_;
}
