summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/texdef/latexdef.pl
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/texdef/latexdef.pl')
-rwxr-xr-xMaster/texmf-dist/scripts/texdef/latexdef.pl932
1 files changed, 932 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/texdef/latexdef.pl b/Master/texmf-dist/scripts/texdef/latexdef.pl
new file mode 100755
index 00000000000..2f42e46ded3
--- /dev/null
+++ b/Master/texmf-dist/scripts/texdef/latexdef.pl
@@ -0,0 +1,932 @@
+#!/usr/bin/env perl
+################################################################################
+# texdef -- Show definitions of TeX commands
+# Copyright (c) 2011-2012 Martin Scharrer <martin@scharrer-online.de>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) 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, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+use strict;
+use warnings;
+use File::Temp qw/tempdir/;
+use File::Basename;
+use Cwd;
+
+my ($scriptname) = fileparse($0, qw(\.pl \.perl));
+
+my $TEX = 'pdflatex';
+if ($scriptname =~ /^(.*)def$/) {
+ $TEX = $1;
+}
+my $TEXOPTIONS = " -interaction nonstopmode ";
+
+## Variables for options and settings
+my $CLASS = undef;
+my $USERCLASS = 0;
+my @PACKAGES = ();
+my @OTHERDEFS = ();
+my $INPREAMBLE = 0;
+my $PRINTVERSION = 0;
+my $TMPDIR = '';
+my $SHOWVALUE = 0;
+my $ISENVIRONMENT = 0;
+my $PRINTORIGDEF = 0;
+my $FINDDEF = 0;
+my $LISTCMD = 0;
+my $LISTCMDDEF = 0;
+my $BEFORECLASS = 0;
+my $PGFKEYS = 0;
+my $PGFKEYSPLAIN = 0;
+my $FAKECMD = "\0FAKECOMMAND\0";
+my $EDIT = 0;
+my $EDITOR;
+my $EDITORCMDLN;
+my @ENVCODE = ();
+my %DEFS;
+my $LISTSTR = '@TEXDEF@LISTDEFS@'; # used as a dummy command to list definitions
+my @FILES; # List of files (sty, cls, ...)
+my @FILEORDER; # Order of files
+my %ALIAS; # list of aliases; required for registers
+my $currfile = ''; # current file name
+# Operating system:
+my $OS = $^O;
+my $WINDOWS = ($OS =~ /MSWin/);
+
+my @IGNOREDEFREG = (# List of definitions to be ignored. Can be a regex or string
+ qr/^ver\@.*\.(?:sty|cls)$/,
+ qr/^opt\@.*\.(?:sty|cls)$/,
+ qr/^reserved\@[a-z]$/,
+ qr/-h\@\@k$/,
+ qr/^catcode\d+$/,
+ qr/^l\@ngrel\@x$/,
+ qr/^\@temp/,
+ qr/^KV\@/,
+ qr/^KVO\@/,
+ qr/^KVOdyn\@/,
+ qr/^KVS\@/,
+ qr/^currfile/,
+ qr/^filehook\@atbegin\@/,
+ qr/^filehook\@atend\@/,
+ qr/^count\d+$/,
+ qr/^pgfk@\//
+);
+my %IGNOREDEF = map { $_ => 1 } qw(
+ usepackage RequirePackage documentclass LoadClass @classoptionslist
+ CurrentOption tracingassigns in@@ escapechar
+ @unprocessedoptions @let@token @gtempa @filelist @filef@und
+ @declaredoptions @currnamestack @currname @currext
+ @ifdefinable default@ds ds@ @curroptions
+ filename@area filename@base filename@ext
+);
+
+## Adds arguments to %IGNOREDEF
+sub addignore {
+ my $opt = shift;
+ my $arg = shift;
+ my @args = split (/,/, $arg);
+ if ($opt eq 'ignore-cmds') {
+ @IGNOREDEF{@args} = (1) x scalar @args;
+ }
+ else {
+ push @IGNOREDEFREG, map { qr/$_/ } @args;
+ }
+}
+
+use Getopt::Long;
+my $data = "file.dat";
+my $length = 24;
+my $verbose;
+
+my $ISLATEX = 0;
+my $ISTEX = 0;
+my $ISCONTEXT = 0;
+
+my $BEGINENVSTR = '%s';
+my $ENDENVSTR = '%s';
+
+my $VERSION = 'Version 1.7b -- 2012/05/15';
+sub usage {
+ my $option = shift;
+ my $ret = ($option) ? 0 : 1;
+print << 'EOT';
+texdef -- Show definitions of TeX commands
+Version 1.7b -- 2012/05/15
+Copyright (C) 2011-2012 Martin Scharrer <martin@scharrer-online.de>
+This program comes with ABSOLUTELY NO WARRANTY;
+This is free software, and you are welcome to redistribute it under certain conditions;
+
+Usage:
+ texdef [options] commandname [commandname ...]
+ latexdef [options] commandname [commandname ...]
+
+Other program names are possible. See the 'tex' option. Command names do not need to start with `\`.
+
+Options:
+ --tex <format>, -t <format> : Use given format of TeX: 'tex', 'latex', 'context'.
+ Variations of 'tex' and 'latex', like 'luatex', 'lualatex', 'xetex', 'xelatex' are supported.
+ The default is given by the used program name: 'texdef' -> 'tex', 'latexdef' -> 'latex', etc.
+ --source, -s : Try to show the original source code of the command definition (L).
+ --value, -v : Show value of command instead (i.e. \the\command).
+ --Environment, -E : Every command name is taken as an environment name. This will show the definition of
+ both \Macro\foo and \Macro\endfoo if \texttt{foo} is used as command name (L).
+ --preamble, -P : Show definition of the command inside the preamble.
+ --beforeclass, -B : Show definition of the command before \documentclass.
+ --package <pkg>, -p <pkg> : (M) Load given tex-file, package or module depending on whether '*tex', '*latex'
+ or 'context' is used. For LaTeX the <pkg> can start with `[<options>]` and end
+ with `<pkgname>` or `{<pkgname>}`.
+ --class <class>, -c <class> : (LaTeX only) Load given class instead of default ('article').
+ The <class> can start with `[<classs options>]` and end
+ with `<classname>` or `{<classname>}`.
+ --environment <env>, -e <env> : (M) Show definition inside the given environment <env>.
+ --othercode <code>, -o <code> : (M) Add other code into the preamble before the definition is shown.
+ This can be used to e.g. load PGF/TikZ libraries.
+ --before <code>, -b <code> : (M) Place <code> before definition is shown.
+ The <code> can be arbitray TeX code and doesn't need be be balanced.
+ --after <code>, -a <code> : (M) Place <code> after definition is shown.
+ The <code> can be arbitray TeX code and doesn't need be be balanced.
+ --find, -f : Find file where the command sequence was defined (L).
+ --Find, -F : Show full filepath of the file where the command sequence was defined (L).
+ --list, -l : List user level command sequences of the given packages (L).
+ --list-defs, -L : List user level command sequences and their shorten definitions of the given packages (L).
+ --list-all, -ll : List all command sequences of the given packages (L).
+ --list-defs-all, -LL : List all command sequences and their shorten definitions of the given packages (L).
+ --ignore-cmds <cs,cs,..>, -i : Ignore the following command sequence(s) in the above lists. (M)
+ --ignore-regex <regex,..>, -I : Ignore all command sequences in the above lists which match the given Perl regular expression(s). (M)
+ --pgf-keys, -k : Takes commands as pgfkeys and displays their definitions. Keys must use the full path but the common '.\@cmd' prefix is applied.
+ --pgf-Keys, -K : Takes commands as pgfkeys and displays their definitions. Keys must use the full path.
+ --version, -V : If used alone prints version of this script.
+ (L) Together with -p or -c prints version of LaTeX package(s) or class, respectively.
+ --edit : Opens the file holding the macro definition. Uses --Find and --source. (L)
+ If the source definition can not be found the definition is printed as normal instead.
+ --editor <editor> : Can be used to set the used editor. If not used the environment variables TEXDEF_EDITOR, EDITOR and
+ SELECTED_EDITOR are read in this order. If none of these are set a list of default
+ editors are tried. The <editor> string can include '%f' for the filename, '%n' for
+ the line number and '%%' for a literal '%'. If no '%' is used '+%n %f' is added to
+ the given command.
+ --tempdir <directory> : Use given existing directory for temporary files.
+ --help, -h : Print this help and quit.
+
+ Long option can be shorten as long the are still unique. Short options can be combined.
+ If the option 'environment', 'before' and 'after' are used toegether the
+ produced code will be inserted in the given order (reversed order for 'after').
+ (M) = This option can be given multiple times.
+ (L) = LaTeX only. Requires the packages 'filehook' and 'currfile'.
+
+Examples:
+Show the definition of '\chapter' with different classes ('article' (default), 'book' and 'scrbook'):
+
+ latexdef chapter
+ latexdef -c book chapter
+ latexdef -c scrbook chapter
+
+Show value of `\textwidth` with different class options:
+
+ latexdef -c [a4paper]{book} -v paperwidth
+ latexdef -c [letter]{book} -v paperwidth
+
+Show definition of TikZ's '\draw' outside and inside a 'tikzpicture' environment:
+
+ latexdef -p tikz draw
+ latexdef -p tikz --env tikzpicture draw
+
+Show definition of TikZ's '\draw' inside a node, inside a 'beamer' frame in 'handout' mode:
+
+ latexdef -c [handout]beamer -p tikz --env frame --env tikzpicture -b '\node {' -a '};' draw
+
+List all user level command sequences (macros) defined by the 'xspace' LaTeX package:
+
+ latexdef -l -p xspace
+
+EOT
+ exit ($ret);
+}
+
+sub envcode {
+ my $opt = shift;
+ my $arg = shift;
+ push @ENVCODE, [ $opt, $arg ];
+}
+
+## Define and process options
+Getopt::Long::Configure ("bundling");
+GetOptions (
+ 'value|v!' => \$SHOWVALUE,
+ 'Environment|E!' => \$ISENVIRONMENT,
+ 'version|V!' => \$PRINTVERSION,
+ 'tempdir=s' => \$TMPDIR,
+ 'find|f!' => sub { $FINDDEF = 1 },
+ 'Find|F!' => sub { $FINDDEF = 2 },
+ 'source|s!' => \$PRINTORIGDEF,
+ 'list|l' => sub { $LISTCMD++ },
+ 'list-def|L' => sub { $LISTCMDDEF++ },
+ 'list-all' => sub { $LISTCMD=2 },
+ 'list-def-all' => sub { $LISTCMDDEF=2 },
+ 'ignore-cmds|i=s' => \&addignore,
+ 'ignore-regex|I=s' => \&addignore,
+ 'no-list|no-l' => sub { $LISTCMD=0; $LISTCMDDEF=0; },
+ 'no-list-def|no-L' => sub { $LISTCMDDEF=0 },
+ 'no-list-all|no-ll' => sub { $LISTCMD=0; $LISTCMDDEF=0; },
+ 'no-list-def-all|no-LL' => sub { $LISTCMDDEF=0 },
+ 'preamble|P!' => \$INPREAMBLE,
+ 'beforeclass|B!' => \$BEFORECLASS,
+ 'class|c=s' => \$CLASS,
+ 'package|p=s' => \@PACKAGES,
+ 'otherdefs|o=s' => \@OTHERDEFS,
+ 'environment|e=s' => \&envcode,
+ 'before|b=s' => \&envcode,
+ 'after|a=s' => \&envcode,
+ 'tex|t=s' => \$TEX,
+ 'help|h' => \&usage,
+ 'pgf-keys|k' => \$PGFKEYS,
+ 'pgf-Keys|K' => \$PGFKEYSPLAIN,
+ 'edit!' => sub { $EDIT=1; $FINDDEF = 2; $PRINTORIGDEF = 1; },
+ 'editor=s' => \$EDITOR,
+) || usage();
+
+# usage() unless @ARGV;
+
+if ($EDIT && !$EDITOR) {
+ $EDITOR = $ENV{'TEXDEF_EDITOR'} || $ENV{'EDITOR'} || $ENV{'SELECTED_EDITOR'};
+ if (!$EDITOR) {
+ if (!$WINDOWS && exists $ENV{HOME}) {
+ # Check ~/.selected_editor file (Ubuntu)
+ my $fn = "$ENV{HOME}/.selected_editor";
+ if (-r $fn) {
+ open (my $fh, '<', $fn);
+ while (<$fh>) {
+ s/#.*//;
+ if (/^\s*SELECTED_EDITOR=(["']?)(.*)\1/) {
+ $EDITOR=$2;
+ }
+ }
+ close ($fh);
+ }
+ }
+ }
+ if (!$EDITOR) {
+ warn "No editor set. Using default!\n";
+ if ($WINDOWS) {
+ $EDITOR = 'texworks "%f"';
+ }
+ else {
+ for my $ed (qw(/usr/bin/vim /usr/bin/emacs /usr/bin/nano)) {
+ if (-x $ed) {
+ $EDITOR = $ed;
+ last;
+ }
+ }
+ if (!$EDITOR) {
+ for my $ed (qw(/usr/bin/editor /usr/bin/open /bin/open)) {
+ if (-x $ed) {
+ $EDITOR = "$ed \"%f\"";
+ last;
+ }
+ }
+ }
+ }
+ }
+ if (!$EDITOR) {
+ warn "No suitable editor found. Disable editing!\n";
+ $EDIT = 0;
+ }
+}
+
+## Format specific settings
+if ($TEX =~ /latex$/) {
+ $ISLATEX = 1;
+ $BEGINENVSTR = '\begin{%s}' . "\n";
+ $ENDENVSTR = '\end{%s}' . "\n";
+}
+elsif ($TEX =~ /tex$/) {
+ $ISTEX = 1;
+ $BEGINENVSTR = '\%s' . "\n";
+ $ENDENVSTR = '\end%s' . "\n";
+}
+elsif ($TEX =~ /context$/) {
+ $ISCONTEXT = 1;
+ $BEGINENVSTR = '\start%s' . "\n";
+ $ENDENVSTR = '\stop%s' . "\n";
+}
+
+$USERCLASS = $CLASS;
+$CLASS = 'article' if not $CLASS;
+$CLASS =~ /^(?:\[(.*)\])?{?(.*?)}?$/;
+$CLASS = $2;
+my $CLASSOPTIONS = $1 || '';
+
+if ($FINDDEF == 1 && !$ISLATEX) { die "Error: The --find / -f option is only implemented for LaTeX!\n"; }
+if ($FINDDEF == 2 && !$ISLATEX) { die "Error: The --Find / -F option is only implemented for LaTeX!\n"; }
+
+my @cmds = @ARGV;
+$LISTCMD = $LISTCMDDEF if $LISTCMDDEF;
+if ($LISTCMD && !$ISLATEX) { die "Error: Listing for commands is only implemented for LaTeX!\n"; }
+if ($LISTCMD) {
+ @cmds = ($LISTSTR);
+}
+
+if ($PRINTVERSION) {
+ if (!@PACKAGES && !$USERCLASS) {
+ print STDERR "texdef: $VERSION\n";
+ exit (0) if not @cmds;
+ }
+ elsif (!@cmds) {
+ @cmds = ($FAKECMD);
+ }
+}
+
+sub print_versions {
+ my @files = map { $_.'.sty' } @PACKAGES;
+ unshift @files, "$USERCLASS.cls" if $USERCLASS;
+
+ foreach my $file (@files) {
+ print "\\message{^^J:TEXDEF: $file: \\csname ver\@$file\\endcsname^^J}%\n";
+ }
+ $PRINTVERSION = 0;# only print it once
+}
+
+usage() if not @cmds;
+
+my $cwd = getcwd();
+my $DIRSEP;
+if ($WINDOWS) {
+ $DIRSEP = ';';
+} else {
+ $DIRSEP = ':';
+}
+$ENV{TEXINPUTS} = '.' . $DIRSEP . $cwd . $DIRSEP . ($ENV{TEXINPUTS} || '');
+
+if (!$TMPDIR) {
+ $TMPDIR = tempdir( 'texdef_XXXXXX', CLEANUP => 1, TMPDIR => 1 );
+}
+chdir $TMPDIR or die "Couldn't change into temporary directory '$TMPDIR'\n";
+my $TMPFILE = 'texdeftmp.tex';
+
+sub testdef {
+ my $cmd = shift;
+ my $def = shift;
+ if ($def eq 'macro:->\@latex@error {Can be used only in preamble}\@eha ' && $cmd ne '\@notprerr') {
+ unshift @cmds, '^' . $cmd;
+ }
+ elsif ($def =~ /^(?:\\[a-z]+ )?macro:.*?>(.*)/) {
+ my $macrodef = $1;
+ if ($macrodef =~ /^\\protect (.*?) ?$/) {
+ my $protectedmacro = $1;
+ unshift @cmds, $protectedmacro;
+ }
+ elsif ($macrodef =~ /^\\x\@protect (.*?) ?\\protect (.*?) ?$/) {
+ my $protectedmacro = $2;
+ unshift @cmds, $protectedmacro;
+ }
+ elsif ($macrodef =~ /^\\\@protected\@testopt {?\\.*? }? *(\\\\.*?) /) {
+ unshift @cmds, $1;
+ }
+ elsif ($macrodef =~ /^\\\@testopt {?(\\.*?) }?/) {
+ unshift @cmds, $1;
+ }
+ }
+ elsif ($def =~ /^\\(char|mathchar)|(dimen|skip|muskip|count)\d/) {
+ unshift @cmds, '#' . $cmd;
+ }
+}
+
+my $bschar = 0;
+my $pcchar = 0;
+my $lbchar = 0;
+my $rbchar = 0;
+
+sub special_chars {
+ return if (!$bschar && !$pcchar && !$lbchar && !$rbchar);
+ print '\begingroup'."\n";
+ if ($bschar) {
+ print '\lccode`.=92 \lowercase{\expandafter\gdef\csname @backslashchar\endcsname{.}}'."\n";
+ }
+ if ($pcchar) {
+ print '\lccode`.=37 \lowercase{\expandafter\gdef\csname @percentchar\endcsname{.}}'."\n";
+ }
+ if ($lbchar) {
+ print '\lccode`.=123 \lowercase{\expandafter\gdef\csname @charlb\endcsname{.}}'."\n";
+ }
+ if ($rbchar) {
+ print '\lccode`.=125 \lowercase{\expandafter\gdef\csname @charrb\endcsname{.}}'."\n";
+ }
+ print '\endgroup'."\n";
+}
+
+#######################################################################################################################
+## Loop around given commands ##
+#######################################################################################################################
+while (my $cmd = shift @cmds) {
+
+next if $cmd eq '';
+my $origcmd = $cmd;
+my $showvalue;
+my $inpreamble;
+if ($cmd ne $FAKECMD) {
+if ($PGFKEYS) {
+ $cmd = "pgfk\@$cmd/.\@cmd";
+ push @PACKAGES, "pgfkeys";
+}
+if ($PGFKEYSPLAIN) {
+ $cmd = "pgfk\@$cmd";
+ push @PACKAGES, "pgfkeys";
+}
+if (length ($cmd) > 1) {
+ $cmd =~ s/^([#^])?\\?//;
+ my $type = $1 || '';
+ $showvalue = $type eq '#';
+ $inpreamble = $type eq '^';
+}
+$bschar = $cmd =~ s/\\/\\csname\0\@backslashchar\\endcsname\0/g;
+$pcchar = $cmd =~ s/\%/\\csname\0\@percentchar\\endcsname\0/g;
+$lbchar = $cmd =~ s/\{/\\csname\0\@charlb\\endcsname\0/g;
+$rbchar = $cmd =~ s/\}/\\csname\0\@charrb\\endcsname\0/g;
+$cmd =~ s/\s/\\space /g;
+$cmd =~ s/\0/ /g;
+}
+
+open (my $tmpfile, '>', $TMPFILE);
+select $tmpfile;
+
+print "\\nonstopmode\n";
+
+if ($ISLATEX) {
+ #print "\\nofiles\n";
+ if ($FINDDEF || $LISTCMD || $PRINTORIGDEF) {
+ # Load the 'filehook' and 'currfile' packages without 'kvoptions' by providing dummy definitions for the 'currfile' options:
+ print '\makeatletter\expandafter\def\csname ver@kvoptions.sty\endcsname{0000/00/00}\let\SetupKeyvalOptions\@gobble';
+ print '\newcommand\DeclareStringOption[2][]{}';
+ print '\newcommand\DeclareBoolOption[2][false]{\expandafter\newif\csname ifcurrfile@#2\endcsname\csname currfile@#2#1\endcsname}';
+ print '\let\DeclareVoidOption\@gobbletwo\def\ProcessKeyvalOptions{\@ifstar{}{}}';
+ print '\def\currfile@mainext{tex}\def\currfile@maindir{\@currdir}\let\ifcurrfile@fink\iffalse\makeatother';
+ print "\\RequirePackage{filehook}\n";
+ print "\\RequirePackage{currfile}\n";
+ print '\makeatletter\expandafter\let\csname ver@kvoptions.sty\endcsname\relax\let\SetupKeyvalOptions\@undefined\let\DeclareBoolOption\@undefined\let\DeclareStringOption\@undefined';
+ print '\let\DeclareVoidOption\@undefined\let\ProcessKeyvalOptions\@undefined\makeatother';
+ }
+ if ($FINDDEF || $PRINTORIGDEF) {
+ print '{\expandafter}\expandafter\ifx\csname ' . $cmd . '\expandafter\endcsname\csname @undefined\endcsname' . "\n";
+ print '\AtBeginOfFiles{{{\expandafter}\expandafter\ifx\csname ' . $cmd . '\expandafter\endcsname\csname @undefined\endcsname\else' . "\n";
+ print ' \ClearHook\AtBeginOfFiles{}\relax';
+ print ' \ClearHook\AtEndOfFiles{}\relax';
+ # Get parent filename
+ print ' \csname currfile@pop\endcsname';
+ print ' {\message{^^J:: \expandafter\string\csname '.$cmd.'\endcsname\space first defined in "\currfilename".^^J}}\fi}}', "\n";
+ print ' \csname currfile@push\endcsname';
+ print '\AtEndOfFiles{{{\expandafter}\expandafter\ifx\csname ' . $cmd . '\expandafter\endcsname\csname @undefined\endcsname\else' . "\n";
+ print ' \ClearHook\AtBeginOfFiles{}\relax';
+ print ' \ClearHook\AtEndOfFiles{}\relax';
+ print ' {\message{^^J:: \expandafter\string\csname '.$cmd.'\endcsname\space first defined in "\currfilename".^^J}}\fi}}', "\n";
+ print '\else'. "\n";
+ print ' {\message{^^J:: \expandafter\string\csname '.$cmd.'\endcsname\space is defined by (La)TeX.^^J}}', "\n";
+ print '\fi'. "\n";
+ }
+ if ($LISTCMD) {
+ print '\AtBeginOfEveryFile{\message{^^J>> entering file "\currfilename"^^J}}'."\n";
+ print '\AtEndOfEveryFile{\message{^^J<< leaving file "\currfilename"^^J}}'."\n";
+ }
+ if (!$BEFORECLASS) {
+ print "\\documentclass[$CLASSOPTIONS]{$CLASS}\n";
+ if ($LISTCMD) {
+ print '\tracingonline=1\relax'. "\n";
+ print '\tracingassigns=1\relax'. "\n";
+ }
+
+ foreach my $pkg (@PACKAGES) {
+ $pkg =~ /^(?:\[(.*)\])?{?(.*?)}?$/;
+ my ($pkgname,$pkgoptions) = ($2, $1 || '');
+ print "\\usepackage[$pkgoptions]{$pkgname}\n";
+ }
+ {
+ local $, = "\n";
+ print @OTHERDEFS, '';
+ }
+ unless ($inpreamble || $INPREAMBLE) {
+ print_versions if $PRINTVERSION;
+ print '\tracingonline=0\relax'. "\n";
+ print '\tracingassigns=0\relax'. "\n";
+ print "\\begin{document}\n";
+ }
+ }
+ else {
+ if ($LISTCMD) {
+ print '\tracingonline=1\relax'. "\n";
+ print '\tracingassigns=1\relax'. "\n";
+ }
+ }
+}
+elsif ($ISCONTEXT) {
+ foreach my $pkgname (@PACKAGES) {
+ print "\\usemodule[$pkgname]\n";
+ }
+ {
+ local $, = "\n";
+ print @OTHERDEFS, '';
+ }
+ &special_chars();
+ print "\\starttext\n" unless $inpreamble || $INPREAMBLE;
+}
+elsif ($ISTEX) {
+ foreach my $pkgname (@PACKAGES) {
+ print "\\input $pkgname \n";
+ }
+ {
+ local $, = "\n";
+ print @OTHERDEFS, '';
+ }
+ &special_chars();
+}
+
+foreach my $envc (@ENVCODE) {
+ my ($envtype,$env) = @$envc;
+ if ($envtype eq 'environment') {
+ printf $BEGINENVSTR, $env;
+ }
+ elsif ($envtype eq 'before') {
+ print "$env\n";
+ }
+}
+
+if (!$LISTCMD && $cmd ne $FAKECMD) {
+
+print '\immediate\write0{==============================================================================}%'."\n";
+if (length ($cmd) > 1) {
+if ($showvalue || $SHOWVALUE) {
+ print '\immediate\write0{\string\the\expandafter\string\csname ', $cmd, '\endcsname}%'."\n";
+ print '\immediate\write0{------------------------------------------------------------------------------}%'."\n";
+ print '\immediate\write0{\expandafter\the\csname ', $cmd, '\endcsname}%'."\n";
+} else {
+ print '\begingroup';
+ print '\immediate\write0{\expandafter\string\csname ', $cmd, '\endcsname}%'."\n";
+ print '\immediate\write0{------------------------------------------------------------------------------}%'."\n";
+ print '\expandafter\endgroup\expandafter\immediate\expandafter\write\expandafter0\expandafter{\expandafter\meaning\csname ', $cmd, '\endcsname}%'."\n";
+}
+}
+else {
+if ($showvalue || $SHOWVALUE) {
+ print '\immediate\write0{\string\the\string\\', $cmd, '}%'."\n";
+ print '\immediate\write0{------------------------------------------------------------------------------}%'."\n";
+ print '\immediate\write0{\the\\', $cmd, '}%'."\n";
+} else {
+ print '\immediate\write0{\string\\', $cmd, '}%'."\n";
+ print '\immediate\write0{------------------------------------------------------------------------------}%'."\n";
+ print '\immediate\write0{\meaning\\', $cmd, '}%'."\n";
+}
+}
+print '\immediate\write0{==============================================================================}%'."\n";
+
+}
+
+foreach my $envc (reverse @ENVCODE) {
+ my ($envtype,$env) = @$envc;
+ if ($envtype eq 'environment') {
+ printf $ENDENVSTR, $env;
+ }
+ elsif ($envtype eq 'after') {
+ print "$env\n";
+ }
+}
+
+if ($ISLATEX) {
+ print "\\documentclass[$CLASSOPTIONS]{$CLASS}\n" if $BEFORECLASS;
+ if ($inpreamble || $INPREAMBLE || $BEFORECLASS) {
+ print_versions if $PRINTVERSION;
+ print '\tracingonline=0\relax'. "\n";
+ print '\tracingassigns=0\relax'. "\n";
+ print "\\begin{document}\n"
+ }
+ print "\\end{document}\n";
+}
+elsif ($ISCONTEXT) {
+ print "\\starttext\n" if $inpreamble || $INPREAMBLE;
+ print "\\stoptext\n";
+}
+elsif ($ISTEX) {
+ print "\\bye\n";
+}
+
+close ($tmpfile);
+
+select STDOUT;
+
+# Removes all '{' and '}' characters which no real braces.
+sub remove_invalid_braces {
+ $_[0] =~ s/\\[\\%]//g; # remove \\ and \%
+ $_[0] =~ s/%.*$//; # remove line comments
+ $_[0] =~ s/\\[{}]//g; # remove \{ and \}
+}
+
+sub env_braces {
+ my $line = shift;
+ remove_invalid_braces $line;
+ my $level = shift || 0;
+ my $count = shift || 0;
+ for my $char (split //, $line) {
+ if ($char eq '{') {
+ $level++;
+ }
+ elsif ($char eq '}') {
+ $level--;
+ if ($level == 0) {
+ $count++;
+ }
+ }
+ }
+ return ($level, $count);
+}
+
+sub call_editor {
+ my $path = shift;
+ my $linenumber = shift;
+ print "Opening file '$path', line $linenumber.\n";
+ if ($EDITOR =~ /%/) {
+ $EDITOR =~ s/%%/\000/;
+ $EDITOR =~ s/%f/$path/;
+ $EDITOR =~ s/%n/$linenumber/;
+ $EDITOR =~ s/\000/%/;
+ system($EDITOR);
+ }
+ else {
+ system($EDITOR, "+$linenumber", $path);
+ }
+}
+
+sub print_orig_def {
+ my $rmacroname = shift;
+ my $file = shift;
+ my $path = shift;
+ my $linenumber;
+ my $found = 0;
+ open (my $fh, '<', $path) or return;
+ my $rmacrodef = qr/
+ ^ # Begin of line (no whitespaces!)
+ (
+ (?:(?:\\global|\\long|\\protected|\\outer)\s*)* # Prefixes (maybe with whitespace between them)
+ )
+ \\(
+ (?:[gex]?def) \s* \\ # TeX definitions
+ | (?:new|renew|provide)command\s* \*? \s* {? \s* \\ # LaTeX definitions
+ | (?:new|renew|provide)robustcmd\s* \*? \s* {? \s* \\ # etoolbox definitions
+ | (?:new(?:box|count|dimen|if|insert|read|skip|muskip|toks|write)) \s* \\ # TeX registers etc.
+ | (?:char|count|dimen|mathchar|skip|toks)def \s* \\ # TeX chardefs etc.
+ | \@namedef{? # Definition by name only
+ | Declare[a-zA-z]+ \s* \*? \s* {? \s* \\ # Declare... definitions
+ | declare[a-zA-z]+ \s* \*? \s* {? \s* \\ # declare... definitions
+ )
+ $rmacroname # Macro name without backslash
+ [^a-zA-Z@]
+ /xms;
+ my $rmacrolet = qr/
+ ^ # Begin of line (no whitespaces!)
+ (?:global\s*)? # Prefixes (maybe with whitespace between them)
+ \\let \s* \\ # let
+ $rmacroname # Macro name without backslash
+ \s* =? # Optional '='
+ \s* \\ ([a-zA-Z@]+) # Second macro
+ /xms;
+ my $renvdef = qr/
+ ^ # Begin of line (no whitespaces!)
+ \\(
+ (?:new|renew|provide)environment\s* { \s* # LaTeX definitions
+ )
+ ($rmacroname) # Environment names follow same rules as macro names
+ \s* } # closing brace
+ (.*) # Rest of line
+ /xms;
+ while (my $line = <$fh>) {
+ if ($line =~ $rmacrodef) {
+ my $defcmd = $1;
+ $found = 1;
+ $linenumber = $.;
+ if ($EDIT) {
+ call_editor($path, $linenumber);
+ last;
+ }
+ print "% $file, line $linenumber:\n";
+ print $line;
+ last if $defcmd =~ /^(?:new(?:box|count|dimen|if|insert|read|skip|muskip|toks|write))/;
+ last if $defcmd =~ /^(?:char|count|dimen|mathchar|skip|toks)def/;
+ remove_invalid_braces $line;
+ my $obrace = $line =~ tr/{/{/;
+ my $cbrace = $line =~ tr/}/}/;
+ while ($obrace != $cbrace) {
+ my $line = <$fh>;
+ print $line;
+ remove_invalid_braces $line;
+ $obrace += $line =~ tr/{/{/;
+ $cbrace += $line =~ tr/}/}/;
+ }
+ print "\n";
+ last;
+ }
+ elsif ($line =~ $rmacrolet) {
+ my $letcmd = $1;
+ $found = 1;
+ $linenumber = $.;
+ if ($EDIT) {
+ call_editor($path, $linenumber);
+ last;
+ }
+ print "% $file, line $linenumber:\n";
+ print $line;
+ print "\n";
+ unshift @cmds, $letcmd;
+ last;
+ }
+ elsif ($line =~ $renvdef) {
+ $found = 2;
+ $linenumber = $.;
+ if ($EDIT) {
+ call_editor($path, $linenumber);
+ last;
+ }
+ print "% $file, line $linenumber:\n";
+ print $line;
+ my ($level, $count) = env_braces $line;
+ while ($count < 3) {
+ my $line = <$fh>;
+ print $line;
+ ($level, $count) = env_braces $line, $level, $count;
+ }
+ print "\n";
+ last;
+ }
+ }
+ close($fh);
+ return $found;
+}
+
+open (my $texpipe, '-|', "$TEX $TEXOPTIONS \"$TMPFILE\" ");
+
+my $name = '';
+my $definition = '';
+my $errormsg = '';
+my $origdeffound = 0;
+
+while (<$texpipe>) {
+ if (/^::\s*(.*)/) {
+ my $line = $1;
+ if ($FINDDEF == 2) {
+ if ($line =~ /first defined in "(.*)"/) {
+ my $path = `kpsewhich "$1"`;
+ chomp $path;
+ $line =~ s/$1/$path/;
+ }
+ }
+ if ($PRINTORIGDEF) {
+ if ($line =~ /first defined in "(.*)"/) {
+ my $file = $1;
+ my $path = `kpsewhich "$file"`;
+ chomp $path;
+ $origdeffound = print_orig_def($cmd, $file, $path);
+ }
+ elsif ($line =~ /is defined by \(La\)TeX./) {
+ my $file = 'latex.ltx';
+ my $path = `kpsewhich "$file"`;
+ chomp $path;
+ $file = $path if $FINDDEF > 1;
+ $origdeffound = print_orig_def($cmd, $file, $path);
+ }
+ if (!$origdeffound) {
+ print "Source code definition of '$origcmd' could not be found.\n";
+ print "$line\n";
+ }
+ }
+ else {
+ print "$line\n";
+ }
+ next;
+ }
+ if (/^(:TEXDEF:\s*(.*))/) {
+ my $line = $1;
+ chomp ($line);
+ my $text = $2;
+ while (length $line >= 79) {
+ $line = <$texpipe>;
+ chomp ($line);
+ $text .= $line;
+ }
+ print "$text\n";
+ next;
+ }
+ if ($LISTCMD) {
+ if (/^>> entering file "(.*)"$/) {
+ push @FILES, $currfile;
+ $currfile = $1;
+ push @FILEORDER, $currfile if not exists $DEFS{$currfile};
+ }
+ elsif (/^<< leaving file "(.*)"$/) {
+ $currfile = pop @FILES;
+ }
+ elsif (/^{(?:into|reassigning) \s*(.*)}?$/) {
+ my ($cs, $def) = split (/=/, $1, 2);
+ $cs =~ s/^\\//;
+ $def =~ s/\}$//;
+ if ($LISTCMD > 1 || $cs !~ /[@ ]/) {
+ if ($def =~ /^\\((?:skip|count|toks|muskip|box|dimen)\d+)$/) {
+ $ALIAS{$1} = $cs;
+ }
+ elsif (exists $ALIAS{$cs}) {
+ $cs = $ALIAS{$cs}
+ }
+ $DEFS{$currfile}{$cs} = $def;
+ }
+ }
+ }
+ last if /^=+$/;
+ if ($_ =~ /^!\s*(.*)/ && !$errormsg) {
+ chomp;
+ my $line = $1;
+ $errormsg = $line;
+ while (length $line >= 79) {
+ $line = <$texpipe>;
+ chomp $line;
+ $errormsg .= $line;
+ }
+ }
+}
+while (<$texpipe>) {
+ last if /^-+$/;
+ next if /^$/;
+ chomp;
+ $name .= $_;
+}
+while (<$texpipe>) {
+ last if /^=+$/;
+ next if /^$/;
+ chomp;
+ $definition .= $_;
+ if ($_ =~ /^!\s*(.*)/ && !$errormsg) {
+ $errormsg = $1;
+ }
+}
+while (<$texpipe>) {
+ if ($_ =~ /^!\s*(.*)/ && !$errormsg) {
+ chomp;
+ my $line = $1;
+ $errormsg = $line;
+ while (length $line >= 79) {
+ $line = <$texpipe>;
+ chomp $line;
+ $errormsg .= $line;
+ }
+ }
+}
+close ($texpipe);
+
+my $error = $? >> 8;
+
+if ($error) {
+ if ( ($SHOWVALUE || $showvalue) && ($errormsg =~ /^You can't use `.*' after \\the\./) ) {
+ print STDERR "\n$name:\nError: Given command sequence does not contain a value.\n\n";
+ }
+ else {
+ print STDERR "\n$name:\nCompile error: $errormsg\n\n";
+ }
+ next;
+}
+
+#last if $PRINTORIGDEF && $origdeffound && $EDIT;
+
+next if $cmd eq $FAKECMD;
+if ($cmd eq $LISTSTR) {
+ foreach $currfile (@FILEORDER) {
+ next if not keys %{$DEFS{$currfile}};
+ print "\nDefined by file '$currfile':\n";
+ CMD:
+ foreach my $cmd (sort keys %{$DEFS{$currfile}}) {
+ next CMD if exists $IGNOREDEF{$cmd};
+ foreach my $pattern (@IGNOREDEFREG) {
+ next CMD if $cmd =~ $pattern;
+ }
+ print "\\$cmd";
+ print ": $DEFS{$currfile}{$cmd}" if ($LISTCMDDEF);
+ print "\n";
+ }
+ }
+} elsif (!$PRINTORIGDEF || !$origdeffound) {
+ print "\n(in preamble)" if $inpreamble;
+ print "\n$name:\n$definition\n\n";
+}
+
+if (!($PRINTORIGDEF && $origdeffound)) {
+ testdef($origcmd,$definition);
+}
+if ($ISENVIRONMENT && $origdeffound < 2 && $cmd !~ /^end/) {
+ unshift @cmds, 'end' . $cmd;
+}
+
+}
+
+chdir $cwd;
+__END__