#!/usr/bin/env perl
# mktexcommandlist.pl – generating a command list for all installed packages
# Copyright (C) 2013 Markus Kohm
#
# 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 .
#-----------------------------------------------------------------------------
use strict;
use Getopt::Long qw(:config bundling permute auto_version auto_help);
use File::Find;
use DB_File;
use Time::gmtime;
my $id=q$Id: mktexcommandlist.pl 8 2013-07-15 08:02:45Z mjk $;
my $prgname='unknown';
$prgname=$1
if $id =~ /^\S+:\s(\S*)\s.*$/u;
our $VERSION='unknown';
$VERSION=$1
if $id =~ /^\S+:\s\S*\s(\d*)\s.*$/u;
my $opt_latex;
my $opt_generic;
exit 1 if not GetOptions ( 'latex|l!' => \$opt_latex,
'generic|g!' => \$opt_generic );
my $do_cmd;
if ( $#ARGV < 0 ) {
print STDERR "Warning: No command, nothing to do. Try --help to get more information.\n";
exit 0;
}
our $texmfroot;
our %files;
our %packages;
if ( $ARGV[0] eq 'createdb' ) {
create_db();
} elsif ( $ARGV[0] eq 'find' ) {
find_command( $ARGV[1] );
} elsif ( $ARGV[0] eq 'createhtml' ) {
create_command_list_html();
} else {
die "Unknon command '$ARGV[0]'\n";
exit 1;
}
exit 0;
sub find_command() {
my $cmd=shift;
chomp($cmd);
die "Command find expects a value. Try --help to get more information.\n"
if ( $cmd eq '' );
my $files_info = new DB_File::HASHINFO;
tie %files, "DB_File", "cmdfiles.db", O_RDONLY, 0666, $files_info
or die "Cannot open data base file 'cmdfiles.db': $!\n";
my $packages_info = new DB_File::HASHINFO;
tie %packages,"DB_File", "filepackage.db", O_RDONLY, 0666, $packages_info
or die "Cannot open data base file 'filepackages.db': $!\n";
print "$cmd\n";
my %foundatpackages;
if ( $files{$cmd} ) {
map {
if ( exists $foundatpackages{$packages{$_}} ) {
$foundatpackages{$packages{$_}} =
"$foundatpackages{$packages{$_}}\t$_";
} else {
$foundatpackages{$packages{$_}} =
"$_";
}
} split /,/,$files{$cmd};
}
foreach my $package (sort (keys( %foundatpackages ) ) ) {
print " TeX Live package $package:\n";
map {
print "\t$_\n";
} sort( split /\t/,$foundatpackages{$package} );
}
untie %packages;
untie %files;
}
sub create_command_list_html() {
my $gm=gmtime();
my $lastletter='';
my $files_info = new DB_File::HASHINFO;
tie %files, "DB_File", "cmdfiles.db", O_RDONLY, 0666, $files_info
or die "Cannot open data base file 'cmdfiles.db': $!\n";
my $packages_info = new DB_File::HASHINFO;
tie %packages,"DB_File", "filepackage.db", O_RDONLY, 0666, $packages_info
or die "Cannot open data base file 'filepackages.db': $!\n";
print '
';
print "\n
\n";
print "\n";
print "\n";
print "\n";
print "\n";
printf "\n",
$gm->year + 1900, $gm->mon+1, $gm->mday, $gm->hour, $gm->min, $gm->sec;
print "(La)TeX Command List\n\n\n";
print "(La)TeX Command List
\n";
print "This page has been generated using $prgname $VERSION. It shows a list of (La)TeX commands implemented by the packages of TeX Live with the corresponding TeX Live package and the files within those packages.
\n";
my $letters='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
my $i=0;
print "\n";
while ( $i < 52 ) {
print '',substr($letters,$i,1),' ';
$i += 1;
}
print "
\n";
foreach my $cmd (sort (keys %files)) {
if ( $lastletter ne substr($cmd,1,1) ) {
print "\n" if ( $lastletter ne '' );
$lastletter=substr($cmd,1,1);
print "$lastletter
\n\n";
}
print "$cmd
- ";
my %foundatpackages;
if ( $files{$cmd} ) {
map {
if ( exists $foundatpackages{$packages{$_}} ) {
$foundatpackages{$packages{$_}} =
"$foundatpackages{$packages{$_}}\t$_";
} else {
$foundatpackages{$packages{$_}} =
"$_";
}
} split /,/,$files{$cmd};
foreach my $package (sort (keys( %foundatpackages ) ) ) {
print "
$package:
\n \n";
map {
print " - $_
\n";
} sort( split /\t/,$foundatpackages{$package} );
print "
\n";
}
}
print " \n";
}
print "
\n";
print "\n";
untie %packages;
untie %files;
}
sub create_db() {
if ( ( $opt_latex != 1 ) && ( $opt_generic != 1 ) ) {
print STDERR "Warning: Neither --latex nor --generic using both.\n";
$opt_latex = 1;
$opt_generic = 1;
}
$texmfroot=qx{kpsewhich -var-value=TEXMFROOT}
|| die "Cannot determine TEXMFROOT\n";
chomp($texmfroot);
my $texmfdist=qx{kpsewhich -var-value=TEXMFDIST}
|| die "Cannot determine TEXMFDIST\n";
chomp($texmfdist);
print STDERR "TEXMFDIST: $texmfdist\n";
my $files_info = new DB_File::HASHINFO;
unlink "cmdfiles.db";
tie %files, "DB_File", "cmdfiles.db", O_RDWR|O_CREAT, 0666, $files_info
or die "Cannot open data base file 'cmdfiles.db': $!\n";
my $packages_info = new DB_File::HASHINFO;
unlink "filepackage.db";
tie %packages,"DB_File", "filepackage.db", O_RDWR|O_CREAT, 0666, $packages_info
or die "Cannot open data base file 'filepackages.db': $!\n";
my $topdir="$texmfdist/tex/generic";
find( { wanted => \&process_file,
follow_skip => 2 }, "$topdir" ) if ( $opt_generic == 1 );
$topdir="$texmfdist/tex/latex";
find( { wanted => \&process_file,
follow_skip => 2 }, "$topdir" ) if ( $opt_latex == 1 );
print STDERR "\n";
untie %packages;
untie %files;
}
sub process_file() {
if ( -T && -r ) {
print STDOUT " \r", substr($File::Find::name,-79,79), "\r";
my $filename = $1
if ( $File::Find::name =~ /^\Q$texmfroot\/\E(.*)$/ );
chomp( $filename );
if ( $filename ne '' ) {
my ($package,$file)
= split(/:/,qx{tlmgr search --file "^$filename\$"});
if ( $package && ( $package ne '' ) ) {
$packages{$filename}="$package";
open( my $file, "<", $File::Find::name )
or die "\nCannot open $File::Find::name: $!\n";
map {
my $ts = $_;
my $cmd;
if ( $ts =~ /\\def\s?(\\[A-Za-z]+)[\s#\{]/ ) {
$cmd=$1
if ( "$1" ne '\csname' );
} elsif ( $ts =~ /\\(let|newif)\s?(\\[A-Za-z]+)[\s\\]/ ) {
$cmd=$2
if ( "$2" ne '\csname' );
} elsif ( $ts =~ /\\(DeclareRobustC|providec|newc)ommand\*?\s*\{(\\[A-Za-z]+)[\s\}]/ ) {
$cmd =$2
if ( "$2" ne '\csname' );
} elsif ( $ts =~ /\\(DeclareRobustC|providec|newc)ommand\*?\s*(\\[A-Za-z]+)[\s\[\{]/ ) {
$cmd =$2
if ( "$2" ne '\csname' );
} else {
# print "$ts\n";
}
if ( $cmd ) {
if ( $files{$cmd} ) {
# print "$cmd already $files{$cmd}\n";
my $i=0;
my $found=0;
foreach (split /,/,$files{$cmd}) {
$found = 1
if ( $_ eq $filename );
}
$files{$cmd} = "$files{$cmd},$filename"
if ( $found == 0 );
# print "$cmd now $files{$cmd}\n";
} else {
$files{$cmd}=$filename;
}
}
} grep /\\(def\s?\\[A-Za-z]+[\s#\{]|(let|newif)\s?\\[A-Za-z]+[\s\\]|(DeclareRobustC|newc|providec)ommand\*?\s*(\{\\[A-Za-z]*[\s\}]|\\[A-Za-z]*[\s\{\[]))/ , <$file>;
close $file;
} else {
print STDERR "Ignorring file $filename, that is not part of any package!\n";
}
} else {
die 'Internal error: cannot determine file name!\n';
}
}
}
__END__
=head1 NAME
mktexcommandlist – generating a list of all commands, the defining files and
the corresponding TeX Live package
=head1 SYNOPSIS
mktexcommandlist command [options]
Commands:
createdb generate cmdfiles.db and filepackage.db for later searchs
createhtml generate texcommands.html (needs cmdfiles.db and
filepackage.db)
find search for TeX command (needs cmdfiles.db and
filepackage.db)
createdb options:
--generic|-g generate the databases for generic commands
--latex|-l generate the databases for LaTeX commands
general options:
--help brief help message
--version the version
By default both, LaTeX and generic files will be seached for commands.
=head1 COMMANDS
=over 4
=item B
Create a Berkeley database F which maps each TeX command to a list
of files, that define the command, and F, that maps each file
to a TeX Live package. These database files are needed by the other commands.
=item B
Create a html file with all TeX commands, corresponding TeX Live packages and
files. Needs F and F (see L).
=item B F<>
Search for files and TeX Live packages, that define the given F<>.
Needs F and F (see L).
=back
=head1 OPTIONS
=over 4
=item B<--generic|-g>
Generate the databases for all commands found in generic files.
=item B<--latex|-l>
Generate the databases for all commands found in LaTeX files.
=item B<--help>
Show a brief help message and terminate.
=item B<--version>
Show a the version information only and terminate.
=back
=head1 DESCRIPTION
B generates a command list, that may be used to find the file, that provides the command, and the TeX Live package, that provides this file.
=cut