home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / lyx21041.zip / XFree86 / lib / X11 / lyx / reLyX / BasicLyX.pm (.txt) next >
LaTeX Document  |  1999-04-26  |  55KB  |  1,239 lines

  1. # This file is part of reLyX
  2. # Copyright (c) 1998-9 Amir Karger karger@post.harvard.edu
  3. # You are free to use and modify this code under the terms of
  4. # the GNU General Public Licence version 2 or later.
  5. package BasicLyX;
  6. # This package includes subroutines to translate "clean" LaTeX to LyX.
  7. # It translates only "basic" stuff, which means it doesn't do class-specific
  8. #     things. (It uses the TeX parser Text::TeX)
  9. use strict;
  10. use RelyxTable; # Handle LaTeX tables
  11. use RelyxFigure; # Handle LaTeX figures
  12. use Verbatim;   # Copy stuff verbatim
  13. use vars qw($bibstyle_insert_string $bibstyle_file $Begin_Inset_Include);
  14. $bibstyle_insert_string = "%%%%%Insert bibliographystyle file here!";
  15. $bibstyle_file = "";
  16. $Begin_Inset_Include = "\\begin_inset Include";
  17. #################### PACKAGE-WIDE VARIABLES ###########################
  18. my $debug_on; # is debugging on?
  19. ######
  20. #Next text starts a new paragraph?
  21. # $INP = 0 for plain text not starting a new paragraph
  22. # $INP = 1 for text starting a new paragraph, so that we write a new
  23. #    \layout command and renew any font changes for the new paragraph
  24. # Starts as 1 cuz if the first text in the document is plain text
  25. #    (not, e.g., in a \title command) it will start a new paragraph
  26. my $IsNewParagraph = 1;
  27. my $OldINP; #Save $IsNewParagraph during footnotes
  28. # Some layouts may have no actual text in them before the next layout
  29. # (e.g. slides). Pending Layout is set when we read a command that puts us
  30. # in a new layout. If we get some regular text to print out, set it to false.
  31. # But if we get to another layout command, first print out the command to
  32. # start the pending layout.
  33. my $PendingLayout = 0;
  34. # HACK to protect spaces within optional argument to \item
  35. my $protect_spaces = 0;
  36. # $MBD = 1 if we're in a list, but haven't seen an '\item' command
  37. #    In that case, we may need to start a nested "Standard" paragraph
  38. my $MayBeDeeper = 0;
  39. my $OldMBD; #Save $MBD during footnotes -- this should very rarely be necessary!
  40. # Stack to take care of environments like Enumerate, Quote
  41. # We need a separate stack for footnotes, which have separate layouts etc.
  42. # Therefore we use a reference to an array, not just an array
  43. my @LayoutStack = ("Standard"); #default if everything else pops off
  44. my $CurrentLayoutStack = \@LayoutStack;
  45. # Status of various font commands
  46. # Every font characteristic (family, series, etc.) needs a stack, because
  47. #    there may be nested font commands, like \textsf{blah \texttt{blah} blah}
  48. # CurrentFontStatus usually points to the main %FontStatus hash, but
  49. #     when we're in a footnote, it will point to a temporary hash
  50. my %FontStatus = (
  51.     '\emph' => ["default"],
  52.     '\family' => ["default"],
  53.     '\series' => ["default"],
  54.     '\shape' => ["default"],
  55.     '\bar' => ["default"],
  56.     '\size' => ["default"],
  57.     '\noun' => ["default"],
  58. my $CurrentFontStatus = \%FontStatus;
  59. # Currently aligning paragraphs right, left, or center?
  60. my $CurrentAlignment = "";
  61. my $OldAlignment; # Save $AS during footnotes
  62. # Global variables for copying tex stuff
  63. my $tex_mode_string; # string we accumulate tex mode stuff in
  64. my @tex_mode_tokens; # stack of tokens which required calling copy_latex_known
  65. # LyX strings to start and end TeX mode
  66. my $start_tex_mode = "\n\\latex latex \n";
  67. my $end_tex_mode = "\n\\latex default \n";
  68. # String to write before each item
  69. my $item_preface = "";
  70. #############  INFORMATION ABOUT LATEX AND LYX   #############################
  71. # LyX translations of LaTeX font commands
  72. my %FontTransTable = (
  73.    # Font commands
  74.    '\emph' => "\n\\emph on \n",
  75.    '\underline' => "\n\\bar under \n",
  76.    '\underbar' => "\n\\bar under \n", # plain tex equivalent of underline?
  77.    '\textbf' => "\n\\series bold \n",
  78.    '\textmd' => "\n\\series medium \n",
  79.    '\textsf' => "\n\\family sans \n",
  80.    '\texttt' => "\n\\family typewriter \n",
  81.    '\textrm' => "\n\\family roman \n",
  82.    '\textsc' => "\n\\shape smallcaps \n",
  83.    '\textsl' => "\n\\shape slanted \n",
  84.    '\textit' => "\n\\shape italic \n",
  85.    '\textup' => "\n\\shape up \n",
  86.    '\noun' => "\n\\noun on \n", # LyX abstraction of smallcaps
  87. # Font size commands
  88.    '\tiny' => "\n\\size tiny \n",
  89.    '\scriptsize' => "\n\\size scriptsize \n",
  90.    '\footnotesize' => "\n\\size footnotesize \n",
  91.    '\small' => "\n\\size small \n",
  92.    '\normalsize' => "\n\\size default \n",
  93.    '\large' => "\n\\size large \n",
  94.    '\Large' => "\n\\size Large \n",
  95.    '\LARGE' => "\n\\size LARGE \n",
  96.    '\huge' => "\n\\size huge \n",
  97.    '\Huge' => "\n\\size Huge \n",
  98.    # This doesn't work yet!
  99.    #'\textnormal' => "\n\\series medium \n\\family roman \n\\shape up \n",
  100. # Things LyX implements as "Floats"
  101. my %FloatTransTable = (
  102.    # Footnote, Margin note
  103.    '\footnote' => "\n\\begin_float footnote \n",
  104.    '\thanks' => "\n\\begin_float footnote \n", # thanks is same as footnote
  105.    '\marginpar' => "\n\\begin_float margin \n",
  106. # Environments that LyX implements as "floats"
  107. my %FloatEnvTransTable = (
  108.     "table" => "\n\\begin_float tab \n",
  109.     "table*" => "\n\\begin_float wide-tab \n",
  110.     "figure" => "\n\\begin_float fig \n",
  111.     "figure*" => "\n\\begin_float wide-fig \n",
  112. # Simple LaTeX tokens which are turned into small pieces of LyX text
  113. my %TextTokenTransTable = (
  114.     # LaTeX escaped characters
  115.     '\_' => '_',
  116.     '\%' => '%',
  117.     '\$' => '$',
  118.     '\&' => '&',
  119.     '\{' => '{',
  120.     '\}' => '}',
  121.     '\#' => '#',
  122.     '\~' => '~',
  123.     '\^' => '^',
  124.     # \i and \j are used in accents. LyX doesn't recognize them in plain
  125.     #    text. Hopefully, this isn't a problem.
  126.     '\i' => '\i',
  127.     '\j' => '\j',
  128.     # Misc simple LaTeX tokens
  129.     '~'      => "\n\\protected_separator \n",
  130.     '@'      => "@", # TeX.pm considers this a token, but it's not really
  131.     '\@'     => "\\SpecialChar \\@",
  132.     '\ldots' => "\n\\SpecialChar \\ldots\{\}\n",
  133.     '\-'     => "\\SpecialChar \\-\n",
  134.     '\LaTeX' => "LaTeX",
  135.     '\LaTeXe' => "LaTeX2e",
  136.     '\TeX'    => "TeX",
  137.     '\LyX'    => "LyX",
  138.     '\hfill'  => "\n\\hfill \n",
  139.     '\noindent'        => "\n\\noindent \n",
  140.     '\textbackslash'   => "\n\\backslash \n",
  141.     '\textgreater'     => ">",
  142.     '\textless'        => "<",
  143.     '\textbar'         => "|",
  144. # LyX translations of some plain LaTeX text (TeX parser won't recognize it
  145. #     as a Token, so we need to translate the Text::TeX::Text token.)
  146. my %TextTransTable = (
  147.     # Double quotes
  148.     "``" => "\n\\begin_inset Quotes eld\n\\end_inset \n\n",
  149.     "''" => "\n\\begin_inset Quotes erd\n\\end_inset \n\n",
  150.     # Tokens that don't start with a backslash, so parser won't recognize them
  151.     # (LyX doesn't support them, so we just print them in TeX mode)
  152.     '?`' => "$start_tex_mode?`$end_tex_mode",
  153.     '!`' => "$start_tex_mode!`$end_tex_mode",
  154. # Things that LyX translates as "LatexCommand"s
  155. # E.g., \ref{foo} ->'\begin_inset LatexCommand \ref{foo}\n\n\end_inset \n'
  156. # (Some take arguments, others don't)
  157. my @LatexCommands = map {"\\$_"} qw(ref pageref label cite bibliography
  158.                              index printindex tableofcontents
  159.                                  listofalgorithms listoftables listoffigures);
  160. my @IncludeCommands = map {"\\$_"} qw(input include);
  161. # Included postscript files
  162. # LyX 1.0 can't do \includegraphics*!
  163. my @GraphicsCommands = map {"\\$_"} qw(epsf epsffile epsfbox
  164.                                        psfig epsfig includegraphics);
  165. # Accents. Most of these take an argument -- the thing to accent
  166. # (\l and \L are handled as InsetLatexAccents, so they go here too)
  167. my $AccentTokens = "\\\\[`'^#~=.bcdHklLrtuv\"]";
  168. # Environments which describe justifying (converted to LyX \align commands)
  169. #    and the corresponding LyX commands
  170. my %AlignEnvironments = (
  171.     "center" => "\n\\align center \n",
  172.     "flushright" => "\n\\align right \n",
  173.     "flushleft" => "\n\\align left \n",
  174. # Some environments' begin commands take an extra argument
  175. # Print string followed by arg for each item in the list, or ignore the arg ("")
  176. my %ExtraArgEnvironments = (
  177.     "thebibliography" => "",
  178.     "lyxlist" =>'\labelwidthstring ',
  179.     "labeling" =>'\labelwidthstring ', # koma script list
  180. # Math environments are copied verbatim
  181. my $MathEnvironments = "(math|equation|displaymath|eqnarray(\\*)?)";
  182. # ListLayouts may have standard paragraphs nested inside them.
  183. my $ListLayouts = "Itemize|Enumerate|Description";
  184. #####################   PARSER INVOCATION   ##################################
  185. sub call_parser {
  186. # This subroutine calls the TeX parser & translator
  187. # Before it does that, it does lots of setup work to get ready for parsing.
  188. # Arg0 is the file to read (clean) LaTeX from
  189. # Arg1 is the file to write LyX to
  190. # Arg2 is the file to read layouts from (e.g., in LYX_DIR/layouts/foo.layout)
  191.     my ($InFileName, $OutFileName) = (shift,shift);
  192. # Before anything else, set the package-wide variables based on the
  193. #    user-given flags
  194.     # opt_d is set to 1 if '-d' option given, else (probably) undefined
  195.     $debug_on = (defined($main::opt_d) && $main::opt_d);
  196.     # Hash of tokens passed to the TeX parser
  197.     # Many values are $Text::TeX::Tokens{'\operatorname'}, which has
  198.     #    Type=>report_args and count=>1
  199.     # Note that we don't have to bother putting in tokens which will be simply
  200.     #    translated (e.g., from %TextTokenTransTable).
  201.     my %MyTokens = ( 
  202.     '{' => $Text::TeX::Tokens{'{'},
  203.     '}' => $Text::TeX::Tokens{'}'},
  204.     '\begin' => $Text::TeX::Tokens{'\begin'},
  205.     '\end' => $Text::TeX::Tokens{'\end'},
  206.     # Lots of other commands will be made by ReadCommands:Merge
  207.     # by reading the LaTeX syntax file
  208.     # Font sizing commands (local)
  209.     '\tiny' => {Type => 'local'},
  210.     '\small' => {Type => 'local'},
  211.     '\scriptsize' => {Type => 'local'},
  212.     '\footnotesize' => {Type => 'local'},
  213.     '\small' => {Type => 'local'},
  214.     '\normalsize' => {Type => 'local'},
  215.     '\large' => {Type => 'local'},
  216.     '\Large' => {Type => 'local'},
  217.     '\LARGE' => {Type => 'local'},
  218.     '\huge' => {Type => 'local'},
  219.     '\Huge' => {Type => 'local'},
  220.     # Tokens to ignore (which make a new paragraph)
  221.     # Just pretend they actually ARE new paragraph markers!
  222.     '\maketitle' => {'class' => 'Text::TeX::Paragraph'},
  223.     );
  224.     # Now add to MyTokens all of the commands that were read from the
  225.     #    commands file by package ReadCommands
  226.     &ReadCommands::Merge(\%MyTokens);
  227. # Here's the actual subroutine. The above is all preparation
  228.     # Output LyX file
  229.     my $zzz = $debug_on ? " ($InFileName --> $OutFileName)\n" : "... ";
  230.     print STDERR "Translating$zzz";
  231.     open (OUTFILE,">$OutFileName");
  232.     # Open the file to turn into LyX.
  233.     my $infile = new Text::TeX::OpenFile $InFileName,
  234.     'defaultact' => \&basic_lyx,
  235.     'tokens' => \%MyTokens;
  236.     # Process what's left of the file (everything from \begin{document})
  237.     $infile->process;
  238.     # Last line of the LyX file
  239.     print OUTFILE "\n\\the_end\n";
  240.     close OUTFILE;
  241.     #warn "Done with basic translation\n";
  242.     return;
  243. } # end subroutine call_parser
  244. ##########################   MAIN TRANSLATOR SUBROUTINE   #####################
  245. sub basic_lyx {
  246. # This subroutine is called by Text::TeX::process each time subroutine
  247. #     eat returns a value.
  248. # Argument 0 is the return value from subroutine eat
  249. # Argument 1 is the Text::TeX::OpenFile (namely, $TeXfile)
  250.     my $eaten = shift;
  251.     my $fileobject = shift;
  252.     # This handles most but maybe not all comments
  253.     # THere shouldn't be any if we've run CleanTeX.pl
  254.     print "Comment: ",$eaten->comment if defined $eaten->comment && $debug_on;
  255.     my $type = ref($eaten);
  256.     print "$type " if $debug_on;
  257.     # This loop is basically just a switch. However, making it a for
  258.     #    (1) makes $_ = $type (convenient)
  259.     #    (2) allows us to use things like next and last
  260.     TYPESW: for ($type) {
  261.         # some pre-case work
  262.         s/^Text::TeX:://o or die "unknown token?!";
  263.     my ($dummy, $tok);
  264.     my ($thistable);
  265.     # The parser puts whitespace before certain kinds of tokens along
  266.     # with that token. If that happens, save a space
  267.     my $pre_space = ""; # whitespace before a token
  268.     if (/BegArgsToken|^Token|::Group$/) {
  269.         $dummy = $eaten->exact_print;
  270.         # Only set prespace if we match something
  271.         #    We wouldn't want it to be more than one space, since that's
  272.         # illegal in LyX. Also, replace \t or \n with ' ' since they are
  273.         # ignored in LyX. Hopefully, this won't lead to anything worse
  274.         # than some lines with >80 chars
  275.         #    Finally, don't put a space at the beginning of a new paragraph
  276.         if (($dummy =~ /^\s+/) && !$IsNewParagraph) {
  277.         $pre_space = " ";
  278.         }
  279.         # Handle blank lines.
  280.         if (m/^Paragraph$/o) {
  281.         # $INP <>0 means We will need a new layout command
  282.         $IsNewParagraph = 1;
  283.         # $MBD means start a begin_deeper within list environments
  284.         #    unless there's an \item command
  285.         $MayBeDeeper = 1;
  286.             last TYPESW;
  287.         }
  288.     # If, e.g., there's just a comment in this token, don't do anything
  289.     # This actually shouldn't happen if CleanTeX has already removed them
  290.     last TYPESW if !defined $eaten->print;
  291.         
  292.         # Handle LaTeX tokens
  293.         if (/^Token$/o) {
  294.         my $name = $eaten->token_name; # name of the token, e.g., "\large"
  295.         print "'$name' " if $debug_on;
  296.         # Tokens which turn into a bit of LyX text
  297.         if (exists $TextTokenTransTable{$name}) {
  298.             &CheckForNewParagraph; #Start new paragraph if necessary
  299.         my $to_print = $TextTokenTransTable{$name};
  300.         # \@ has to be specially handled, because it depends on
  301.         # the character AFTER the \@
  302.         if ($name eq '\@') {
  303.             my $next = $fileobject->eatGroup(1);
  304.             my $ch="";
  305.             $ch = $next->print or warn "\@ confused me!\n";
  306.             if ($ch eq '.') {
  307.             # Note: \@ CAN'T have pre_space before it
  308.             print OUTFILE "$to_print$ch\n";
  309.             print "followed by $ch" if $debug_on;
  310.             } else {
  311.                warn "LyX (or LaTeX) can't handle '$ch' after $name\n";
  312.             print OUTFILE $ch;
  313.             }
  314.         } else { # it's not \@
  315.             # Print the translated text (include preceding whitespace)
  316.             print OUTFILE "$pre_space$to_print";
  317.         } # end special handling for \@
  318.         # Handle tokens that LyX translates as a "LatexCommand" inset
  319.         } elsif (grep {$_ eq $name} @LatexCommands) {
  320.         &CheckForNewParagraph; #Start new paragraph if necessary
  321.             print OUTFILE "$pre_space\n\\begin_inset LatexCommand ",
  322.                        $name,
  323.                   "\n\n\\end_inset \n\n";
  324.         # Math -- copy verbatim until you're done
  325.         } elsif ($name eq '\(' || $name eq '\[') {
  326.         print "\nCopying math beginning with '$name'\n" if $debug_on;
  327.         # copy everything until end text
  328.         $dummy = &Verbatim::copy_verbatim($fileobject, $eaten);
  329.         $dummy = &fixmath($dummy); # convert '\sp' to '^', etc.
  330.         &CheckForNewParagraph; # math could be first thing in a par
  331.         print OUTFILE "$pre_space\n\\begin_inset Formula $name ";
  332.         print $dummy if $debug_on;
  333.         print OUTFILE $dummy;
  334.         } elsif ($name eq '\)' || $name eq '\]') {
  335.             # end math
  336.         print OUTFILE "$name\n\\end_inset \n\n";
  337.         print "\nDone copying math ending with '$name'" if $debug_on;
  338.         # Items in list environments
  339.         } elsif ($name eq '\item') {
  340.         # What if we had a nested "Standard" paragraph?
  341.         # Then write \end_deeper to finish the standard layout
  342.         #     before we write the new \layout ListEnv command
  343.         if ($$CurrentLayoutStack[-1] eq "Standard") {
  344.             pop (@$CurrentLayoutStack); # take "Standard" off the stack
  345.             print OUTFILE "\n\\end_deeper ";
  346.             print "\nCurrent Layout Stack: @$CurrentLayoutStack"
  347.                   if $debug_on;
  348.         } # end deeper if
  349.         # Upcoming text (the item) will be a new paragraph, 
  350.         #    requiring a new layout command based on whichever
  351.         #    kind of list environment we're in
  352.         $IsNewParagraph = 1;
  353.         # But since we had an \item command, DON'T nest a
  354.         #    deeper "Standard" paragraph in the list
  355.         $MayBeDeeper = 0;
  356.         # Check for an optional argument to \item
  357.         # If so, within the [] we need to protect spaces
  358.         # TODO: In fact, for description, if there's no [] or
  359.         # there's an empty [], then we need to write a ~, since LyX
  360.         # will otherwise make the next word the label
  361.         # If it's NOT a description & has a [] then we're stuck!
  362.         # They need to fix the bullets w/in lyx!
  363.         if (($dummy = $fileobject->lookAheadToken) &&
  364.             ($dummy =~ /\s*\[/)) {
  365.             $fileobject->eatFixedString('\['); # eat the [
  366.             $protect_spaces = 1;
  367.         # Special lists (e.g. List layout) have to print something
  368.         # before each item. In that case, CFNP and print it
  369.         if ($item_preface) {
  370.             &CheckForNewParagraph;
  371.             print OUTFILE $item_preface;
  372.         # Font sizing commands
  373.         # (Other font commands are TT::BegArgsTokens because they take
  374.         #     arguments. Font sizing commands are 'local' TT::Tokens)
  375.         } elsif (exists $FontTransTable{$name}) {
  376.         my $command = $FontTransTable{$name}; #e.g., '\size large'
  377.         if (! $IsNewParagraph) {
  378.             print OUTFILE "$pre_space$command";
  379.         } #otherwise, wait until we've printed the new paragraph command
  380.         # Store the current font change
  381.         ($dummy = $command) =~ s/\s*(\S+)\s+(\w+)\s*/$1/;
  382.         die "Font command error" if !exists $$CurrentFontStatus{$dummy};
  383.         push (@{$CurrentFontStatus->{$dummy}}, $2);
  384.         print "\nCurrent $dummy Stack: @{$CurrentFontStatus->{$dummy}}"
  385.               if $debug_on;
  386.         # Table stuff
  387.         } elsif ($name eq '&') {
  388.         if ($thistable = &RelyxTable::in_table) {
  389.             print OUTFILE "\n\\newline \n";
  390.             $thistable->nextcol;
  391.         } else {warn "& is illegal outside a table!"}
  392.         } elsif ($name eq '\\\\' || $name eq '\\newline') {
  393.         &CheckForNewParagraph; # could be at beginning of par?
  394.                 print OUTFILE "\n\\newline \n";
  395.         # If we're in a table, \\ means add a row to the table
  396.         # Note: if we're on the last row of the table, this extra
  397.         # row will get deleted later. This hack is necessary, because
  398.         # we don't know while reading when the last row is!
  399.         if ($thistable = &RelyxTable::in_table) {
  400.             $thistable->addrow;
  401.         } elsif ($name eq '\hline') {
  402.         if ($thistable = &RelyxTable::in_table) {
  403.             # hcline does hline if no arg is given
  404.             $thistable->hcline;
  405.         } else {warn "\\hline is illegal outside a table!"}
  406.         # Figures
  407.         } elsif ($name =~ /^\\epsf[xy]size$/) {
  408.         # We need to eat '=' followed by EITHER more text OR
  409.         # one (or more?!) macros describing a TeX size
  410.             my $arg = $fileobject->eatMultiToken;
  411.         my $length = $arg->print;
  412.         $length =~ s/^\s*=\s*// or warn "weird '$name' command!";
  413.         # If there's no "cm" or other letters in $length, the next token
  414.         # ought to be something like \textwidth. Then it will be empty
  415.         # or just have numbers in it.
  416.         # This is bugprone. Hopefully not too many people use epsf!
  417.         if ($length =~ /^[\d.]*\s*$/) {
  418.             my $next = $fileobject->eatMultiToken;
  419.             $length .= $next->print;
  420.         $length =~ s/\s*$//; # may have \n at end
  421.         # If we can't parse the command, print it in tex mode
  422.         &RelyxFigure::parse_epsfsize($name, $length) or 
  423.             &print_tex_mode("$name=$length");
  424.         # Miscellaneous...
  425.         } elsif ($name =~ /\\verb.*?/) {
  426.             my $dummy = &Verbatim::copy_verb($fileobject, $eaten);
  427.         print "\nCopying $name in TeX mode: " if $debug_on;
  428.         &print_tex_mode ($dummy);
  429.         # Otherwise it's an unknown token, which must be copied
  430.         #     in TeX mode, along with its arguments, if any
  431.         } else {
  432.         if (defined($eaten->relyx_args($fileobject))) {
  433.            ©_latex_known($eaten, $fileobject);
  434.         } else { # it's not in MyTokens
  435.             ©_latex_unknown($eaten, $fileobject);
  436.         }
  437.             last TYPESW;
  438.         }
  439.         
  440.     # Handle tokens that take arguments, like \section{},\section*{}
  441.     if (/^BegArgsToken$/) {
  442.         my $name = $eaten->token_name;
  443.         print "$name" if $debug_on;
  444.         # Handle things that LyX translates as a "LatexCommand" inset
  445.         if (grep {$_ eq $name} @LatexCommands) {
  446.         &CheckForNewParagraph; #Start new paragraph if necessary
  447.             print OUTFILE "$pre_space\n\\begin_inset LatexCommand ";
  448.         #    \bibliography gets handled as a LatexCommand inset, but
  449.         # it's a special case, cuz LyX syntax expects "\BibTeX"
  450.         # instead of "\bibliography" (I have no idea why), and because
  451.         # we have to print extra stuff
  452.         #    Because we might not have encountered the
  453.         # \bibliographystyle command yet, we write
  454.         # "insert bibstyle here", and replace that string
  455.         # with the actual bibliographystyle argument in
  456.         # LastLyX (i.e., the next pass through the file)
  457.         if ($name eq "\\bibliography") {
  458.             print OUTFILE "\\BibTeX[", $bibstyle_insert_string, "]";
  459.         } else {
  460.             print OUTFILE "$name";
  461.         # \cite takes an optional argument, e.g.
  462.         my $args = $eaten->relyx_args ($fileobject);
  463.         while ($args =~ s/^o//) {
  464.             my $tok = $fileobject->eatOptionalArgument;
  465.             my $dummy = $tok->exact_print;
  466.             print OUTFILE $dummy;
  467.         print OUTFILE "\{";
  468.             last TYPESW; # skip to the end of the switch
  469.         }
  470.         if (grep {$_ eq $name} @IncludeCommands) {
  471.         &CheckForNewParagraph; #Start new paragraph if necessary
  472.             print OUTFILE "$pre_space\n$Begin_Inset_Include $name\{";
  473.             last TYPESW; # skip to the end of the switch
  474.         }
  475.         # This is to handle cases where _ is used, say, in a filename.
  476.         # When _ is used in math mode, it'll be copied by the math mode
  477.         # copying subs. Here we handle cases where it's used in non-math.
  478.         # Examples are filenames for & citation labels.
  479.         # (It's illegal to use it in regular LaTeX text.)
  480.         if ($name eq "_") {
  481.            print OUTFILE $eaten->exact_print;
  482.            last TYPESW;
  483.         }
  484.         # Sectioning and Title environments (using a LyX \layout command)
  485.         if (exists $ReadCommands::ToLayout->{$name}) {
  486.         &ConvertToLayout($name);
  487.         last TYPESW; #done translating
  488.         # Font characteristics
  489.         } elsif (exists $FontTransTable{$name}) {
  490.         my $dum2;
  491.             my $command = $FontTransTable{$name};
  492.             ($dummy, $dum2) = ($command =~ /(\S+)\s+(\w+)/);
  493.         # HACK so that "\emph{hi \emph{bye}}" yields unemph'ed "bye"
  494.         if ( ($dummy eq "\\emph") && 
  495.              ($CurrentFontStatus->{$dummy}->[-1] eq "on")) {
  496.                $dum2 = "default"; # "on" instead of default?
  497.                $command =~ s/on/default/;
  498.         # If we're about to start a new paragraph, save writing
  499.         #    this command until *after* we write '\layout Standard'
  500.         if (! $IsNewParagraph) {
  501.             print OUTFILE "$pre_space$command";
  502.         # Store the current font change
  503.         die "Font command error" if !exists $$CurrentFontStatus{$dummy};
  504.         push (@{$CurrentFontStatus->{$dummy}}, $dum2);
  505.         # Handle footnotes and margin notes
  506.         # Make a new font table & layout stack which will be local to the 
  507.         #    footnote or marginpar
  508.         } elsif (exists $FloatTransTable{$name}) {
  509.             my $command = $FloatTransTable{$name};
  510.         # Open the footnote
  511.         print OUTFILE "$pre_space$command";
  512.         # Make $CurrentFontStatus point to a new (anonymous) font table
  513.             $CurrentFontStatus =  {
  514.             '\emph' => ["default"],
  515.             '\family' => ["default"],
  516.             '\series' => ["default"],
  517.             '\shape' => ["default"],
  518.             '\bar' => ["default"],
  519.             '\size' => ["default"],
  520.             '\noun' => ["default"],
  521.         # And make $CurrentLayoutStack point to a new (anon.) stack
  522.         $CurrentLayoutStack = ["Standard"];
  523.         # Store whether we're at the end of a paragraph or not
  524.         #    for when we get to end of footnote AND 
  525.         # Note that the footnote text will be starting a new paragraph
  526.         # Also store the current alignment (justification)
  527.         $OldINP = $IsNewParagraph; $OldMBD = $MayBeDeeper;
  528.         $OldAlignment = $CurrentAlignment;
  529.         $IsNewParagraph = 1;
  530.         $MayBeDeeper = 0; #can't be deeper at beginning of footnote
  531.         $CurrentAlignment = "";
  532.         # Accents
  533.         } elsif ($name =~ m/^$AccentTokens$/) {
  534.         &CheckForNewParagraph; # may be at the beginning of a par
  535.         print OUTFILE "$pre_space\n",'\i ',$name,'{'
  536.         
  537.         # Included Postscript Figures
  538.         # Currently, all postscript including commands take one
  539.         # required argument and 0 to 2 optional args, so we can
  540.         # clump them together in one else.
  541.         } elsif (grep {$_ eq $name} @GraphicsCommands) {
  542.         &CheckForNewParagraph; # may be at the beginning of a par
  543.         my $arg1 = $fileobject->eatOptionalArgument;
  544.         # arg2 is a token of an empty string for most commands
  545.         my $arg2 = $fileobject->eatOptionalArgument;
  546.         my $arg3 = $fileobject->eatRequiredArgument;
  547.         my $save = $arg1->exact_print . $arg2->exact_print .
  548.                    $arg3->exact_print;
  549.         # Parse and put figure into LyX file
  550.         # Print it verbatim if we didn't parse correctly
  551.         my $thisfig = new RelyxFigure::Figure;
  552.         if ($thisfig->parse_pscommand($name, $arg1, $arg2, $arg3)) {
  553.             print OUTFILE $thisfig->print_info;
  554.         } else {
  555.             &print_tex_mode($eaten->exact_print . $save);
  556.         # Tables
  557.         } elsif ($name eq "\\multicolumn") {
  558.         if ($thistable = &RelyxTable::in_table) {
  559.             # the (simple text) first arg.
  560.             $dummy = $fileobject->eatRequiredArgument->contents->print;
  561.             my $group = $fileobject->eatRequiredArgument;
  562.             $thistable->multicolumn($dummy, $group);
  563.         } else {warn "\\multicolumn is illegal outside a table!"}
  564.         } elsif ($name eq '\cline') {
  565.         if ($thistable = &RelyxTable::in_table) {
  566.             # the (simple text) first arg.
  567.             $dummy = $fileobject->eatRequiredArgument->contents->print;
  568.             # sub hcline does cline if an arg is given
  569.             $thistable->hcline($dummy);
  570.         } else {warn "\\cline is illegal outside a table!"}
  571.         # Bibliography
  572.         } elsif ($name eq '\bibliographystyle') {
  573.         $tok = $fileobject->eatRequiredArgument;
  574.         $bibstyle_file = "";
  575.         # There may be >1 token in the {}, e.g. "{a_b}" -> 3 tokens
  576.         my @toks = $tok->contents;
  577.         foreach $tok (@toks) {
  578.             # kludge: CleanTeX adds {} after _
  579.             $tok = $tok->contents if ref($tok) eq "Text::TeX::Group";
  580.             $bibstyle_file .= $tok->print;
  581.         print "\nBibliography style file is $bibstyle_file"if $debug_on;
  582.         # LyX \bibitem actually looks just like LaTeX bibitem, except
  583.         # it's in a Bibliography par & there must be a space after the
  584.         # bibitem command. Note we need to explicitly print the braces...
  585.         } elsif ($name eq "\\bibitem") {
  586.         $IsNewParagraph=1; # \bibitem always starts new par. in LyX
  587.         &CheckForNewParagraph;
  588.         $tok = $fileobject->eatOptionalArgument;
  589.             print OUTFILE "$name ", $tok->exact_print, "{";
  590.         # Miscellaneous
  591.         # ensuremath -- copy verbatim until you're done
  592.         # but add \( and \)
  593.         # Note that we'll only get here if the command is NOT in math mode
  594.         } elsif ($name eq '\ensuremath') {
  595.         print "\nCopying math beginning with '$name'\n" if $debug_on;
  596.         my $tok = $fileobject->eatGroup; # eat math expression
  597.         my $dummy = $tok->exact_print;
  598.         $dummy =~ s/\{(.*)\}/$1/;
  599.         $dummy = &fixmath($dummy); # convert '\sp' to '^', etc.
  600.         &CheckForNewParagraph; # math could be first thing in a par
  601.         print OUTFILE "$pre_space\n\\begin_inset Formula \\( ";
  602.         print $dummy if $debug_on;
  603.         print OUTFILE $dummy;
  604.             # end math
  605.         print OUTFILE "\\)\n\\end_inset \n\n";
  606.         print "\nDone copying math" if $debug_on;
  607.         # Token in the ReadCommands command list that basic_lyx doesn't
  608.         #    know how to handle
  609.         } else {
  610.         ©_latex_known($eaten,$fileobject);
  611.         } # end big if
  612.         # Exit the switch
  613.         last TYPESW;
  614.     # ArgTokens appear when we've used eatRequiredArgument
  615.     if (/^ArgToken$/) {
  616.         # If we're copying a recognized but untranslatable token in tex mode
  617.         my $tok = $tex_mode_tokens[-1] || 0;
  618.         if ($eaten->base_token == $tok) {
  619.         ©_latex_known($eaten,$fileobject);
  620.         }
  621.         
  622.         last TYPESW;
  623.     if (/^EndArgsToken$/) {
  624.         # If we're copying a recognized but untranslatable token in tex mode
  625.         my $tok = $tex_mode_tokens[-1] || 0;
  626.         if ($eaten->base_token eq $tok) {
  627.         ©_latex_known($eaten,$fileobject);
  628.             last TYPESW;
  629.         }
  630.         my $name = $eaten->token_name;
  631.         print "$name" if $debug_on;
  632.         # Handle things that LyX translates as a "LatexCommand" inset
  633.         # or "Include" insets
  634.         if (grep {$_ eq $name} @LatexCommands, @IncludeCommands) {
  635.             print OUTFILE "\}\n\n\\end_inset \n\n";
  636.         } elsif (exists $ReadCommands::ToLayout->{$name}) {
  637.         &EndLayout($name);
  638.         # Font characteristics
  639.         # Pop the current FontStatus stack for a given characteristic
  640.         #    and give the new command (e.g., \emph default)
  641.         } elsif (exists $FontTransTable{$name}) {
  642.             my $command = $FontTransTable{$name};
  643.             ($dummy) = ($command =~ /(\S+)\s+\w+/);
  644.         pop @{$CurrentFontStatus->{$dummy}};
  645.         $command = "\n$dummy $CurrentFontStatus->{$dummy}->[-1] \n";
  646.         print OUTFILE "$command";
  647.         # Footnotes and marginpars
  648.         } elsif (exists $FloatTransTable{$name}) {
  649.             print OUTFILE "\n\\end_float \n\n";
  650.         # Reset the layout stack and font status table pointers to
  651.         #    point to the global stack/table again, instead of the
  652.         #    footnote-specific stack/table
  653.         $CurrentFontStatus = \%FontStatus;
  654.         $CurrentLayoutStack = \@LayoutStack;
  655.         # We need to reissue any font commands (but not layouts)
  656.         foreach $dummy (keys %$CurrentFontStatus) {
  657.             if ($CurrentFontStatus->{$dummy}->[-1] ne "default") {
  658.             print OUTFILE $FontTransTable{$dummy};
  659.             }
  660.         # Same paragraph status as we had before the footnote started
  661.         $IsNewParagraph = $OldINP; $MayBeDeeper = $OldMBD;
  662.         $CurrentAlignment = $OldAlignment;
  663.         } elsif ($name =~ m/^$AccentTokens$/) {
  664.         print OUTFILE "}\n";
  665.         
  666.         } elsif ($name eq "\\bibitem") {
  667.             print OUTFILE "}\n";
  668.         } # End if on $name
  669.         # Exit main switch
  670.         last TYPESW;
  671.     } # end if EndArgsToken
  672.     # Handle END of scope of local commands like \large
  673.     if (/^EndLocal$/) {
  674.         my $name = $eaten->token_name; #cmd we're ending, e.g.,\large
  675.         print $name if $debug_on;
  676.         if (exists $FontTransTable{$name}) {
  677.         my $command = $FontTransTable{$name};
  678.         ($dummy = $command) =~ s/\s*(\S*)\s+(\w+)\s*/$1/; #e.g., '\size'
  679.         die "Font command error" if !exists $$CurrentFontStatus{$dummy};
  680.         # TT::OF->check_presynthetic returns local commands FIFO!
  681.         # So pop font stack, but warn if we pop the wrong thing
  682.         warn " font confusion?" if
  683.                pop @{$CurrentFontStatus->{$dummy}} ne $2;
  684.         print "\nCurrent $dummy Stack: @{$CurrentFontStatus->{$dummy}}"
  685.               if $debug_on;
  686.         my $newfont = $CurrentFontStatus->{$dummy}->[-1];
  687.         $command = "\n$dummy $newfont\n";
  688.         print OUTFILE "$command";
  689.         } else {
  690.             warn "Unknown EndLocal token!\n";
  691.         }
  692.         last TYPESW;
  693.     # We don't print { or }, but we make sure that the spacing is correct
  694.     # Handle '{'
  695.     if (/^Begin::Group$/) {
  696.         print OUTFILE "$pre_space";
  697.         last TYPESW;
  698.     # Handle '{'
  699.     if (/^End::Group$/) {
  700.         print OUTFILE "$pre_space";
  701.         last TYPESW;
  702.         # Handle \begin{foo}
  703.         if (/^Begin::Group::Args$/) {
  704.         print $eaten->print," " if $debug_on; # the string "\begin{foo}"
  705.         my $env = $eaten->environment;
  706.         
  707.         # Any environment found in the layouts files
  708.         if (exists $ReadCommands::ToLayout->{$env}) {
  709.             &ConvertToLayout($env);
  710.         # Some environments have an extra argument. In that case,
  711.         # print the \layout command (cuz these environments always
  712.         # start new pars). Then either print the extra arg or
  713.         # ignore it (depending on the environment).
  714.         if (exists $ExtraArgEnvironments{$env}) {
  715.             # Should be just one token in the arg.
  716.             my $arg = $fileobject->eatBalanced->contents->print;
  717.             if ($ExtraArgEnvironments{$env}) { #print it
  718.             print "\nArgument $arg to $env environment"
  719.                                 if $debug_on;
  720.             $item_preface = $ExtraArgEnvironments{$env} . $arg."\n";
  721.             } else { #ignore it
  722.             print "\nIgnoring argument '$arg' to $env environment"
  723.                                 if $debug_on;
  724.             }
  725.         } # end if for reading extra args to \begin command
  726.         # Math environments
  727.         } elsif ($env =~ /^$MathEnvironments$/o) {
  728.         &CheckForNewParagraph; # may be beginning of paragraph
  729.         my $begin_text = $eaten->print;
  730.         print "\nCopying math beginning with '$begin_text'\n"
  731.                                                       if $debug_on;
  732.         print OUTFILE "\n\\begin_inset Formula $begin_text ";
  733.         $dummy = &Verbatim::copy_verbatim($fileobject, $eaten);
  734.         $dummy = &fixmath($dummy); # convert '\sp' to '^', etc.
  735.         print $dummy if $debug_on;
  736.         print OUTFILE $dummy;
  737.         # Alignment environments
  738.         } elsif (exists $AlignEnvironments{$env}) {
  739.         # Set it to the command which creates this alignment
  740.             $CurrentAlignment = $AlignEnvironments{$env};
  741.         ($dummy) = ($CurrentAlignment =~ /\S+\s+(\w+)/);
  742.         print "\nNow $dummy-aligning text " if $debug_on;
  743.         # alignment environments automatically start a new paragraph
  744.         $IsNewParagraph = 1;
  745.         # Environments lyx translates to floats
  746.         } elsif (exists $FloatEnvTransTable{$env}) {
  747.         $tok = $fileobject->eatOptionalArgument;
  748.         if ($tok && defined ($dummy = $tok->print) && $dummy) {
  749.             print "\nIgnoring float placement '$dummy'" if $debug_on;
  750.             my $command = $FloatEnvTransTable{$env};
  751.         # Open the footnote
  752.         print OUTFILE "$command";
  753.         # table
  754.         } elsif ($env =~ /^tabular$/) { # don't allow tabular* or ctabular
  755.         # Table must start a new paragraph
  756.         $IsNewParagraph = 1; $MayBeDeeper = 1;
  757.         # We want to print table stuff *after* a \layout Standard
  758.         &CheckForNewParagraph;
  759.         # Since table info needs to come *before* the table content,
  760.         #    put a line in the output file saying that the *next*
  761.         #    reLyX pass needs to put the table info there
  762.         print OUTFILE "\n$RelyxTable::TableBeginString\n";
  763.         # Read and ignore an optional argument [t] or [b]
  764.         $tok = $fileobject->eatOptionalArgument;
  765.         if ($tok && defined ($dummy = $tok->print) && $dummy) {
  766.             print "\nIgnoring positioning arg '$dummy'" if $debug_on;
  767.             # Read the argument into a TT::Group
  768.         #   (that group may contain groups, e.g. for {clp{10cm}}
  769.         $tok = $fileobject->eatGroup;
  770.         new RelyxTable::Table $tok;
  771.         # \begin document
  772.         } elsif ($env eq "document") {
  773.         # do nothing
  774.             #print "\nStarting to translate actual document" if $debug_on;
  775.         # Special environments to copy as regular text (-r option).
  776.         # Do this by copying the \begin & \end command in TeX mode
  777.         # (\Q\E around $env allows *'s in environment names!)
  778.         } elsif (grep /^\Q$env\E$/, @ReadCommands::regular_env) {
  779.         print "\nCopying $env environment as regular text\n"
  780.                                                       if $debug_on;
  781.         $dummy = $eaten->print; # \begin{env}, ignore initial whitespace
  782.         &print_tex_mode($dummy);
  783.         # otherwise, it's an unknown environment
  784.         # In that case, copy everything up to the \end{env}
  785.         #    Save stuff in global tex_mode_string so we can print it
  786.         # when we read & handle the \end{env}
  787.         } else {
  788.         print "\nUnknown environment $env" if $debug_on;
  789.         $tex_mode_string = "";
  790.         # print "\begin{env}
  791.         # For reLyXskip env, don't print the \begin & \end commands!
  792.         $tex_mode_string .= $eaten->exact_print 
  793.                         unless $env eq "reLyXskip";
  794.         $tex_mode_string .=&Verbatim::copy_verbatim($fileobject,$eaten);
  795.         }
  796.             last TYPESW;
  797.         }
  798.         
  799.         # Handle \end{foo}
  800.         if (/^End::Group::Args$/) {
  801.         print $eaten->print," " if $debug_on; # the string "\end{foo}"
  802.         my $env = $eaten->environment;
  803.         # End of list or quote/verse environment
  804.         # OR special environment given with -t option
  805.         if (exists $ReadCommands::ToLayout->{$env}) {
  806.             &EndLayout($env);
  807.         $item_preface = ""; # reset when at end of List env.
  808.         # End of math environments
  809.         } elsif ($env =~ /^$MathEnvironments$/o) {
  810.         print OUTFILE "\\end{$env}\n\\end_inset \n\n";
  811.         print "\nDone copying math environment '$env'" if $debug_on;
  812.         } elsif (exists $AlignEnvironments{$env}) {
  813.         # Back to block alignment
  814.             $CurrentAlignment = "";
  815.         print "\nBack to block alignment" if $debug_on;
  816.         # assume that \end should end a paragraph
  817.         # This isn't correct LaTeX, but LyX can't align part of a par
  818.         $IsNewParagraph = 1;
  819.         # Environments lyx translates to floats
  820.         } elsif (exists $FloatEnvTransTable{$env}) {
  821.             print OUTFILE "\n\\end_float \n\n";
  822.         # table
  823.         } elsif ($env =~ /tabular$/) { # don't allow tabular*
  824.             if ($thistable = &RelyxTable::in_table) {
  825.             $thistable->done_reading;
  826.             print OUTFILE "\n$RelyxTable::TableEndString\n";
  827.         } else {warn "found \\end{tabular} when not in table!"}
  828.         # Anything after a table will be a new paragraph
  829.         $IsNewParagraph = 1; $MayBeDeeper = 1;
  830.         } elsif ($env eq "document") {
  831.             print "\nDone with document!" if $debug_on;
  832.         # "regular" environment given with -r option
  833.         } elsif (grep /^\Q$env\E$/, @ReadCommands::regular_env) {
  834.         $dummy = $eaten->print; # \end{env}, ignore initial whitespace
  835.         &print_tex_mode($dummy);
  836.         # Next stuff will be new env.
  837.         $IsNewParagraph = 1;
  838.         # End of unknown environments. We're already in TeX mode
  839.         } else {
  840.         # Add \end{env} (including initial whitespace) to string
  841.         # For reLyXskip environment, don't print \begin & \end commands!
  842.         $tex_mode_string .= $eaten->exact_print
  843.                                unless $env eq "reLyXskip";
  844.         # Now print it
  845.         &print_tex_mode($tex_mode_string);
  846.         print "Done copying unknown environment '$env'" if $debug_on;
  847.         }
  848.             last TYPESW;
  849.         }
  850.     # Note for text handling: we have to do lots of stuff to handle
  851.     # spaces in (as close as possible to) the same way that LaTeX does
  852.     #    LaTeX considers all whitespace to be the same, so basically, we
  853.     # convert each clump of whitespace to one space. Unfortunately, there
  854.     # are special cases, like whitespace at the beginning/end of a par,
  855.     # which we have to get rid of to avoid extra spaces in the LyX display.
  856.     #    \n at the end of a paragraph must be considered like a space,
  857.     # because the next line might begin with a token like \LyX. But
  858.     # if the next line starts with \begin, say, then an extra space will be
  859.     # generated in the LyX file. Oh well. It doesn't affect the dvi file.
  860.     if (/^Text$/) {
  861.         my $outstr = $eaten->print; # the actual text
  862.         # don't bother printing whitespace
  863.         #    Note: this avoids the problem of extra whitespace generating
  864.         # extra Text::TeX::Paragraphs, which would generate extra
  865.         # \layout commands
  866.         last TYPESW if $outstr =~ /^\s+$/;
  867.         # whitespace at beginning of a paragraph is meaningless
  868.         # e.g. \begin{foo}\n hello \end{foo} shouldn't print the \n
  869.         # (Note: check IsNewParagraph BEFORE calling &CFNP, which zeros it)
  870.         my $replace = $IsNewParagraph ? "" : " ";
  871.         $outstr =~ s/^\s+/$replace/;
  872.         # Only write '\layout Standard' once per paragraph
  873.         &CheckForNewParagraph;
  874.         # \n\n signals end of paragraph, so get rid of it (and any space
  875.         # before it)
  876.         $outstr =~ s/\s*\n\n$//;
  877.         # Print the LaTeX text to STDOUT
  878.         print "'$outstr'" if $debug_on;
  879.         # LyX *ignores* \n and \t, whereas LaTeX considers them just
  880.         # like a space.
  881.         #    Also, many spaces are equivalent to one space in LaTeX
  882.         # (But LyX misleadingly displays them on screen, so get rid of them)
  883.         $outstr =~ s/\s+/ /g;
  884.         # protect spaces in an optional argument if necessary
  885.         # Put a SPACE after the argument for List, Description layouts
  886.         if ($protect_spaces) {
  887.         $dummy = $TextTokenTransTable{'~'};
  888.         # This will not handle brackets in braces!
  889.         if ($outstr =~ /\]/) { # protect spaces only *until* the bracket
  890.             my $tempstr = $`;
  891.             my $tempstr2 = $';
  892.             # Note that any \t's have been changed to space already
  893.             $tempstr =~ s/ /$dummy/g;
  894.             # Print 1 space after the argument (which finished with ])
  895.             # Don't print 2 (i.e. remove leading space from $tempstr2)
  896.             # don't print the bracket
  897.             $tempstr2 =~ s/^ //;
  898.             $outstr = "$tempstr $tempstr2";
  899.             $protect_spaces = 0; # Done with optional argument
  900.         } else { # protect all spaces, since we're inside brackets
  901.             $outstr =~ s/ /$dummy/g;
  902.         } # end special stuff for protected spaces
  903.         # Translate any LaTeX text that requires special LyX handling
  904.         foreach $dummy (keys %TextTransTable) {
  905.         $outstr =~ s/\Q$dummy\E/$TextTransTable{$dummy}/g;
  906.         }
  907.         # "pretty-print" the string. It's not perfect, since there may
  908.         # be text in the OUTFILE before $outstr, but it will keep from
  909.         # having REALLY long lines.
  910.         # Try to use approximately the same word-wrapping as LyX does:
  911.         # - before space after a period, except at end of string
  912.         # - before first space after column seventy-one
  913.         # - after 80'th column
  914.         while (1) {
  915.             $outstr =~ s/\. (?!$)/.\n /      or
  916.         $outstr =~ s/(.{71,79}?) /$1\n / or
  917.         $outstr =~ s/(.{80})(.)/$1\n$2/ or
  918.         last; # exit loop if string is < 79 characters
  919.         }
  920.         # Actually print the text
  921.         print OUTFILE "$outstr";
  922.         last TYPESW;
  923.     } # end TT::Text handling
  924.     # The default action - this should never happen!
  925.     print("I don't know ",$eaten->print) if $debug_on;
  926.     } # end for ($type)
  927.     print "\n" if $debug_on;
  928. } #end sub basic_lyx
  929. #########################  TEX MODE  SUBROUTINES  #########################
  930. # This subroutine copies and prints a latex token and its arguments if any.
  931. # This sub is only needed if the command was not found in the syntax file
  932. # Use exact_print to preserve, e.g., whitespace after macros
  933. sub copy_latex_unknown {
  934.     my $eaten = shift;
  935.     my $fileobject = shift;
  936.     my $outstr = $eaten->exact_print;
  937.     my ($dummy, $tok, $count);
  938. # Copy the actual word. Copy while you've still got
  939. #     arguments. Assume all args must be in the same paragraph
  940. #     (There could be new paragraphs *within* args)
  941.     #    We can't use copy_verbatim (unless we make it smarter) because
  942.     # it would choke on nested braces
  943.     print "\nUnknown token: '",$eaten->print,"': Copying in TeX mode\n"
  944.                                                          if $debug_on;
  945.     my $dum2;
  946.     while (($dum2 = $fileobject->lookAheadToken) &&
  947.        ($dum2 =~ /^[[{]$/)) {
  948.     if ($dum2 eq '[') { #copy optional argument - assume it's simple
  949.         $tok = $fileobject->eatOptionalArgument;
  950.         $outstr .= $tok->exact_print; # also print brackets & whitespace
  951.     } else {
  952.         $count = 0;
  953.         EAT: { #copied from eatBalanced, but allow paragraphs
  954.             die unless defined ($tok = $fileobject->eatMultiToken);
  955.         $outstr.="\n",redo EAT if ref($tok) eq "Text::TeX::Paragraph";
  956.         $dummy = $tok->exact_print;
  957.         $outstr .= $dummy;
  958.         # Sometimes, token will be '\n{', e.g.
  959.         $count++ if $dummy =~ /^\s*\{$/; # add a layer of nesting
  960.         $count-- if $dummy =~ /^\s*\}$/; # end one layer of nesting
  961.         redo EAT if $count; #don't dump out until all done nesting
  962.         } #end EAT block
  963.     } # end if $dummy = [{
  964.     } #end while
  965.     # Add {} after macro if it's followed by '}'. Otherwise, {\foo}bar
  966.     #     will yield \foobar when LyX creates LaTeX files
  967.     $outstr.="{}" if $outstr=~/\\[a-zA-Z]+$/ && $dum2 eq '}';
  968.     # Print it out in TeX mode
  969.     &print_tex_mode($outstr);
  970.     print "\nDone copying unknown token" if $debug_on;
  971. } # end sub copy_latex_unknown
  972. # Copy an untranslatable latex command whose syntax we know, along with its
  973. # arguments
  974. #    The command itself, optional arguments, and untranslatable
  975. # arguments get copied in TeX mode. However, arguments which contain regular
  976. # LaTeX will get translated by reLyX. Do that by printing what you have so
  977. # far in TeX mode, leaving this subroutine, continuing with regular reLyX
  978. # translating, and then returning here when you reach the ArgToken or
  979. # EndArgsToken at the end of the translatable argument.
  980. #    We need to keep a stack of the tokens that brought us here, because
  981. # you might have nested commands (e.g., \mbox{hello \fbox{there} how are you}
  982. sub copy_latex_known {
  983.     my ($eaten, $fileobject) = (shift,shift);
  984.     my $type = ref($eaten);
  985.     $type =~ s/^Text::TeX::// or die "unknown token?!";
  986.     # token itself for TT::Token, TT::BegArgsToken,
  987.     # Corresponding BegArgsToken for ArgToken,EndArgsToken
  988.     my $temp_start = $eaten->base_token;
  989. # Initialize tex mode copying
  990.     if ($type eq "BegArgsToken" or $type eq "Token") {
  991.     print "\nCopying untranslatable token '",$eaten->print,
  992.                                   "' in TeX mode" if $debug_on;
  993.     push @tex_mode_tokens, $temp_start;
  994.     # initialize the string of stuff we're copying
  995.     $tex_mode_string = $eaten->exact_print;
  996.     } # Start tex copying?
  997. # Handle arguments
  998.     # This token's next arguments -- returns a string matching /o*[rR]?/
  999.     my $curr_args = $eaten->next_args($fileobject);
  1000.     if ($type eq "EndArgsToken" or $type eq "ArgToken") {
  1001.     # Print ending '}' for the group we just finished reading
  1002.     $tex_mode_string .= '}';
  1003.     }
  1004.     # If there could be optional arguments next, copy them
  1005.     while ($curr_args =~ s/^o// && $fileobject->lookAheadToken eq '[') {
  1006.     my $opt = $fileobject->eatOptionalArgument;
  1007.     $tex_mode_string .= $opt->exact_print;
  1008.     }
  1009.     $curr_args =~ s/^o*//; # Some OptArgs may not have appeared
  1010.     if ($type eq "BegArgsToken" or $type eq "ArgToken") {
  1011.     # Print beginning '{' for the group we're about to read
  1012.     $tex_mode_string .= '{';
  1013.     }
  1014.     # Now copy the next required argument, if any
  1015.     # Copy it verbatim (r), or translate it as regular LaTeX (R)?
  1016.     if ($curr_args =~ s/^r//) {
  1017.     my $group = $fileobject->eatRequiredArgument;
  1018.     my $temp = $group->exact_print;
  1019.     # Remove braces. They're put in explicitly
  1020.     $temp =~ s/\{(.*)\}/$1/; # .* is greedy
  1021.     $tex_mode_string .= $temp;
  1022.     } elsif ($curr_args =~ s/^R//) {
  1023.     print "\n" if $debug_on;
  1024.     &print_tex_mode($tex_mode_string);
  1025.     $tex_mode_string = "";
  1026.     print "\nTranslating this argument for ",$temp_start->print,
  1027.           " as regular LaTeX" if $debug_on;
  1028.     } else { # anything but '' is weird
  1029.     warn "weird arg $curr_args to ",$temp_start->print,"\n" if $curr_args;
  1030.     }
  1031. # Finished tex mode copying
  1032.     if ($type eq "Token" or $type eq "EndArgsToken") {
  1033.     # Add {} to plain tokens followed by { or }. Otherwise {\foo}bar
  1034.     # and \foo{bar} yield \foobar in the LaTeX files created by LyX
  1035.     my $dummy;
  1036.     if ($type eq "Token" and
  1037.             $dummy=$fileobject->lookAheadToken and
  1038.             $dummy =~ /[{}]/)
  1039.         $tex_mode_string .= '{}';
  1040.     # Print out the string
  1041.     print "\n" if $debug_on;
  1042.     &print_tex_mode($tex_mode_string);
  1043.     $tex_mode_string = "";
  1044.         # We're done with this token
  1045.     pop(@tex_mode_tokens);
  1046.     my $i = $type eq "Token" ? "" : " and its arguments";
  1047.     my $j = $temp_start->print;
  1048.     print "\nDone copying untranslatable token '$j'$i in TeX mode"
  1049.                                                       if $debug_on;
  1050.     } # end tex copying?
  1051. } # end sub copy_latex_known
  1052. # Print a string in LyX "TeX mode"
  1053. #    The goal of this subroutine is to create a block of LyX which will be
  1054. # translated exactly back to the original when LyX creates its temporary LaTeX
  1055. # file prior to creating a dvi file.
  1056. #    Don't print \n\n at the end of the string... instead just set the new
  1057. # paragraph flag. Also, don't print whitespace at the beginning of the string.
  1058. # Print nothing if it's the beginning of a paragraph, or space otherwise.
  1059. # These two things avoid extra C-Enter's in the LyX file
  1060. sub print_tex_mode {
  1061.     my $outstr = shift;
  1062.     print "'$outstr'" if $debug_on;
  1063.     # Handle extra \n's (& spaces) at beginning & end of string
  1064.     my $str_ends_par = ($outstr =~ s/\n{2,}$//);
  1065.     if ($IsNewParagraph) {
  1066.         $outstr =~ s/^\s+//;  # .e.g, $outstr follows '\begin{quote}'
  1067.     } else {
  1068.     # Any whitespace is equivalent to one space in LaTeX
  1069.         $outstr =~ s/^\s+/ /; # e.g. $outstr follows "\LaTeX{}" or "Hi{}"
  1070.     }
  1071.     # Check whether string came right at the beginning of a new paragraph
  1072.     # This *might* not be necessary. Probably can't hurt.
  1073.     &CheckForNewParagraph;
  1074.     # Get into TeX mode
  1075.     print OUTFILE "$start_tex_mode";
  1076.     # Do TeX mode translation;
  1077.     $outstr =~ s/\\/\n\\backslash /g;
  1078.     # don't translate \n in '\n\backslash' that we just made!
  1079.     $outstr =~ s/\n(?!\\backslash)/\n\\newline \n/g;
  1080.     # Print the actual token + arguments if any
  1081.     print OUTFILE $outstr;
  1082.     # Get OUT of LaTeX mode (and end nesting if nec.)
  1083.     print OUTFILE "$end_tex_mode";
  1084.     $IsNewParagraph = $str_ends_par;
  1085.     return;
  1086. } # end sub print_tex_mode
  1087. ############################  LAYOUT  SUBROUTINES  ###########################
  1088. sub CheckForNewParagraph {
  1089. # This subroutine makes sure we only write \layout command once per paragraph
  1090. #    It's mostly necessary cuz 'Standard' layout is the default in LaTeX;
  1091. #    there is no command which officially starts a standard environment
  1092. # If we're in a table, new layouts aren't allowed, so just return
  1093. # If $IsNewParagraph is 0, it does nothing
  1094. # If $INP==1, It starts a new paragraph
  1095. # If $CurrentAlignment is set, it prints the alignment command for this par.
  1096. # If $MayBeDeeper==1 and we're currently within a list environment,
  1097. #    it starts a "deeper" Standard paragraph
  1098.     my $dummy; 
  1099.     my $layout = $$CurrentLayoutStack[-1];
  1100.     return if &RelyxTable::in_table;
  1101.     if ($IsNewParagraph) {
  1102.     # Handle standard text within a list environment specially
  1103.     if ($MayBeDeeper) {
  1104.         if ($layout =~ /^$ListLayouts$/o) {
  1105.         push (@$CurrentLayoutStack, "Standard");
  1106.         print "\nCurrent Layout Stack: @$CurrentLayoutStack\n"
  1107.                                              if $debug_on;
  1108.         print OUTFILE "\n\\begin_deeper ";
  1109.         $layout = "Standard";
  1110.         }
  1111.         $MayBeDeeper = 0; # Don't test again until new paragraph
  1112.     # Print layout command itself
  1113.     print OUTFILE "\n\\layout $layout\n\n";
  1114.     print OUTFILE $CurrentAlignment if $CurrentAlignment;
  1115.     # Now that we've written the command, it's no longer a new paragraph
  1116.     $IsNewParagraph = 0;
  1117.     # And we're no longer waiting to see if this paragraph is empty
  1118.     $PendingLayout = 0;
  1119.     # When you write a new paragraph, reprint any font commands
  1120.     foreach $dummy (keys %$CurrentFontStatus) {
  1121.         my $currf = $CurrentFontStatus->{$dummy}->[-1];
  1122.         if ($currf ne "default") {
  1123.         print OUTFILE "\n$dummy $currf \n";
  1124.         }
  1125.     } # end if $INP
  1126. } # end sub CheckForNewParagraph
  1127. sub ConvertToLayout {
  1128. # This subroutine begins a new layout, pushing onto the layout stack, nesting
  1129. # if necessary. It doesn't actually write the \layout command -- that's
  1130. # done by CheckForNewParagraph.
  1131. #    The subroutine assumes that it's being passed an environment name or macro
  1132. # which is known and which creates a known layout
  1133. #    It uses the ToLayout hash (created by the ReadCommands module) which
  1134. # gives the LyX layout for a given LaTeX command or environment
  1135. # Arg0 is the environment or macro
  1136.     my $name = shift;
  1137.     my $layoutref = $ReadCommands::ToLayout->{$name};
  1138.     my $layout = $layoutref->{'layout'};
  1139.     my $keepempty = $layoutref->{'keepempty'};
  1140.     my $dummy = ($name =~ /^\\/ ? "macro" : "environment");
  1141.     print "\nChanging $dummy $name to layout $layout" if $debug_on;
  1142.     # Nest if the layout stack has more than just "Standard" in it
  1143.     if ($#{$CurrentLayoutStack} > 0) {
  1144.     # Die here for sections & things that can't be nested!
  1145.     print " Nesting!" if $debug_on;
  1146.     print OUTFILE "\n\\begin_deeper ";
  1147.     }
  1148.     # If we still haven't printed the *previous* \layout command because that
  1149.     # environment is empty, print it now! (This happens if an environment
  1150.     # is nested inside a keepempty, like slide.)
  1151.     &CheckForNewParagraph if $PendingLayout;
  1152.     # Put the new layout onto the layout stack
  1153.     push @$CurrentLayoutStack, $layout;
  1154.     print "\nCurrent Layout Stack: @$CurrentLayoutStack" if $debug_on;
  1155.     # Upcoming text will be new paragraph, needing a new layout cmd
  1156.     $IsNewParagraph = 1;
  1157.     # Test for nested "Standard" paragraph in upcoming text?
  1158.     # Some environments can nest. Sections & Title commands can't
  1159.     $MayBeDeeper = $layoutref->{"nestable"};
  1160.     # We haven't yet read any printable stuff in the new paragraph
  1161.     # If this is a layout that's allowed to be empty, prepare for an
  1162.     # empty paragraph
  1163.     $PendingLayout = $keepempty;
  1164. } # end sub ConvertToLayout
  1165. sub EndLayout {
  1166. # This subroutine ends a layout, popping the layout stack, etc.
  1167. #    The subroutine assumes that it's being passed an environment name or macro
  1168. # which is known and which creates a known layout
  1169. #    It uses the ToLayout hash (created by the ReadCommands module) which
  1170. # gives the LyX layout for a given LaTeX command or environment
  1171. # Arg0 is the environment or macro
  1172.     my $name = shift;
  1173.     my $layoutref = $ReadCommands::ToLayout->{$name};
  1174.     my $layout = $layoutref->{'layout'};
  1175.     my $dummy = ($name =~ /^\\/ ? "macro" : "environment");
  1176.     print "\nEnding $dummy $name (layout $layout)" if $debug_on;
  1177.     # If we still haven't printed the *previous* \layout command because that
  1178.     # environment is empty, print it now! Before we pop the stack!
  1179.     # This happens for a totally empty, non-nesting environment,
  1180.     # like hollywood.sty's fadein
  1181.     &CheckForNewParagraph if $PendingLayout;
  1182.     my $mylayout = pop (@$CurrentLayoutStack);
  1183.     # If a standard paragraph was the last thing in a list, then
  1184.     #     we need to end_deeper and then pop the actual list layout
  1185.     # This should only happen if the Standard layout was nested
  1186.     #    in a nestable layout. We don't check.
  1187.     if ($mylayout eq "Standard") {
  1188.     print OUTFILE "\n\\end_deeper ";
  1189.     print " End Standard Nesting!" if $debug_on;
  1190.     $mylayout = pop (@$CurrentLayoutStack);
  1191.     }
  1192.     # The layout we popped off the stack had better match the
  1193.     #    environment (or macro) we're ending!
  1194.     if ($mylayout ne $layout) { die "Problem with Layout Stack!\n"};
  1195.     print "\nCurrent Layout Stack: @$CurrentLayoutStack" if $debug_on;
  1196.     # If we're finishing a nested layout, we need to end_deeper
  1197.     # This should only happen if the layout was nested
  1198.     #    in a nestable layout. We don't check.
  1199.     # Note that if we're nested in a list environment and the
  1200.     #     NEXT paragraph is Standard, then we'll have an extra
  1201.     #     \end_deeper \begin_deeper in the LyX file. It's sloppy
  1202.     #     but it works, and LyX will get rid of it when it
  1203.     #     resaves the file.
  1204.     if ($#{$CurrentLayoutStack} > 0) {
  1205.     print " End Nesting!" if $debug_on;
  1206.     print OUTFILE "\n\\end_deeper ";
  1207.     }
  1208.     # Upcoming text will be new paragraph, needing a new layout cmd
  1209.     $IsNewParagraph = 1;
  1210.     # Test for nested "Standard" paragraph in upcoming text?
  1211.     # Some environments can nest. Sections & Title commands can't
  1212.     $MayBeDeeper = $layoutref->{"nestable"};
  1213. } # end sub EndLayout
  1214. #######################  MISCELLANEOUS SUBROUTINES  ###########################
  1215. sub fixmath {
  1216. # Translate math commands LyX doesn't support into commands it does support
  1217.     my $input = shift;
  1218.     my $output = "";
  1219.     while ($input =~ s/
  1220.         (.*?)    # non-token matter ($1)
  1221.         (\\      # token ($2) is a backslash followed by ...
  1222.             ( ([^A-Za-z] \*?) |    # non-letter (and *) ($4) OR
  1223.           ([A-Za-z]+ \*?)   )  # letters (and *) ($5)
  1224.         )//xs) # /x to allow whitespace/comments, /s to copy \n's
  1225.     { 
  1226.     $output .= $1;
  1227.     my $tok = $2;
  1228.     if (exists $ReadCommands::math_trans{$tok}) {
  1229.         $tok = $ReadCommands::math_trans{$tok};
  1230.         # add ' ' in case we had, e.g., \|a, which would become \Verta
  1231.         # Only need to do it in those special cases
  1232.         $tok .= ' ' if
  1233.                 defined $4 && $tok =~ /[A-Za-z]$/ && $input =~ /^[A-Za-z]/;
  1234.     $output .= $tok;
  1235.     }
  1236.     $output .= $input; # copy what's left in $input
  1237.     return $output;
  1238. 1; # return true to calling subroutine
  1239.