#! /usr/bin/perl -w # # mathgrep - grep through a LaTeX document looking only in math sections # # Copyright (C) 2007 Andrew Stacey # # This program is free software; you can redistribute it and/or mod # ify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. # use Term::ANSIColor; use Getopt::Long qw(:config auto_help bundling); use Pod::Usage; use strict; my ( $inline, # Toggle inline math $display, # Toggle display math $count, # Output count of successes $filename, # Output filename $lines, # Output line numbers $preamble, # Skip preamble $postamble, # Skip postamble $separate, # Toggle print separator $help, # Print simple help $start, # Line to start at $finish, # Line to finish at $textcolour, # Whether to colour matches $defaultcolour, # Default colour for matches $code, # Perl expr to apply $man, # Print man page $gpl, # Print perlgpl $infile, # Current input file $matchcount, # Number of successes $mathend, # Point in line where math ends $mathstart, # Point in line where math starts $postmath, # Stuff on line after math $match, # Did this match succeed? $premath, # Stuff on line before math $endmath, # String to match at end of math $offset, # Offset to start looking at $mathpattern, # Recognise start of math $replace, # Are we doing a replace? $displaymathpattern, # Recognise display math $extramathpattern, # Recognise extra math $inlinemathpattern, # Recognise inline math $reset, # Reset colour $after, # Context lines after $before, # Context lines before $verbose, # Verbosity level $line, # Current line in file $mathline, # Line no. at start of math $outmemory, # Memory for output routine $outsub, # Output subroutine $finalout, # Final output subroutine $presub, # Preamble subroutine $postsub, # Postamble subroutine $inhandle, # Input filehandle $outhandle, # Output filehandle $version, # Version option $verstring, # Version message $insub # Subroutine to set up input ); my @mathpatterns; # Used to assemble math pattern my @mathenvs; # Display math environments my @extraenvs; # Extra math envs specified by user my @lines; # Current lines in input # # Default options # $verbose = 0; # No verbosity $inline = 1; # Check inline math $display = 1; # Check display math $after = 0; # No context after match $before = 0; # No context before match $defaultcolour = "red"; # Default colour $verstring = "mathgrep version 1.0, Copyright (C) 2007 Andrew Stacey\nmathgrep comes with ABSOLUTELY NO WARRANTY; for details type\n `mathgrep --man'.\nThis is free software, and you are welcome to redistribute it under certain\nconditions; type `mathgrep --man' for details."; # # Get commandline options # GetOptions ( "i|inline!" => \$inline, # Do we exec on inline maths? "d|display!" => \$display, # Do we exec on display maths? "E|environments=s" => \@extraenvs, # Extra environments to check "c|count" => \$count, # Print a count of successes "l|files-with-matches" => \$filename, # Print successful filenames "L|files-without-matches" => sub {my ($a,$b) = @_; $filename = 0}, # Print successful filenames "n|line-number" => \$lines, # Print line numbers "R|preamble" => \$preamble, # Do we exec in preamble? "O|postamble" => \$postamble, # Do we exec in postamble? "r|replace:s" => \$replace, # Are we doing a replace? "S|start=i" => \$start, # Line number to start at "F|finish=i" => \$finish, # Line number to finish at "U|colour|color:s" => sub {my ($a, $b) = @_; $textcolour = ($b ? $b : $defaultcolour);}, # Colour in matches "P|separate!" => \$separate, # Do we print a separator "C|context=i" => sub {my ($a,$b) = @_; $after = $before = $b;}, # Lines of context to print "B|before-context=i" => \$before, # Lines before a match to print "A|after-context=i" => \$after, # Lines after a match to print "h|?|help" => \$help, # Print help and exit "man" => \$man, # Print help and exit "gpl" => \$gpl, # Print perlgpl page and exit "v|verbose+" => \$verbose, # Verbosity level "s|quiet" => sub {$verbose = 0}, "V|version" => \$version, "e|code=s" => \$code # Specify code ) or pod2usage(2); # Do our best to make sure that we have a code @ARGV and !$code and ($code = shift @ARGV); # Certain simple conditions mean that we cannot continue die "$verstring\n" if $version; pod2usage(-exitval => 1, -verbose => 0) if $help; pod2usage(-verbose => 2 ) if $man; exec 'perldoc perlgpl' if $gpl; pod2usage("$0: No code given.") if (!defined($code)); pod2usage("$0: No files given.") if ((@ARGV == 0) && (-t STDIN)); # Sort out colour. Only works if code is reasonably simple. if (defined($textcolour)) { my ($pre,$post,$mods); eval {$textcolour = color $textcolour}; pod2usage("$0: Couldn't figure out colour $textcolour: $@\n") if ($@); $reset = color 'reset'; pod2usage("$0: Can't mix colour and replace\n") if ($code =~ /^\s*s/); $code =~ s/^\s*m?(.)/s$1(/; $pre = $1; $code =~ s/(.)([egimosx]*)\s*$/)$1/; $post = $1; $mods = $2 . "g"; if ($pre ne $post) { $code .= $pre; } $code .= $textcolour . "\$1" . $reset . $post . $mods; } # Sanity check! $_ = "junk"; eval $code; pod2usage("Code doesn't compute: " . $code . "\n$@\n") if $@; # The default for the separator should be on, but not if we're doing a # replace $separate = (defined($replace) ? 0 : 1) unless (defined($separate)); # The way this is set up, we actually need one line more of context # after a match than we think we do. $after++; &debug(2,"Using code: $code"); # Assemble the pattern to match against to decide whether or not we # are in mathematics. # Extra environments can be specified using multiple --environments # options or as comma-separated lists after one such @extraenvs and (@extraenvs = split(/,/, join(',', @extraenvs))); @mathenvs = ("math", "displaymath","equation", "eqnarray", "align", "gather", "multline", "flalign", "alignat"); $displaymathpattern = '\\\\begin{(' . join("\\*?|", @mathenvs) . "\\*?" . ')}|\\\\\\['; $inlinemathpattern = '\\\\\('; $extramathpattern = '\\\\begin{(' . join("\\*?|", @extraenvs) . "\\*?" . ')}'; $inline and push @mathpatterns, $inlinemathpattern; $display and push @mathpatterns, $displaymathpattern; @extraenvs and push @mathpatterns, $extramathpattern; $mathpattern = join ("|", @mathpatterns); pod2usage("$0: No mathematical delimiters specified.\n") unless $mathpattern; # If we're not to search past \end{document} we need to know about # that. if ($postamble) { $mathpattern .= '|\\\\end\{document\}'; } # Set up the output routine. Returns anonymous subroutines which # decide how to process the output. # # $outsub gets passed: # a reference to an array containing the lines to output # whether or not the last match was successful # the line number of the start of the array # a reference to the filehandle to print to # a reference to an array which allows $outsub to "remember" information # # $finalout gets passed: # the current filename # the number of matches # a reference to the filehandle to print to # # Neither returns anything ($outsub, $finalout) = &outinit( defined($replace), $count, $filename, $lines, $after, $before, $separate, \$outmemory ); # These routines get called at the start and finish of processing a # file. The intention is that $presub gets us to where we want to # start matching and $postsub deals with whatever is left of the file # once we stop matching. # # $presub gets given the input filehandle, the output routine, the # output filehandle, and the output memory string; it returns the # first line of input to check and the offset to start at. # # $postsub gets given the input filehandle, the output routine, the # output filehandle, and the output memory string; it returns nothing. ($presub, $postsub) = &preinit( $preamble, $start, $after, defined($replace) ); # This sets up the input. It takes one argument, the current # filename, and returns two references to filehandles: the input and # output. $insub = &ininit( $replace ); # If we have no files, we must be using STDIN. We've already gone # through a few tests to be sure that we actually have some input so # we should be fairly safe here. unshift(@ARGV, "-") unless @ARGV; FILE: foreach $infile (@ARGV) { # Reset the number of successes at the start of each file $matchcount = 0; &debug(2,"Checking file $infile"); unless (($inhandle,$outhandle) = &$insub($infile)) { &debug(1, "Couldn't set up input for $infile, skipping"); next FILE; } unless (($line, $offset) = &$presub($inhandle,$outsub,$outhandle,\$outmemory)) { &debug(1, "No valid input in $infile, skipping"); next FILE; } # Start with some pure, pristine input; possibly with an offset # value in $offset. PROCESS: while () { if ($line =~ /^[^%]{$offset}[^%]*?($mathpattern)/) { # It's possible that this match was against an # \end{document} so we first rule out that possibility. We # don't need to condition this on '$postamble' since we # could only have matched this if '$postamble' was already # set. if ($1 eq '\end{document}') { last PROCESS; } # Otherwise, we matched the start of a math environment # after the offset (and before any comments); record where # we started and figure out how this environment should # end. $mathline = $.; $mathstart = $+[0]; $endmath = &endmath($1); # Start forming our array of lines to check @lines = ($line); # Do we have an $endmath on this line? if ($lines[0] =~ /^[^%]{$mathstart}[^%]*?($endmath)/) { # Record, for posterity, the point in the string where # this environment ended. This isn't quite the offset # that we'll need as a replace might do something a bit # funny; so we record the distance to the /end/ of the # line. $mathend = length($lines[0]) - $-[1]; } else { # No we don't so let's slurp in more input until we get # a match on the end of this particular math # environment. Record the end point as before. do { eof($inhandle) and do { &debug(1,"Input ended while waiting for $endmath"); next FILE; }; $line = <$inhandle>; push @lines, $line; # We might have gotten to the end of where we're # supposed to get to } until (($lines[$#lines] =~ /^[^%]*$endmath/) or (defined($finish) and ($. >= $finish))); # If we got a match, it will be before a comment # character so recording the point at which the # $endmath started is simple. if ($lines[$#lines] =~ /^[^%]*$endmath/) { $lines[$#lines] =~ /$endmath/; $mathend = length($lines[$#lines]) - $-[0]; } else { # If we didn't get a match but we have still # reached this point then we have reached the end # of our input. We still want to process that # which we have, though. $mathend = 0; } } # We now have a chunk of maths, we know where it started # and where it ended. Extract the bits before and after # the maths. This would be a bit neater if "-0" and "0" # were different numbers as far as substr was concerned! $premath = substr($lines[0],0,$mathstart); if ($mathend) { $postmath = substr($lines[$#lines],-$mathend); } else { $postmath = ""; } # Now set $_ to the mathematical bit in the middle. Handle # two different cases depending on how many lines of input # we have. if ($#lines == 0) { if ($mathend) { $_ = substr($lines[0],$mathstart,-$mathend); } else { $_ = substr($lines[0],$mathstart); } } else { $_ = substr($lines[0],$mathstart); for (my $i = 1; $i < $#lines; $i++) { $_ .= $lines[$i]; } if ($mathend) { $_ .= substr($lines[$#lines],0,-$mathend); } else { $_ .= $lines[$#lines]; } } # This is where we carry out the match or replacement. Had # to look at the '/usr/bin/rename' script to see how best # to handle this. Keep a record of the number of matches, # simpler to just do this than check $count for whether we # need to. ($match = eval $code) and $matchcount++; # Now we decide what to do depending on whether or not we # got a match. Basic idea is that whatever happened, we # process all but the last line of our current batch of # input. The last line of input gets saved for the next # iteration as there may be more maths on it. @lines = split(/(?<=\n)/, $premath . $_ . $postmath); $line = pop @lines; $offset = length($line) - $mathend; &$outsub(\@lines,$match,$mathline,$outhandle,\$outmemory); } else { # No more maths, so process the current line and proceed. &$outsub([$line],0,$.,$outhandle,\$outmemory); # Get the next load of input, checking that we can, of # course. if (eof($inhandle)) { last PROCESS; } $line = <$inhandle>; $offset = 0; } # Do we exit here? if (defined($finish) and ($. > $finish)) { # Yes we do, so send the current line off to be processed # and stop processing lines. &$outsub([$line],0,$.,$outhandle,\$outmemory); last PROCESS; } } # No more lines to process, but we may still be called upon to # output some more of our input. &$postsub($inhandle,$outsub,$outhandle,\$outmemory); &$finalout($infile, $matchcount, $outhandle); close $inhandle; close $outhandle unless ($outhandle eq *STDOUT{IO}); } exit; # Print helpful messages or debuging messages depending on the # verbosity level sub debug { my ($v, $m) = @_; chomp($m); $m .= "\n"; print STDERR $m if ($v <= $verbose); } # Sets up the subroutines to be run either side of the matching # process. Returns references to the subroutines. # # $pr gets given the input filehandle, the output routine, the output # filehandle, and the output memory string; it returns the first line # of input to check and the offset to start at. # # $po gets given the input filehandle, the output routine, the output # filehandle, and the output memory string; it returns nothing. sub preinit { my ($p, $s, $a, $r) = @_; my ($pr,$po); if ($p and $s) { $pr = sub { my ($fh,$o,$of,$om) = @_; return (undef, undef) if eof($fh); PREAMBLE: while (<$fh>) { if (/^[^%]*\\begin\{document\}/) { last PREAMBLE; } &$o([$_],0,$.,$of,$om); } if ($. >= $s) { return ($_,$+[0]); } while (<$fh>) { if ($. = $s) { return ($_,0); } &$o([$_],0,$.,$of,$om); } return (undef, undef); }; } elsif ($p) { $pr = sub { my ($fh, $o,$of,$om) = @_; return (undef, undef) if eof($fh); while (<$fh>) { if (/^[^%]*\\begin\{document\}/) { return ($_,$+[0]); } &$o([$_],0,$.,$of,$om); } return (undef, undef); }; } elsif ($s) { $pr = sub { my ($fh, $o,$of,$om) = @_; return (undef, undef) if eof($fh); while (<$fh>) { if ($. = $s) { return ($_,0); } &$o([$_],0,$.,$of,$om); } return (undef, undef); }; } else { $pr = sub { my ($fh, $o,$of,$om) = @_; return (undef, undef) if eof($fh); $_ = <$fh>; return ($_,0); }; } if ($r) { $po = sub { my ($fh, $o,$of,$om) = @_; while (<$fh>) { &$o([$_],0,$.,$of,$om); } return; }; } elsif ($a) { $po = sub { my ($fh, $o,$of,$om) = @_; my $l; for (my $i = 0; $i <$a; $i++) { return if eof($fh); $l = <$fh>; &$o([$l],0,$.,$of,$om); } return; }; } else { $po = sub {}; }; return ($pr, $po); } # This takes the string matched at the beginning of a math section and # returns the regexp which matches at the end. sub endmath { my ($b) = @_; my $e; $e = "\\" . $b; $e =~ s/\*/\\*/; $e =~ s/\(/\\)/; $e =~ s/\[/\\]/; $e =~ s/begin/end/; # If we're supposed to stop at an '\end{document}' then let's add # that to the '$endmath' string to be sure that we stop when we # get there. if ($postamble) { $e .= '|\\end\{document\}'; } return $e; } # This sets up the input routine. It returns a reference to a # subroutine; this subroutine takes as input the input filename and # returns references to the input and output filehandles. sub ininit { my ($r) = @_; $r and return sub { my ($i) = @_; my ($j,$p); my $b = $i . $r; if (-e $b) { unlink $b or return (undef,undef); } link $i, $b and unlink $i and open ($j, $b) and open ($p, ">$i") and return ($j,$p); return (undef,undef); }; return sub { my ($i) = @_; my $j; open ($j, $i) and return ($j, *STDOUT{IO}); return (undef,undef) } } # Set up the ouput routines. The input to the normal output # subroutine consists of: # a reference to an array of lines # whether those lines matched # the starting line number # the filehandle to print output on # a reference to a string which gives the routine "memory"; this can # be different depending on what the routine wants to remember. # # We also set up a routine to be called at the end of each file, this # takes the filename, the number of matches, and the output filehandle # as input. sub outinit { my ($r,$c,$f,$l,$a,$b,$s,$u) = @_; my ($o, $e); if ($r) { $$$u[0] = ($s ? "%---%\n" : ""); $$$u[1] = 0; $o = sub { my ($ol,$om,$os,$oh,$ou) = @_; if ($om != $$$ou[1]) { print $oh $$$ou[0]; } for (my $i = 0; $i <= $#$ol; $i++) { print $oh $$ol[$i]; } $$$ou[1] = $om; }; $e = sub {}; return ($o, $e); } if ($c and $f) { $o = sub { }; $e = sub { my ($ei, $em, $eh) = @_; print $eh $ei . ": " . $em . "\n"; }; return ($o, $e); } if (defined($f)) { $o = sub {}; $e = sub { my ($ei, $em, $eh) = @_; if (($f and $em) or (!$f and !$em)) { print $eh $ei . "\n"; } }; return ($o,$e); } if ($c) { $o = sub { }; $e = sub { my ($ei, $em, $eh) = @_; print $eh $em . "\n"; }; return ($o, $e); } # For this routine, $outmemory is a reference to an array: # 0: the separator to print between matches # 1: whether to print a separator next time # 2: an array of previous lines that we may need to print, # depending on the setting of $before # 3: the number of lines we still need to print to ensure that we # print $after lines after a match $$$u[0] = ($s ? "%---%\n" : ""); $$$u[2] = []; $o = sub { my ($ol,$om,$os,$oh,$ou) = @_; if ($om) { print $oh $$$ou[0] if ($$$ou[1]); print $oh join('', @{$$$ou[2]}); for (my $i = 0; $i <= $#$ol; $i++) { if ($l) { print $oh " " x (4 - length($os)) . $os . ":"; $os++; } print $oh $$ol[$i]; } $$$ou[3] = $a; $$$ou[2] = []; $$$ou[1] = 0; } elsif ($$$ou[3]) { if (@{$ol}) { if ($l) { print $oh " " x (4 - length($os)) . $os; print $oh ($$$ou[3] == $a ? ":" : "-"); } print $oh join('', @{$ol}); $$$ou[3]--; } } else { if (@{$ol}) { if ($l) { push @{$$$ou[2]}, " " x (4 - length($os)) . $os . "-" . $$ol[0]; } else { push @{$$$ou[2]}, $$ol[0]; } } if ($#{$$$ou[2]} >= $b) { shift @{$$$ou[2]}; $$$ou[1] = 1; } } }; $e = sub {}; return ($o, $e); } __END__ =head1 NAME mathgrep - grep through a LaTeX document looking only in math sections =head1 SYNOPSIS mathgrep [OPTION]... CODE [FILE]... Apply CODE to mathematics in each FILE or standard input. Example: mathgrep '/\\hbar/' quantum.tex classical.tex Options: -e, --code CODE Use CODE -i, --[no]inline Apply CODE to inline math -d, --[no]display Apply CODE to display math -E, --environments ENVS Apply CODE to ENVS -c, --count Only print a count of matching lines per FILE -l, --files-with-matches Only print FILE names containing matches -L, --files-without-matches Only print FILE names containing no match -n, --line-number Print line number with output lines -R, --preamble Skip preamble (before \begin{document}) -O, --postamble Skip postamble (after \end{document}) -r, --replace [EXT] Print all lines; if EXT is specified then backup FILE to FILEEXT and redirect output to FILE -S, --start NUM Start applying CODE at line NUM -F, --finish NUM Stop applying CODE at line NUM -U, --colo[u]r [COLOUR] Highlight matches in COLOUR (default: red) -P, --[no]separate Print a separator between matches -C, --context NUM Print NUM lines of output context -B, --before-context NUM Print NUM lines of leading context -A, --after-context NUM Print NUM lines of trailing context -h, -?, --help Print help message --man Print man page --gpl Print GPL -v, --verbose Increase verbosity -s, --quiet Suppress verbosity -V, --version Print version and exit =head1 DESCRIPTION B goes through the specified input FILEs (or standard input if no FILEs are given) applying CODE to any mathematics it finds. By default, B prints any lines containing mathematics on which CODE evaluated successfully. The CODE is a bit of perl code which does something to C<$_>. For grep-like performace try C. B can also do search-and-replace. The CODE is evaluated on the whole of a section of mathematics in one go, even if the mathematics splits across several lines. All the lines containing the section of mathematics are printed if CODE evaluated successfully. The options and their behaviour are as like to those of grep as I could make them. =head1 OPTIONS =over 8 =item B<-A NUM, --after-context NUM> Print NUM lines of trailing context after a line on which CODE executes successfully. =item B<-B, --before-context NUM> Print NUM lines of leading context after a line on which CODE executes successfully. =item B<-C, --context NUM> Print NUM lines of context either side of a line on which CODE executes successfully. =item B<-c, --count> Suppress normal output, instead print a count of the number of sections of mathematics on which CODE evaluated successfully. Note: this may not be the exact number of matches as CODE is applied once to each section of mathematics. =item B<-d, --[no]display> Toggles whether to look in display math environments. =item B<-E, --environments ENV[,ENV]...> Allows the user to specify a list of extra environments to look in. These are always checked. Multiple instances of this option are allowed, as are comma-separated lists. =item B<-e, --code CODE> Specifies the CODE to apply. This gives a way to ensure that the code is not swallowed up in another option. Another way is to finish the list of options with B<--> before specifying the CODE. =item B<-F, --finish NUM> Finish checking the input after NUM lines. Subsequent lines may still be printed depending on the status of the B<--after-context>, B<--context>, or B<--replace> options. =item B<-h, -?, --help> Print a summary of all the options and exit. =item B<-i, --[no]inline> Toggles whether to look in inline math environments. =item B<-l, --files-with-matches> Suppress normal output, instead just print the FILE name of a FILE on which CODE evaluated successfully at least once. Can be meaningfully combined with B<--count>. =item B<-L, --files-without-matches> Suppress normal output, instead just print the FILE name of a FILE on which CODE did not evaluate successfully at all. Cannot be meaningfully combined with B<--count>. =item B<--man> Print this manpage and exit. =item B<--gpl> Display the GNU Public License and exit (note: it actually displays the GPL that comes with Perl so if your installation of Perl is not set up correctly then this might not work). =item B<-n, --line-number> Prefix each line of output with its line number within its input file. Note: if CODE has mucked about with the number of lines in a section of mathematics then the line count may go a little awry within that section but it will correct itself again for the next section. =item B<-O, --postamble> Stop applying CODE to maths when C<\end{document}> has been reached. =item B<-P, --[no]separate> Toggle whether to print a separator between matches. Default is on, unless B<--replace> has been specified. The separator is C<%---%> so won't mess up running LaTeX on the output. =item B<-R, --preamble> Don't start applying CODE to maths until C<\begin{document}> has been reached. =item B<-r, --replace [EXT]> Prints all lines whether CODE successfully ran or not. If EXT is specified then it backups up FILE to FILEEXT and redirects the output to FILE. Similar to the B<-i> option of perl. =item B<-s, --quiet> Suppress verbosity. =item B<-S, --start NUM> Only apply CODE to maths found on or after line NUM. Note: depending on B<--before-context>, B<--context>, or B<--replace> then lines before NUM may still be printed. =item B<-U, --colo[u]r [COLOUR]> If CODE is of the form C or one of its simple variants then this option colours the matching text; red if COLOUR is not specified. =item B<-v, --verbose> Increase the verbosity level. =item B<-V, --version> Print version number and exit. =back =head1 EXAMPLES Suppose that F is \documentclass{article} \newcommand{\texthbar}{\(\hbar\)} \begin{document} The number \(\hbar\) is very small. The product, \(\hbar\hbar\) is miniscule. On the other hand, \(c\), the speed of light is very big. \[ \hbar = \frac{h}{2\pi} \] \begin{myenv} Think of hbar as your friend. \end{myenv} \end{document} Some junk involving \(\hbar\). Then the following examples produce the specified output C %---% \newcommand{\texthbar}{\(\hbar\)} %---% The number \(\hbar\) is very small. The product, \(\hbar\hbar\) is %---% \[ \hbar = \frac{h}{2\pi} \] %---% Some junk involving \(\hbar\). C \documentclass{article} \newcommand{\texthbar}{\(\hfoo\)} \begin{document} The number \(\hfoo\) is very small. The product, \(\hfoo\hfoo\) is miniscule. On the other hand, \(c\), the speed of light is very big. \[ \hfoo = \frac{h}{2\pi} \] \begin{myenv} Think of hbar as your friend. \end{myenv} \end{document} Some junk involving \(\hfoo\). Note the C option on the C, otherwise the line "The number..." would have been: The number \(\hfoo\) is very small. The product, \(\hfoo\hbar\) is C Produces no output, but F is now the altered text above and F is the original. C %---% The number \(\hbar\) is very small. The product, \(\hbar\hbar\) is %---% \[ \hbar = \frac{h}{2\pi} \] %---% Some junk involving \(\hbar\). C %---% \newcommand{\texthbar}{\(\hbar\)} %---% The number \(\hbar\) is very small. The product, \(\hbar\hbar\) is %---% \[ \hbar = \frac{h}{2\pi} \] C %---% \begin{myenv} Think of hbar as your friend. \end{myenv} =head1 NOTES This program determines what is mathematics by looking for a matched pair; for example, C<\(> and C<\)>. It does not check that the end of the pair is the correct one, just that it is the next one. Thus on encountering the text: \(x = y \text{ if \(a = b\), otherwise } x = z\) the program thinks that C is the next bit of mathematics. Consequently, it won't see the C part at all. This sort of thing only happens when the B type of mathematical environment is used inside and outside the C<\text> command. On encountering the text: \[x = y \text{ if \(a = b\), otherwise } x = z\] then the whole of the inner part is considered as mathematics. Note that a byproduct of this is that the arguments of C<\text> and C<\intertext> are viewed as being in mathematics. Another place where this might cause a problem is if you define an alias for a standard math environment: \let\myendalign\endalign \begin{align} x = y \end{myalign} then this program will continue slurping in input until it finds the next C<\end{align}>. Currently this program does not consider $ and $$ to have anything to do with mathematics. This is partly because the coding is a bit tricky (consider the text C<$x=y$$a=b$>) but also because $ and $$ are not actually LaTeX commands but are inherited from TeX. Whilst $ is synonymous with \(, $$ and \[ are actually slightly different (take a look at F if you don't believe me). Maybe if I get bored one day I'll take a look at implementing it. =head1 BUGS Whad'dya mean, "I've found a bug."? Impossible. There are no bugs, only features. To tell me about features, send an email to a.e.stacey@sheffield.ac.uk With the following in the subject line: Howay man, av fund ah borg een maathgrip. =head1 AUTHOR Andrew Stacey =head1 LICENSE Copyright (C) 2007 Andrew Stacey This program is free software; you can redistribute it and/or mod ify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =cut