#!/usr/bin/env perl # File : makeglossaries # Author : Nicola Talbot # Version : 1.6 (2009/05/24) # Description: simple Perl script that calls makeindex or xindy. # Intended for use with "glossaries.sty" (saves having to remember # all the various switches) # This file is distributed as part of the glossaries LaTeX package. # Copyright 2007 Nicola L.C. Talbot # This work may be distributed and/or modified under the # conditions of the LaTeX Project Public License, either version 1.3 # of this license of (at your option) any later version. # The latest version of this license is in # http://www.latex-project.org/lppl.txt # and version 1.3 or later is part of all distributions of LaTeX # version 2005/12/01 or later. # # This work has the LPPL maintenance status `maintained'. # # The Current Maintainer of this work is Nicola Talbot. # This work consists of the files glossaries.dtx and glossaries.ins # and the derived files glossaries.sty, mfirstuc.sty, # glossary-hypernav.sty, glossary-list.sty, glossary-long.sty, # glossary-super.sty, glossaries.perl. # Also makeglossaries and makeglossaries. # # History: # v1.6 (2009-05-24) : # * main glossary no longer automatically added # (only added if information in aux file) # * if file extension is specified, check added to ensure it # corresponds to a known glossary extension. # * added file existance test and file empty test # v1.5 (2008-12-26) : # * added support for xindy # * picks up ordering information from aux file # v1.4 (2008-05-10) : # * added support for filenames with spaces. # v1.3 (2008-03-08) : # * changed first line from /usr/bin/perl -w to /usr/bin/env perl # (Thanks to Karl Berry for suggesting this.) # v1.2 (2008-03-02) : # * added support for --help and --version # * improved error handling # v1.1 (2008-02-13) : # * added -w and strict # * added check to ensure .tex file not passed to makeglossaries # # v1.0 (2007-05-10) : Initial release. use Getopt::Std; use strict; use vars qw($opt_q $opt_t $opt_o $opt_s $opt_p $opt_g $opt_c $opt_r $opt_l $opt_i $opt_L $opt_n $opt_C); $Getopt::Std::STANDARD_HELP_VERSION = 1; my $version="1.5 (2008-12-26)"; # v1.5 added -L for xindy (but language can be specified in # .tex file) # v1.5 added -n (print the command that would be issued but # don't actually run the command) getopts('s:o:t:p:L:C:ilqrcgn'); unless ($#ARGV == 0) { die "makeglossaries: Need exactly one file argument.\nUse `makeglossaries --help' for help.\n"; } # define known extensions # v1.6: removed adding main glossary here as there's no guarantee # that it's been used. If it has been used, the information will # be picked up later in the aux file my %exttype = ( # main => {in=>'glo', out=>'gls', 'log'=>'glg'}, ); # v1.5 define require languages for xindy my %language = (); my %codepage = (); my $ext = ''; my $name = $ARGV[0]; # modified this to make sure users don't try passing the # tex file: if (length($ARGV[0]) > 3 and substr($ARGV[0],-4,1) eq ".") { $name = substr($ARGV[0],0,length($ARGV[0])-4); $ext = substr($ARGV[0],-3,3); if (lc($ext) eq 'tex') { die("Don't pass the tex file to makeglossaries:\n" ."either omit the extension to make all the glossaries, " ."or specify one of the glossary files, e.g. $name.glo, to " ."make just that glossary.\n") } } my $istfile = ""; # should letter ordering be used? (v1.5 added) my $letterordering = defined($opt_l); # check aux file for other glossary types # and for ist file name if (open AUXFILE, "$name.aux") { while () { if (m/\\\@newglossary\s*\{(.*)\}{(.*)}{(.*)}{(.*)}/) { $exttype{$1}{'log'} = $2; $exttype{$1}{'out'} = $3; $exttype{$1}{'in'} = $4; unless ($opt_q) { print "added glossary type '$1' ($2,$3,$4)\n"; } } if (m/\\\@istfilename\s*{([^}]*)}/) { $istfile = $1; # check if double quotes were added to \jobname $istfile=~s/^"(.*)"\.ist$/$1.ist/; } # v1.5 added if (m/\\\@xdylanguage\s*{([^}]+)}{([^}]*)}/) { $language{$1} = $2; } # v1.5 added if (m/\\\@gls\@codepage\s*{([^}]+)}{([^}]*)}/) { $codepage{$1} = $2; } # v1.5 added # Allow -l switch to override specification in aux file unless (defined($opt_l)) { if (m/\\\@glsorder\s*{([^}]+)}/) { my $ordering = $1; if ($ordering eq "word") { $letterordering = 0; } elsif ($ordering eq "letter") { $letterordering = 1; } else { print STDERR "WARNING: Unknown ordering '$letterordering'\n", "Assuming word ordering\n"; $letterordering = 0; } } } } close AUXFILE; } else { print STDERR "Unable to open $name.aux: $!\n"; } # has the style file been specified? unless ($istfile) { die "No \\\@istfilename found in '$name.aux'.\n", "Did your LaTeX run fail?\n", "Did your LaTeX run produce any output?\n", "Did you remember to use \\makeglossaries?\n"; } # v1.5 save the general xindy switches my $xdyopts = ''; unless ($opt_L eq "") { $xdyopts .= " -L $opt_L"; } # save all the general makeindex switches my $mkidxopts = ''; if ($opt_i) { $mkidxopts .= " -i"; } if ($letterordering) { $mkidxopts .= " -l"; $xdyopts .= " -M ord/letorder"; } if ($opt_q) { $mkidxopts .= " -q"; $xdyopts .= " -q"; } if ($opt_r) { $mkidxopts .= " -r"; } if ($opt_c) { $mkidxopts .= " -c"; } if ($opt_g) { $mkidxopts .= " -g"; } unless ($opt_p eq "") { $mkidxopts .= " -p $opt_p"; } unless ($opt_s eq "") { $istfile = $opt_s; } # Use xindy if style file ends with .xdy otherwise use makeindex my $usexindy = $istfile=~m/\.xdy\Z/; if ($ext ne '') { # an extension has been specified, so only process # the specified file # v1.6 %thistype is no longer given a default value my %thistype; my $thislang = ""; my $thiscodepage = ""; foreach my $type (keys %exttype) { if ($exttype{$type}{'in'} eq $ext) { %thistype = %{$exttype{$type}}; $thislang = $language{$type}; $thiscodepage = $codepage{$type}; last; } } # v1.6 If %thistype hasn't been defined, then the given # extension doesn't correspond to any known glossary type unless (defined %thistype) { die "The file extension '$ext' doesn't correspond to any\n", "known glossary extension. Try running makeglossaries\n", "without an extension, e.g. makeglossaries \"$name\".\n"; } my $outfile; if ($opt_o eq "") { $outfile = "$name.$thistype{out}"; } else { $outfile = $opt_o; } my $transcript; if ($opt_t eq "") { $transcript = "$name.$thistype{'log'}"; } else { $transcript = $opt_t; } if ($usexindy) { &xindy("$name.$ext", $outfile, $transcript,$istfile, $thislang, $thiscodepage, $xdyopts, $opt_q, $opt_n); } else { &makeindex("$name.$ext",$outfile,$transcript,$istfile, $mkidxopts,$opt_q, $opt_n); } } else { # no file extension specified so process all glossary types foreach my $type (keys %exttype) { my %thistype = %{$exttype{$type}}; my $inputfile = "$name.$thistype{in}"; # v1.6 added file existance test unless (-e $inputfile) { print STDERR "File '$inputfile' doesn't exist.\n", "*** Skipping glossary '$type'. ***\n"; next; } unless (-r $inputfile) { print STDERR "No read access for '$inputfile' $!\n", "*** Skipping glossary '$type'. ***\n"; next; } # v1.6 added file empty test if (-z $inputfile) { print STDERR "File '$inputfile' is empty.\n", "Have you used any entries defined in glossary '$type'?\n", "*** Skipping glossary '$type'. ***\n"; next; } my $outfile; if ($opt_o eq "") { $outfile = "$name.$thistype{out}"; } else { $outfile = $opt_o; } my $transcript; if ($opt_t eq "") { $transcript = "$name.$thistype{'log'}"; } else { $transcript = $opt_t; } if ($usexindy) { &xindy($inputfile,$outfile,$transcript,$istfile, $language{$type},$codepage{$type}, $xdyopts,$opt_q,$opt_n); } else { &makeindex($inputfile,$outfile,$transcript, $istfile,$mkidxopts,$opt_q,$opt_n); } } } sub makeindex{ my($in,$out,$trans,$ist,$rest,$quiet,$dontexec) = @_; my($name,$cmdstr,$buffer,$n,$i,$j); my(@stuff,@item); $cmdstr = "$rest -s \"$ist\" -t \"$trans\" -o \"$out\" \"$in\""; unless ($quiet) { print "makeindex $cmdstr\n"; } unless ($dontexec) { open STATUS, "makeindex $cmdstr 2>&1 |" or die "Can't fork: $!\n"; my $status = ''; while () { $status .= $_; } close STATUS; if ($?) { my $errno = $?; if (open LOGFILE, ">>$trans") { print LOGFILE "\n$status"; close LOGFILE; } else { print STDERR "Unable to open '$trans' $!\n"; } die "Call to makeindex failed (errno=$errno):\n", $status; } print $status; } } sub xindy{ my($in,$out,$trans,$ist,$language,$codepage,$rest,$quiet, $dontexec) = @_; my($cmdstr, $langparam, $main); my($module); $module = $ist; $module=~s/\.xdy\Z//; # map babel names to xindy names if ($language eq "frenchb") { $language = "french"; } elsif ($language=~/^n?germanb?$/) { $language = "german"; } elsif ($language eq "magyar") { $language = "hungarian"; } elsif ($language eq "lsorbian") { $language = "lower-sorbian"; } elsif ($language eq "norsk") { $language = "norwegian"; } elsif ($language eq "portuges") { $language = "portuguese"; } elsif ($language eq "russianb") { $language = "russian"; } elsif ($language eq "slovene") { $language = "slovenian"; } elsif ($language eq "ukraineb") { $language = "ukrainian"; } elsif ($language eq "usorbian") { $language = "upper-sorbian"; } if ($language) { $langparam = "-L $language"; } else { $langparam = ""; } # most languages work with xindy's default codepage, but # some don't, so if the codepage isn't specific, check # the known cases that will generate an error # and supply a default. (For all other cases, it's up to the # user to supply a codepage.) if ($codepage eq '') { if ($language eq 'dutch') { $codepage = "ij-as-ij"; } elsif ($language eq 'german') { $codepage = "din"; } elsif ($language eq 'gypsy') { $codepage = "northrussian"; } elsif ($language eq 'hausa') { $codepage = "utf"; } elsif ($language eq 'klingon') { $codepage = "utf"; } elsif ($language eq 'latin') { $codepage = "utf"; } elsif ($language eq 'mongolian') { $codepage = "cyrillic"; } elsif ($language eq 'slovak') { $codepage = "small"; } elsif ($language eq 'spanish') { $codepage = "modern"; } elsif ($language eq 'vietnamese') { $codepage = "utf"; } } my $codepageparam = ""; if ($codepage) { $codepageparam = "-C $codepage"; } $main = join(' ', "-I xindy", "-M \"$module\"", "-t \"$trans\"", "-o \"$out\"", "\"$in\""); $cmdstr = join(' ', $rest, $langparam, $codepageparam, $main); unless ($quiet) { print "xindy $cmdstr\n"; } unless ($dontexec) { open STATUS, "xindy $cmdstr 2>&1 |" or die "Can't fork: $!\n"; my $status = ''; my $warnings = ""; my $retry =""; while () { $status .= $_; $warnings .= $_ if /WARNING:/; } close STATUS; if ($status=~/Cannot locate xindy module for language ([^\s]+) in codepage ([^\s]+)/) { $cmdstr = join(' ', $rest, $langparam, $main); unless ($quiet) { my $message = "$&\nRetrying using default codepage.\n"; print STDERR $message; $retry .= $message; print "xindy $cmdstr\n"; } open STATUS, "xindy $cmdstr 2>&1 |" or die "Can't fork: $!\n"; $status = ''; while () { $status .= $_; } close STATUS; } if ($status=~/Cannot locate xindy module for language ([^\s]+)/ and $1 ne 'general') { $cmdstr = join(' ', $rest, "-L general", $main); unless ($quiet) { my $message = "$&\nRetrying with -L general\n"; print STDERR $message; $retry .= $message; print "xindy $cmdstr\n"; } open STATUS, "xindy $cmdstr 2>&1 |" or die "Can't fork: $!\n"; $status = ''; while () { $status .= $_; } close STATUS; } if ($?) { my $errno = $?; if (open LOGFILE, ">>$trans") { print LOGFILE "\n$status"; close LOGFILE; } else { print STDERR "Unable to open '$trans' $!\n"; } if ($language) { die "Call to xindy failed (errno=$errno):\n", $status; } else { # If the language hasn't been set, then it may be # because the document doesn't contain # \printglossaries/\printglossary or it may be # because the user has a customized style file that # contains the language settings. die "Call to xindy failed (errno=$errno):\n", $status, "\n\nNo language detected.", "\nHave you remembered to use \\printglossary\n", "or \\printglossaries in your document?\n\n"; } } print $status; if (open LOGFILE, ">>$trans") { print LOGFILE "\n$warnings"; if ($retry) { print LOGFILE "\nmakeglossaries messages:\n\n", $retry; } close LOGFILE; } else { print STDERR "Unable to open '$trans' $!\n"; } } } sub HELP_MESSAGE{ print "\nSyntax : makeglossaries [options] \n\n"; print "For use with the glossaries package to pass relevant\n"; print "files to makeindex or xindy\n\n"; print "\tBase name of glossary file(s). This should\n"; print "\t\tbe the name of your main LaTeX document without any\n"; print "\t\textension.\n"; print "\n General Options:\n"; print "-o \tUse as the output file.\n"; print "-q\t\tQuiet mode\n"; print "-s \tEmploy as the style file\n"; print "-t \tEmploy as the transcript file\n"; print "\n Xindy Options:\n"; print "-L \tUse .\n"; print "\n Makeindex Options:\n"; print "-c\t\tCompress intermediate blanks\n"; print "-g\t\tEmploy German word ordering\n"; print "-l\t\tLetter ordering\n"; print "-p \tSet the starting page number to be \n"; print "-r\t\tDisable implicit page range formation\n"; print "\nSee makeindex documentation for further details on these "; print "options\n"; } sub VERSION_MESSAGE{ print "Makeglossaries Version $version\n"; print "Copyright (C) 2007 Nicola L C Talbot\n"; print "This material is subject to the LaTeX Project Public License.\n"; } 1;