#!/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