diff options
author | Karl Berry <karl@freefriends.org> | 2011-09-21 23:50:10 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2011-09-21 23:50:10 +0000 |
commit | b86f8cc2e0150a123620c45d3021145fc5f39381 (patch) | |
tree | 624572511b025717418bc61cade546df74e756bd /Build | |
parent | 4f45e76895cf001fdff36c7c0d63b096986f4965 (diff) |
new (old) script ctanify (aug09)
git-svn-id: svn://tug.org/texlive/trunk@24061 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build')
-rw-r--r-- | Build/source/texk/texlive/linked_scripts/Makefile.am | 1 | ||||
-rw-r--r-- | Build/source/texk/texlive/linked_scripts/Makefile.in | 1 | ||||
-rwxr-xr-x | Build/source/texk/texlive/linked_scripts/ctanify/ctanify | 651 |
3 files changed, 653 insertions, 0 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.am b/Build/source/texk/texlive/linked_scripts/Makefile.am index d33c711428e..2f2f1b2a561 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.am +++ b/Build/source/texk/texlive/linked_scripts/Makefile.am @@ -72,6 +72,7 @@ texmf_dist_other_scripts = \ bundledoc/bundledoc \ cachepic/cachepic.tlu \ context/perl/mptopdf.pl \ + ctanify/ctanify \ ctanupload/ctanupload.pl \ de-macro/de-macro \ dviasm/dviasm.py \ diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.in b/Build/source/texk/texlive/linked_scripts/Makefile.in index d7752662b68..48df75f5ad3 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.in +++ b/Build/source/texk/texlive/linked_scripts/Makefile.in @@ -210,6 +210,7 @@ texmf_dist_other_scripts = \ bundledoc/bundledoc \ cachepic/cachepic.tlu \ context/perl/mptopdf.pl \ + ctanify/ctanify \ ctanupload/ctanupload.pl \ de-macro/de-macro \ dviasm/dviasm.py \ diff --git a/Build/source/texk/texlive/linked_scripts/ctanify/ctanify b/Build/source/texk/texlive/linked_scripts/ctanify/ctanify new file mode 100755 index 00000000000..5f9d68a5cf9 --- /dev/null +++ b/Build/source/texk/texlive/linked_scripts/ctanify/ctanify @@ -0,0 +1,651 @@ +#! /usr/bin/env perl + +################################################ +# Prepare a LaTeX package for upload to CTAN # +# By Scott Pakin <scott+ctify@pakin.org> # +################################################ + +use Cwd; +use File::Basename; +use File::Copy; +use File::Find; +use File::Path; +use File::Spec; +use File::stat; +use File::Temp qw(tempdir); +use Getopt::Long; +use Pod::Usage; +use warnings; +use strict; + +# Define some global variables. +our $VERSION = "1.1"; # ctanify version number +my $progname = basename $0; # Name of this program +my $pkgname; # Base name of the package to create +my $miscify = 0; # 1=replace singletons with misc; 0=don't +my $autoinclude = 1; # 1=automatically include files named in .ins +my $skipdroppings = 1; # 1=skip "dropping" files (e.g., "README~") +my $unixify = 1; # 1=make text files use Unix line endings +my @manifest; # List of files to include +my %file2tds; # Map from specific filenames to TDS directories +my @tdsdirlist; # Contents of the TDS tree +my @pkgdirlist; # Contents of the package tree +my @tdsonly; # Files to include only in the TDS tree +my $tdsdir = ""; # Absolute directory of the TDS tree +my $pkgdir = ""; # Absolute directory of the package directory +my $zipname; # Name of the TDS zip file +my $tarname; # Name of the package tar file +my $tdsoutdir; # Directory name where to output TDS tree +my $usagestr = "Usage: ${progname} [--help] [--version] [--pkgname=<package>] [--[no]auto] [--[no]skip] [--[no]unixify] [--tdsonly=<filespec> ...] filename[=<dirname>] ...\n"; + +# Associate file extensions with TDS directories. "%s" is replaced +# with the package name. +my %ext2tds = + ("afm" => "fonts/afm/public/%s", + "bib" => "bibtex/bib/%s", + "bst" => "bibtex/bst/%s", + "cls" => "tex/latex/%s", + "dtx" => "source/latex/%s", + "dvi" => "doc/latex/%s", + "fd" => "tex/latex/%s", + "ins" => "source/latex/%s", + "mf" => "fonts/source/public/%s", + "mp" => "metapost/%s", + "pdf" => "doc/latex/%s", + "pfb" => "fonts/type1/public/%s", + "pfm" => "fonts/type1/public/%s", + "ps" => "doc/latex/%s", + "README" => "doc/latex/%s", + "sty" => "tex/latex/%s", + "tfm" => "fonts/tfm/public/%s", + "txt" => "doc/latex/%s", + "vf" => "fonts/vf/public/%s"); + +# Specify a list of filename regexps to ignore. +my @ignore_re = ('~$', '\bCVS\b', '\b\.svn\b'); + +# Specify a list of file extensions that are considered "text" files. +# This script will convert their line endings to Unix style (a single +# linefeed character). +my %text_ext = + map {($_ => 1)} qw(afm bib bst cls dtx fd ins ltx mf mp sty tex txt); + +# Define a subroutine that returns the size in bytes of a file, +# aborting on error. +sub filesize ($) +{ + my $finfo = stat($_[0]) || die "${progname}: Failed to stat $_ ($!)\n"; + return $finfo->size; +} + +# Define a subroutine that runs a command and aborts on error. +sub run_command ($;$) +{ + my $command = $_[0]; + my $ignore_errors = defined $_[1] ? $_[1] : 0; + my $retval = system $command; + if (!$ignore_errors && $retval>>8) { + die "${progname}: Command \"$command\" failed\n"; + } +} + +# Define a subroutine that Unix-ifies the line endings in text files. +# This subroutine is called from File::Find so it does not accept +# arguments via the @_ variable. +my %reported_line_endings; # Files we converted and told the user about +sub use_unix_line_endings +{ + # Determine if the file is a text file. + my $fname = $_; + return if !-f $fname; + my $fext = ($fname =~ m|([^./]+)$| && $1); + return if !defined $text_ext{$fext}; + + # Make it use Unix line endings. Note that we don't bother + # checking if we're already on a Unix (or Unix-like) system. + my $oldsize = filesize $fname; + open(TEXTFILE, "<$fname") || die "${progname}: Failed to open $File::Find::name ($!)\n"; + my @entirefile = map {s/[\n\r]+$/\n/; $_} <TEXTFILE>; + close TEXTFILE; + open(BINARYFILE, ">$fname") || die "${progname}: Failed to open $File::Find::name ($!)\n"; + binmode BINARYFILE; + print BINARYFILE @entirefile; + close BINARYFILE; + my $newsize = filesize $fname; + if ($newsize != $oldsize && !defined $reported_line_endings{$fname}) { + # Don't frighten the user when he sees a different file size + # in the tar file from the original in the filesystem + my $fullname = $File::Find::name; + $fullname =~ s,^$tdsdir/,,; + $fullname =~ s,^$pkgdir/,,; + warn "${progname}: Modified $fullname to use Unix line endings (use --no-unixify to prevent this)\n"; + $reported_line_endings{$fname} = 1; + } +} + +########################################################################### + +# Parse the command line. +my $wanthelp = 0; +my $wantversion = 0; +GetOptions("p|pkgname=s" => \$pkgname, + "m|misc!" => \$miscify, + "k|skip!" => \$skipdroppings, + "u|unixify!" => \$unixify, + "t|tdsonly=s" => \@tdsonly, + "a|auto!" => \$autoinclude, + "d|tdsdir=s" => \$tdsoutdir, + "V|version" => \$wantversion, + "h|help" => \$wanthelp) + || pod2usage(-verbose => 0, + -exitval => 1); +pod2usage(-verbose => 1, + -exitval => 0) if $wanthelp; +if ($wantversion) { + print "ctanify version $VERSION\n"; + exit 0; +} +pod2usage(-verbose => 0, + -exitval => 1) if !@ARGV; +foreach my $fname (@ARGV) { + # Let the user specify "<filename>=<dirname>" to place files + # explicitly in the TDS tree. + if ($fname =~ /^(.*)=([^=]+)$/) { + foreach my $exp_fname (glob $1) { + $file2tds{$exp_fname} = $2; + push @manifest, $exp_fname; + } + } + else { + push @manifest, glob $fname; + } +} +@tdsonly = map {File::Spec->abs2rel($_)} map {glob} @tdsonly; + +# Determine the package name if not explicitly specified. +if (!defined $pkgname) { + my @mainfiles = (grep(/\.ins$/, @manifest), grep(/\.sty$/, @manifest)); + if (!@mainfiles) { + die "${progname}: Please either list a .ins or .sty file or specify --pkgname\n"; + } + $pkgname = basename $mainfiles[0]; + $pkgname =~ s/\.[^.]+$//; +} + +# Parse each .ins file to find more files to include. +if ($autoinclude) { + foreach my $fname (grep /\.ins$/, @manifest) { + # Read the entire file. + local $/ = undef; + open(INSFILE, "<$fname") || die "${progname}: Failed to open $fname ($!)\n"; + my $insfile = <INSFILE>; + close INSFILE; + $insfile =~ s/\%.*?\n//g; + + # Add all source files (\from) to the manifest. + while ($insfile =~ /\\from\{([^\}]+)\}/g) { + push @manifest, $1; + } + + # Add all generated files (\file) to the manifest but also to + # the list of TDS-only inclusions. + while ($insfile =~ /\\file\{([^\}]+)\}/g) { + push @manifest, $1; + push @tdsonly, $1; + } + } +} + +# Skip "dropping" files and files beginning with ".". +if ($skipdroppings) { + my @newmanifest; + CHECK_DROPPING: + foreach my $fname (@manifest) { + if (substr($fname, 0, 1) eq ".") { + warn "${progname}: Excluding $fname (use --no-skip to force inclusion)\n"; + next CHECK_DROPPING; + } + foreach my $dropping_re (@ignore_re) { + if ($fname =~ $dropping_re) { + warn "${progname}: Excluding $fname (use --no-skip to force inclusion)\n"; + next CHECK_DROPPING; + } + } + push @newmanifest, $fname; + } + @manifest = @newmanifest; +} + +# Map each file to a TDS directory. +foreach my $fname (@manifest) { + next if defined $file2tds{$fname}; + my $fext = ($fname =~ m|([^./]+)$| && $1); + if (!defined $ext2tds{$fext}) { + warn "${progname}: Not including $fname in the TDS tree (unknown extension)\n"; + next; + } + $file2tds{$fname} = sprintf $ext2tds{$fext}, $pkgname; +} + +# Create a working directory and populate it with TDS files. +my $workdir = tempdir("$progname-XXXXXX", TMPDIR => 1, CLEANUP => 1); +$tdsdir = $tdsoutdir ? $tdsoutdir : "$workdir/texmf"; +mkdir $tdsdir || die "${progname}: Failed to create $tdsdir ($!)\n"; +foreach my $fname (@manifest) { + # Create a TDS subdirectory. + my $subdir = $file2tds{$fname}; + next if !defined $subdir; + mkpath "$tdsdir/$subdir"; + + # Copy the specified file into the subdirectory. + copy($fname, "$tdsdir/$subdir/" . basename $fname) || die "${progname}: Failed to copy $fname ($!)\n"; +} +if ($miscify) { + # Replace package directories containing a single file with "misc". + my %renamings; + find(sub { + return if $File::Find::name =~ m|\btexmf/fonts\b|; + if (-d) { + my @children = glob "$_/*"; + if ($#children == 0 && -f $children[0] && $_ eq $pkgname) { + $renamings{$File::Find::name} = $File::Find::dir . "/misc"; + } + } + }, $tdsdir); + while (my ($oldname, $newname) = each %renamings) { + rename $oldname, $newname; + } +} +if ($unixify) { + # Make all text files use Unix line endings. + find(\&use_unix_line_endings, $tdsdir); +} + +# Complain if the doc directory contains PostScript or DVI files. +my @bad_docs; +find(sub { + return if !-f; + push @bad_docs, $_ if $File::Find::name =~ m,\bdoc/latex/$pkgname/.*\.(ps|dvi)$,; +}, $tdsdir); +if (@bad_docs) { + my $bad_docs = join ", ", @bad_docs; + warn "${progname}: CTAN prefers having only PDF documentation (re: $bad_docs)\n"; +} + +# We have no more work to do if we're simply creating a TDS directory. +exit 0 if $tdsoutdir; + +# Store a listing of the TDS directory. +my $prevdir = getcwd(); +chdir $tdsdir || die "${progname}: Failed to switch to $tdsdir ($!)\n"; +find(sub { + return if !-f; + my $fsize = filesize $_; + push @tdsdirlist, [substr($File::Find::name, 2), $fsize]; +}, "."); + +# Archive and remove the TDS directory. +$zipname = "$pkgname.tds.zip"; +my $zip_exclusions = $skipdroppings ? "-x __MACOSX -x .DS_Store" : ""; +run_command "zip -q -r -9 -m $workdir/$zipname . $zip_exclusions", 1; +my $zipsize; +if (-f "$workdir/$zipname") { + $zipsize = filesize "$workdir/$zipname"; +} +else { + $zipname = ""; +} +chdir $prevdir || die "${progname}: Failed to switch to $prevdir ($!)\n"; +rmdir $tdsdir || die "${progname}: Failed to remove $tdsdir ($!)\n"; + +# Copy all files named in the file manifest to the package directory. +$pkgdir = "$workdir/$pkgname"; +mkdir $pkgdir || die "${progname}: Failed to create $pkgdir ($!)\n"; +my %tdsonly = map {($_ => 1)} @tdsonly; +foreach my $fname (@manifest) { + my $relname = File::Spec->abs2rel($fname); + next if defined $tdsonly{$relname}; + my ($namepart, $pathpart, $suffixpart) = fileparse($relname); + mkpath "$pkgdir/$pathpart"; + my $targetfile = "$pkgdir/$pathpart/$namepart$suffixpart"; + copy($fname, $targetfile) || die "${progname}: Failed to copy $fname ($!)\n"; +} +if ($unixify) { + # Make all text files use Unix line endings. + find(\&use_unix_line_endings, $pkgdir); +} + +# Store a listing of the package directory. +$prevdir = getcwd(); +chdir $pkgdir || die "${progname}: Failed to switch to $pkgdir ($!)\n"; +find(sub { + return if !-f; + my $fsize = filesize $_; + push @pkgdirlist, [substr($File::Find::name, 2), $fsize]; +}, "."); +chdir $prevdir || die "${progname}: Failed to switch to $prevdir ($!)\n"; + +# Tar up the package directory. +$tarname = "$pkgname.tar.gz"; +my $tar_exclusions = $skipdroppings ? "--exclude=__MACOSX --exclude=.DS_Store" : ""; +run_command "tar -czf $tarname -C $workdir $tar_exclusions $pkgname $zipname"; + +# Output a listing of what we tarred up. +printf "\n%8d %s\n\n", filesize($tarname), $tarname; +foreach my $fname_size (sort {$a->[0] cmp $b->[0]} @pkgdirlist) { + my ($fname, $fsize) = @$fname_size; + printf " %8d %s/%s\n", $fsize, $pkgname, $fname; +} +if ($zipname ne "") { + printf " %8d %s\n\n", $zipsize, $zipname; + foreach my $fname_size (sort {$a->[0] cmp $b->[0]} @tdsdirlist) { + my ($fname, $fsize) = @$fname_size; + printf " %8d %s\n", $fsize, $fname; + } +} +print "\n"; + +########################################################################### + +__END__ + +=head1 NAME + +ctanify - Prepare a package for upload to CTAN + + +=head1 SYNOPSIS + +ctanify +[B<--pkgname>=I<string>] +[B<-->[B<no>]B<auto>] +[B<--tdsonly>=I<filespec> ...] +[B<-->[B<no>]B<unixify>] +[B<-->[B<no>]B<skip>] +I<filespec>[=I<dirname>] ... + +ctanify +[B<--help>] + +ctanify +[B<--version>] + + +=head1 DESCRIPTION + +B<ctanify> is intended for developers who have a LaTeX package that +they want to distribute via the Comprehensive TeX Archive Network +(CTAN). Given a list of filenames, B<ctanify> creates a tarball (a +F<.tar.gz> file) with the files laid out in CTAN's preferred +structure. The tarball additionally contains a ZIP (F<.zip>) file +with copies of all files laid out in the standard TeX Directory +Structure (TDS), which facilitates inclusion of the package in the TeX +Live distribution. + + +=head1 OPTIONS + +B<ctanify> accepts the following command-line options: + +=over 5 + +=item B<-h>, B<--help> + +Output basic usage information and exit. + +=item B<-V>, B<--version> + +Output B<ctanify>'s version number and exit. + +=item B<-p> I<string>, B<--pkgname>=I<string> + +Specify explicitly a package name. Normally, B<ctanify> uses the base +name of the first F<.ins> or F<.sty> file listed as the package name. +The package name forms the base name of the tarball that B<ctanify> +produces. + +=item B<--noauto> + +Do not automatically add files to the tarball. Normally, B<ctanify> +automatically includes all files mentioned in a F<.ins> file. + +=item B<-t> I<filespec>, B<--tdsonly>=I<filespec> + +Specify a subset of the files named on the command line to include +only in the TDS ZIP file, not in the CTAN package directory. +Wildcards are allowed (quoted if necessary), and B<--tdsonly> can be +used multiple times on the same command line. + +=back + +At least one filename must be specified on the command line. +B<ctanify> automatically places files in the TDS tree based on their +extension, but this can be overridden by specifying explicitly a +target TDS directory using the form I<filespec>=I<dirname>. Wildcards +are allowed for the filespec (quoted if necessary). + + +=head1 ADDITIONAL OPTIONS + +The following options are unlikely to be necessary in ordinary usage. +They are provided for special circumstances that may arise. + +=over 5 + +=item B<-d> I<dirname>, B<--tdsdir>=I<dirname> + +Instead of creating a tarball for CTAN, merely create the package TDS +tree rooted in directory I<dirname>. + +=item B<-nou>, B<--no-unixify> + +Store text files unmodified instead of converting their end-of-line +character to Unix format (a single linefeed character with no +carriage-return character), even though CTAN prefers receiving all +files with Unix-format end-of-line characters. + +=item B<-nok>, B<--no-skip> + +Force B<ctanify> to include files such as Unix hidden files, Emacs +backup files, and version-control metadata files, all of which CTAN +dislikes receiving. + +=item B<-m>, B<--miscify> + +Rename directories containing a single file to C<misc>. (For example, +rename C<tex/latex/mypackage/mypackage.sty> to +C<tex/latex/misc/mypackage.sty>.) This was common practice in the +past but is now strongly discouraged. + +=back + + +=head1 DIAGNOSTICS + +=over 5 + +=item C<Failed to copy I<filename> (No such file or directory)> + +This message is typically caused by a F<.ins> file that generates +I<filename> but that has not already been run through F<tex> or +F<latex> to actually produce I<filename>. B<ctanify> does not +automatically run F<tex> or F<latex>; this needs to be done manually +by the user. See L</CAVEATS> for more information. + +=item C<Modified I<filename> to use Unix line endings (use --no-unixify to prevent this)> + +For consistency, CTAN stores all text files with Unix-style line +endings (a single linefeed character with no carriage-return +character). To help in this effort, B<ctanify> automatically replaces +non-Unix-style line endings. The preceding merely message notifies +the user that he should not be alarmed to see a different size for +I<filename> in the tarball versus the original I<filename> on disk +(which B<ctanify> never modifies). If there's a good reason to +preserve the original line endings (and there rarely is), the +B<--no-unixify> option can be used to prevent B<ctanify> from altering +any files when storing them in the tarball. + +=item C<Excluding I<filename> (use --no-skip to force inclusion)> + +B<ctanify> normally ignores files--even when specified explicitly on +the command line--that CTAN prefers not receiving. These include +files whose names start with "F<.>" (Unix hidden files), end in "F<~>" +(Emacs automatic backups), or that come from a F<CVS> or F<.svn> +directory (version-control metadata files). If there's a good reason +to submit such files to CTAN (and there rarely is), the B<--no-skip> +option can be used to prevent B<ctanify> from ignoring them. + +=item C<CTAN prefers having only PDF documentation (re: I<filename>)> + +Because of the popularity of the PDF format, CTAN wants to have as +much documentation as possible distributed in PDF. The preceding +message asks the user to replace any PostScript or DVI documentation +with PDF if possible. (B<ctanify> will still include PostScript and +DVI documentation in the tarball; the preceding message is merely a +polite request.) + +=item C<Not including I<filename> in the TDS tree (unknown extension)> + +B<ctanify> places files in the TDS tree based on a table of file +extensions. For example, all F<.sty> files are placed in +F<tex/latex/I<package-name>>. If B<ctanify> does not know where to +put a file it does not put it anywhere. See the last paragraph of +L</OPTIONS> for an explanation of how to specify explicitly a file's +target location in the TDS tree. For common file extensions that +happen to be absent from B<ctanify>'s table, consider also notifying +B<ctanify>'s author at the address shown below under L</AUTHOR>. + +=back + + +=head1 EXAMPLES + +=head2 The Common Case + +Normally, all that's needed is to tell B<ctanify> the name of the +F<.ins> file (or F<.sty> if the package does not use DocStrip) and the +prebuilt documentation, if any: + + $ ctanify mypackage.ins mypackage.pdf README + + 490347 mypackage.tar.gz + + 1771 mypackage/README + 15453 mypackage/mypackage.dtx + 1957 mypackage/mypackage.ins + 277683 mypackage/mypackage.pdf + 246935 mypackage.tds.zip + + 1771 doc/latex/mypackage/README + 277683 doc/latex/mypackage/mypackage.pdf + 15453 source/latex/mypackage/mypackage.dtx + 1957 source/latex/mypackage/mypackage.ins + 1725 tex/latex/mypackage/mypackage.sty + +B<ctanify> outputs the size in bytes of the resulting tarball, each +file within it, and each file within the contained ZIP file. In the +preceding example, notice how B<ctanify> automatically performed all +of the following operations: + +=over 5 + +=item * + +including F<mypackage.dtx> (found by parsing F<mypackage.ins>) in both +the F<mypackage> directory and the ZIP file, + +=item * + +including F<mypackage.sty> (found by parsing F<mypackage.ins>) in the +ZIP file but, because it's a generated file, not in the F<mypackage> +directory, and + +=item * + +placing all files into appropriate TDS directories (documentation, +source, main package) within the ZIP file. + +=back + +Consider what it would take to manually produce an equivalent +F<mypackage.tar.gz> file. B<ctanify> is definitely a simpler, quicker +alternative. + + +=head2 Advanced Usage + +B<ctanify> assumes that PostScript files are documentation and +therefore stores them under F<doc/latex/I<package-name>/> in the TDS +tree within the ZIP File. Suppose, however, that a LaTeX package uses +a set of PostScript files to control B<dvips>'s output. In this case, +B<ctanify> must be told to include those PostScript files in the +package directory, not the documentation directory. + + $ ctanify mypackage.ins "mypackage*.ps=tex/latex/mypackage" + + +=head1 FILES + +=over 5 + +=item F<perl> + +B<ctanify> is written in Perl and needs a Perl installation to run. + +=item F<tar>, F<gzip> + +B<ctanify> requires the GNU F<tar> and F<gzip> programs to create a +compressed tarball (F<.tar.gz>). + +=item F<zip> + +B<ctanify> uses a F<zip> program to archive the TDS tree within the +main tarball. + +=back + + +=head1 CAVEATS + +B<ctanify> does not invoke F<tex> or F<latex> on its own, S<e.g., to> +process a F<.ins> file. The reason is that B<ctanify> does not know +in the general case how to produce all of a package's generated files. +It was deemed better to do nothing than to risk overwriting existing +F<.sty> (or other) files or to include outdated generated files in the +tarball. In short, before running B<ctanify> you should manually +process any F<.ins> files and otherwise generate any files that should +be sent to CTAN. + +B<ctanify> has been tested only on Linux. It may work on S<MacOS X>. +It probably does not work on Windows. Volunteers willing to help port +B<ctanify> to other platforms are extremely welcome. + + +=head1 SEE ALSO + +tar(1), +zip(1), +latex(1), +Guidelines for uploading TDS-Packaged materials to CTAN +(L<http://www.ctan.org/TDS-guidelines.html>), +A Directory Structure for TeX Files (L<http://tug.org/tds/>), + + +=head1 AUTHOR + +Scott Pakin, I<scott+ctify@pakin.org> + + +=head1 COPYRIGHT AND LICENSE + +Copyright 2009 Scott Pakin + +This work may be distributed and/or modified under the conditions of +the LaTeX Project Public License, either S<version 1.3c> of this +license or (at your option) any later version. The latest version of +this license is in + +L<http://www.latex-project.org/lppl.txt> + +and S<version 1.3c> or later is part of all distributions of LaTeX +version 2008/05/04 or later. |