summaryrefslogtreecommitdiff
path: root/Master/tlpkg/bin/tlpkginfo
blob: 45df00708ab8e76f138713feaa262631d9ba8188 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
#!/usr/bin/env perl
# $Id$
# Public domain.  Originally written 2005, Karl Berry.
# 
# Return information given a TL package name (i.e., a tlpsrc file name).
# This requires local copies of CTAN and the TeX Catalogue. This program
# needs to be refactored with ctan2tl and ctan2ds, as we currently
# actually unpack .tds.zip files, etc., which does not seem right.
# 
# With -rev (--revision) argument given, returns only the revision number
# of a given package (see below for details).

BEGIN {
  $^W = 1;
  ($mydir = $0) =~ s,/[^/]*$,,;
  unshift (@INC, "$mydir/..");
}

exit (&main ());

sub main {
  if ($#ARGV >= 0 && ($ARGV[0] =~ m/^--?rev(ision)?$/)) {
    &tlpkgrevision();
  } else {
    &ctaninfo ();
  }
}

sub tlpkgrevision {
  # output and exit codes:
  # repository not found or no modules available there
  #	stdout: -1	exit code: 1
  #	stderr warning
  # package not found:
  #	stdout: 0	exit code: 1
  #	stderr warning
  # both found
  #	stdout: rev#	exit code: 0
  #	(no newline on stdout)
  #
  #   tlpkginfo -revision <pkg> [ <tltree> ]
  require TeXLive::TLPOBJ;
  require TeXLive::TLPDB;
  die "need at least pkg name as argument!" if ($#ARGV < 1);
  if ($#ARGV == 2) {
    $root = $ARGV[2];
  } else {
    $root = "$mydir/../..";
  }
  my $tlpdb = TeXLive::TLPDB->new ("root" => $root);
  if (!defined($tlpdb)) {
    printf STDERR "$0: Cannot find tlpdb in TL root dir $root\n";
    print "-1";
    exit(1);
  }
  my $pkg = $tlpdb->get_package($ARGV[1]);
  if (!defined($pkg)) {
    printf STDERR "Cannot find package $ARGV[1] in TL tree of $root\n";
    print "0";
    exit(1);
  }
  print $pkg->revision;
  exit(0);
}


sub ctaninfo {
  $CTAN = $ENV{"CTAN"} || "/home/ftp/tex-archive";
  $TMPDIR = $ENV{"TMPDIR"} || "/tmp";
  my $top_cat = $ENV{"TEX_CATALOGUE"} || "/home/texlive/catalogue";
  $CATALOGUE = "$top_cat/entries";
  -d "$CATALOGUE/k"
    || die "$0: TEX_CATALOGUE ($CATALOGUE) must point to the top level"
           . " of a TeX Catalogue checkout (with entries/ subdir).\n";
  
  # erroneous or problematic tds files (when new, tell CTAN and author).
  # ieeeconf: capitals in directory names.
  $erroneous_tds = join ("|",
    qw(alertmessage algolrevived amstex countriesofeurope dad ebproof
       endfloat engpron gost
       hacm ieeeconf inriafonts imtekda mathdesign sdaps tufte-latex xassoccnt),
    );
  
  # the CJK material is split into several packages.
  $cjk_pkgs = "cjkutils|dnp|(|garuda-|norasi-)c90";

  if ($ARGV[0] eq "--ctan-dir") {
    my $output = &find_ctan_dir ($ARGV[1], 0);
    print "$output\n" if $output;
    return $output ? 0 : 1;

  } elsif ($ARGV[0] eq "--prepare") {
    my $output = &prepare ($ARGV[1]);
    print "$output\n" if $output;  # if no $output, errors elsewhere
    return $output ? 0 : 1;

  } elsif ($ARGV[0] eq "--ctan-root") {
    print "$CTAN\n";
    return 0;

  } else {
    die "Usage: $0 --ctan-dir PKGNAME\n" 
      . "   or:  --prepare PKGNAME\n"
      . "   or:  --revision PKGNAME [TLROOT]\n"
      . "   or:  --ctan-root\n"
      . "(not \"@ARGV\").\n";
  }
}


# Return the location on CTAN for PKGNAME, or 1 if no PKGNAME or the
# location can't be found.
# 
# If DO_COPY is nonzero, construct a working directory and return that.
# 
sub find_ctan_dir {
  my ($pkgname,$do_copy) = @_;
  return 1 unless $pkgname;  
  
  # use explicit catalogue name if given in tlpsrc.
  my $me = &tlpsrc_find_catalogue ($pkgname);
  if (! $me) {
    if ($pkgname =~ /^($cjk_pkgs)$/) {
      # ctan bundles these packages together,
      # but we use separate tlp names.
      $me = "cjk";  # likewise CJK
    } elsif ($pkgname eq "mkgrkindex") {
      $me = "greek-makeindex";  # use directory name
    } else {
      $me = $pkgname;  # usually the same
    }
  }

  # The CTAN path to a package is stored in the TeX Catalogue;
  # use it if present.
  my $ctan_dir = &catalogue_find_ctan_path ($me, $do_copy);
#warn "find_ctan_dir: got $ctan_dir from catalogue_find_ctan_path\n";
  if (! $ctan_dir) {
    # fall back on many special cases, most probably no longer needed.
    (my $me_wo_bin = $me) =~ s,^bin-,,;
    (my $me_wo_powerdot = $me) =~ s,^powerdot-,,;
    (my $me_wo_type1 = $me) =~ s,-type1$,,;

    for my $dir (
      "support/$me_wo_bin",			# bin-thumbpdf
      "support/$me",				# thumbpdf
      "macros/xetex/latex/$me",			# xetex
      "macros/xetex/generic/$me",		# harvardkyoto
      "macros/plain/contrib/$me",		# timetable
      "macros/plain/$me",			# plnfss
      "macros/pdftex/$me",			# mptopdf
      "macros/luatex/latex/$me",		# luabidi
      "macros/latex/required/$me",		# babel
      "macros/latex/exptl/$me",			# semioneside
      "macros/latex/contrib/powerdot/contrib/$me_wo_powerdot", # powerdot-doc-vn
      "macros/latex/contrib/gene/$me",		# eqname
      "macros/latex/contrib/biblatex/$me",	# authoryear-icomp-tt      
      "macros/latex/contrib/biblatex-contrib/$me", # biblatex-*
      "macros/latex/contrib/\L$me",		# HA-prosper
      "macros/unicodetex/latex/$me",            # unicodetex specific stuff?
      "macros/latex/contrib/$me",		# most everything
      "macros/generic/diagrams/$me",		# circ
      "macros/generic/$me",			# abbr
      "macros/context/contrib/$me",		# context-*
      "language/vietnamese/$me/unpacked",	# vntex
      "language/polish/$me",			# cc-pl
      "language/japanese/$me",			# e.g., jfontmaps
      "language/hyphenation/$me",		# bghyphen
      "language/hebrew/$me",			# cjhebrew
      "language/greek/package-babel/$me",	# ibycus-babel
      "language/devanagari/$me",		# velthuis
      "language/croatian/$me",			# hrlatex
      "language/coptic/$me",			# cbcoptic
      "info/symbols/$me",			# comprehensive
      "info/spanish/$me",			# guia-bibtex
      "info/math/voss/$me",			# mathmode
      "info/challenges/$me",			# AroBend
      "info/bibtex/$me",			# tamethebeast
      "info/$me",				# Type1fonts
      "help/$me",				# es-tex-faq
      "graphics/pstricks/contrib/pedigree/$me",	# pst-pdgr
      "graphics/pstricks/contrib/$me",		# pstricks-add
      "graphics/pictex/addon/$me",		# autoarea
      "graphics/pgf/contrib/$me",		# celtic
      "graphics/metapost/contrib/macros/$me",	# mpattern
      "graphics/$me",				# sparklines
      "fonts/utilities/$me",			# accfonts
      "fonts/ps-type1/$me_wo_type1",		# esint-type1
      "fonts/ps-type1/$me",			# cm-super
      "fonts/greek/$me",			# lfb
      "fonts/gothic/$me",			# blacklettert1
      "fonts/cyrillic/$me/texmf",		# lh
      "fonts/chess/$me",			# skaknew
      "fonts/$me",				# MnSymbol
      "biblio/bibtex/contrib/$me",		# dk-bib
                 ) {
      if (-d "$CTAN/$dir") {
        $ctan_dir = $dir;
        last;
      }
    }

    # names totally dissimilar
    $ctan_dir = "fonts/fourier-GUT" if $me eq "fourier";
    $ctan_dir = "fonts/manual/ps-type1/hoekwater" if $me eq "manfnt-font";
    $ctan_dir = "graphics/pdftex" if $me eq "pdftex-def";
    $ctan_dir = "info/biblio" if $me eq "beebe";
    $ctan_dir = "info/epslatex/french" if $me eq "epslatex-fr";
    $ctan_dir = "info/impatient/cn" if $me eq "impatient-cn";
    $ctan_dir = "info/impatient/fr" if $me eq "impatient-fr";
    $ctan_dir = "info/italian/amsldoc" if $me eq "amsldoc-it";
    $ctan_dir = "info/italian/amsthdoc" if $me eq "amsthdoc-it";
    $ctan_dir = "info/l2tabu/italian" if $me eq "l2tabu-it";
    $ctan_dir = "info/latex2e-help-texinfo/spanish" if $me eq "latex2e-help-texinfo-spanish";
    $ctan_dir = "info/lshort/chinese" if $me eq "lshort-chinese";
    $ctan_dir = "info/tex-references" if $me eq "tex-refs";
    $ctan_dir = "info/translations/vn" if $me eq "ntheorem-vn";
    $ctan_dir = "language/armenian/armtex" if $me eq "armenian";
    $ctan_dir = "language/basque" if $me eq "hyphen-basque";
    $ctan_dir = "language/hyphenation/dehyph" if $me eq "hyphen-german";
    $ctan_dir = "language/hyphenation/elhyphen" if $me eq "hyphen-greek";
    $ctan_dir = "macros/generic" if $me eq "genmisc";
    $ctan_dir = "macros/latex/contrib/misc" if $me eq "ltxmisc";
    $ctan_dir = "macros/latex/contrib/t2/cyrplain" if $me eq "cyrplain";

    # do last, for sake of subdirs above.
    $ctan_dir = "language/hyphenation"
      if $me =~ /^hyphen-/ && ! $ctan_dir;
  }

  if ($pkgname =~ /^biber(|-ms)$/) {
    # For biber and biber-ms, the binaries are in separate packages and directories,
    # so copy the entire directory.
    $ctan_dir = "$CTAN/biblio/$pkgname";

  } elsif ($me eq "cm") {
    # For cm, we just want the mf subdir of fonts/cm.
    $ctan_dir .= "/mf";

  } elsif ($pkgname eq "cmexb") {
    # not in Catalogue as yet, 27apr16.
    $ctan_dir = "macros/cstex/base";
    
  } elsif ($pkgname eq "context-legacy") {
    # We use the same mirrored .zip for context and context-legacy,
    # so the value here is irrelevant. Just avoid the warning below.
    $ctan_dir = "/nonesuch";

  } elsif ($pkgname eq "cs") {
    # For cs, we need to unpack from multiple tarballs,
    # so copy the entire cstex directory.
    $ctan_dir = "$CTAN/macros/cstex/base";

  } elsif ($me eq "mathspic") {
    # For mathspic, we cannot use the catalogue directory:
    # we want to ignore the dos/ subdir and just go with the perl/.
    $ctan_dir .= "/perl";
  
  } elsif ($me eq "sauter") {
    # and for sauter, just base.
    $ctan_dir .= "/base";
  }                         
  
  # prepend ctan root if not an absolute dir (this happens when we make
  # a temp dir).
  $ctan_dir = "$CTAN/$ctan_dir"
    if defined $ctan_dir && $ctan_dir =~ m,^[^/],;
  
  warn "$0:find_ctan_dir: no dir for $pkgname?\n" if ! $ctan_dir;
  return $ctan_dir;
}


# If the .tlpsrc file for ME specifies a catalogue entry, return it,
# else return empty string.
# 
sub tlpsrc_find_catalogue {
  my ($me) = @_;

  chomp (my $mydir = `dirname $0`);
  chomp (my $tlpsrcdir = `cd $mydir/../tlpsrc && /bin/pwd`);
  my $tlpsrc = "$tlpsrcdir/$me.tlpsrc";
  if (! -r $tlpsrc) {
    warn "$0: no tlpsrc $tlpsrc\n";
    return "";
  }

  chomp (my $cat = `awk '\$1 == "catalogue" {print \$2}' $tlpsrc`);
  if ($cat =~ /\./) {
    warn "$0: $tlpsrc catalogue value should not contain a period: $cat\n";
  }
  #warn "tlpsrc_find_catalogue value = $cat\n";
  return $cat;
}

# If we find a Catalogue .xml file for PKGNAME, return the path
# to it. Else return undef.
# 
sub catalogue_find_file {
  my ($pkgname) = @_;
  
  # Happily, the Catalogue uses all-lowercase file/directory names.
  my $firstchar = substr (lc ($pkgname), 0, 1);
  my $catfile =  "$CATALOGUE/$firstchar/$pkgname.xml";

  return -r $catfile ? $catfile : undef;
}


# Look up ctan path for given PKGNAME in catalogue entry.
# xml is too hard to parse, so just look for the <ctan path...> entry.
# 
# Return the ctan path if found (without leading /), or undef.
# Except that if DO_COPY is nonzero, construct a working directory and
# return that.
# 
sub catalogue_find_ctan_path {
  my ($pkgname,$do_copy) = @_;

  my $catfile = &catalogue_find_file ($pkgname);
#warn $catfile ? "got catalogue file $catfile\n" : "no catalogue file\n":
  return undef if ! $catfile;

  # get the raw tag from the catalogue file.
  open (CATFILE, "<$catfile") || die "open($catfile) failed, fixme: $!";
  while ($ctan_path = <CATFILE>) {
    last if $ctan_path =~ /<ctan /;
  }
  if (! $ctan_path) { # give up if the tag wasn't there.
    close CATFILE;
    return undef;
  } 
  # if we found the tag, read more lines until we get to the end of the value.
  while ($ctan_path !~ m,/>,) {
    $ctan_path .= <CATFILE>;
  }
  close CATFILE;
  $ctan_path =~ s/\n/ /g;                     # put it on one line 
#warn "catalogue ctan path start: $ctan_path\n";
  ($ctan_path) = ($ctan_path =~ m,(<.*?/>),); # remove angle brackets
  return undef unless $ctan_path;             # if it's not present at all

  # extract just the dir or file name, without options, etc.
  $ctan_path =~ m,path *= *(["'])/(.*?)\1,;
  $ctan_loc = $2;
  if (! $ctan_loc) {
    # should never happen, but who knows
    warn "$0: no ctan_loc found in $ctan_path?!\n";
    return;
  }

  # if the Catalogue lists the path as a single file, there are two
  # possibilities: (1) it really is a single file, e.g., texilikecover,
  # in which case we copy that file into a temp dir and return that temp
  # dir; (2) it is actually in its own directory, e.g., ifxetex, in
  # which case we return undef here and let the code above find it.
  # (The Catalogue maintainers do not consider this a problem.)
  # 
  # Subcase of (2): package name is babel-hungarian, but CTAN path is
  # babel-contrib/hungarian.  Sigh.
  # 
#warn "catalogue ctan path $ctan_path, pkg $pkgname\n";
#warn "catalogue ctan loc $ctan_loc\n";
  my $ret;
  if ($ctan_path =~ /file='true'/) {
    (my $ctan_babel_path = $ctan_path) =~ s,babel-contrib/,babel-,;
    if ($ctan_path =~ m,/$pkgname/,
        || $ctan_babel_path =~ m,/$pkgname/,) {
      $ret = undef;   # pkg dir somewhere in path.
    } else {
      $ret = $do_copy # are we called by prepare() or not? 
        ? &copy_to_tmpdir ($pkgname, "$CTAN/$ctan_loc")
        : "$CTAN/$ctan_loc";
    }
  } else {
    # not a single file, so use the regular ctan location.  
    $ret = "$CTAN/$ctan_loc";
  }

#warn "catalogue returning $ret\n";
  return $ret;
}

# 
# Create a tmpdir with the tds-ready tree for PKG unpacked, if it exists,
# else return the normal ctan dir for PKG.  Return undef if no package
# or can't find any ctan directory. svn update the catalogue file if we
# find it, unless the envvar TLPKGINFO_CATALOGUE_NO_UPDATE is set.
#
sub prepare {
  my ($pkg) = @_;
  return undef unless $pkg;

  # find the real ctan dir and return it if our tds is erroneous
  my $ctan_loc = &find_ctan_dir ($pkg, 1); # pass do_copy=1
#warn "prepare: ctan_loc for $pkg = $ctan_loc (from find_ctan_dir)\n";
  return $ctan_loc if $pkg =~ /^($erroneous_tds)$/;

  # Ordinarily the Catalogue specifies the .tds.zip file location.
  my $tds_path = "";
  my $catname = &tlpsrc_find_catalogue ($pkg);
  my $catfile = &catalogue_find_file ($catname || $pkg);
#warn "prepare: catfile for $pkg = $catfile (from catalogue_find_file)\n"; 
  if ($catfile) {
    # redirect to stderr because we want the only stdout to be the
    # directory name for ctan2tl to use. Intentionally do not use -q
    # so that we have a chance of noticing errors.
    system ("timeout 4s svn update $catfile </dev/null >&2")
      unless $ENV{"TLPKGINFO_CATALOGUE_NO_UPDATE"};
    
    open (CATFILE, "<$catfile") || die "open($catfile) failed, fixme: $!";
    # looking for a line like <install path='...'/>
    # We don't really want to parse xml; turns out these are always on
    # their own lines, so simple enough to extract.
    my $install_path = "";
    while ($install_path = <CATFILE>) {
      last if $install_path =~ /<install .*path/;
    }
    close CATFILE;
    if ($install_path) {
      $install_path =~ m,path *= *(["'])(.*?)\1,;
      if ($2) { # else already initialized to empty
        # install paths are relevant to the install subtree of CTAN.
        $tds_path = "$CTAN/install$2";
      }
    } 
  }
  
#warn "prepare: initial tds_path for $pkg = $tds_path\n";
  # various exceptions to tds.zip location ...
  if ($pkg =~ /^context(-legacy)?$/) {
    $tds_path = "/home/ftp/mirror/www.pragma-ade.nl/context/latest/cont-tmf.zip";

  } elsif ($pkg eq "cmexb") {
    # as with enctex case
    $tds_path = "$CTAN/macros/cstex/base/cmexb.tar.gz";

  } elsif ($pkg eq "enctex") {
    # not tds, but we eliminate the TDS_READY below
    # and do the rest in ctan2tds as usual.
    $tds_path = "$CTAN/systems/enctex/enctex.tar.gz";

  } elsif ($pkg eq "hfoldsty") {
    $tds_path = glob ("$CTAN/fonts/hfoldsty/hfoldsty-v*.zip");

  } elsif ($pkg eq "latex") {
    $tds_path = "$CTAN/install/macros/latex/latex-base.tds.zip";

  }

  my $tds_path_ok = -s $tds_path ? 1 : 0;
#warn "prepare:   final tds path for $pkg = $tds_path (exists: $tds_path_ok)\n";

  return $ctan_loc if ! $tds_path_ok;
  
  # now we have a tds, so unzip it in a tmpdir and be sure it's readable.
  my $tmpdir = &copy_to_tmpdir ($pkg);
  my $unpack = ($tds_path =~ /zip$/)
               ? "unzip -q -o $tds_path -d $tmpdir"
               : "tar -C $tmpdir -xf $tds_path";
  system ($unpack) == 0 || die "unpack failed: $!\n($unpack)";
  system ("chmod -R a+rX $tmpdir");
  
  # create a sentinel file for ctan2tds' donormal() to check.
  system ("echo $tds_path >$tmpdir/TDS_READY")
    unless $pkg =~ /^(cmexb|enctex)$/; # not tds

  return $tmpdir;
}


# copy file, or unpacked archive, to temp dir and return that dir for
# ctan2tl to use.
#
sub copy_to_tmpdir {
  my ($pkgname,$src) = @_;
  
  my $pkgdir = "$TMPDIR/$<.tl.$pkgname";
  system ("rm -rf $pkgdir");
  mkdir ($pkgdir, 0777) || die "mkdir($pkgdir) failed: $!";

  # copying single file $src ...
  system ("cp -p '$src' '$pkgdir/'")
    if $src;

  # there's no real ctan dir for these packages, just zips.
  my $zip = "";
  if ($pkgname eq "cc-pl") {
    $zip = "cc-pl.zip";
  } elsif ($pkgname eq "mex") {
    $zip = "mex105a.zip";
  } elsif ($pkgname eq "pl-mf") {
    $zip = "pl-mf.zip";
  } elsif ($pkgname eq "tap") {
    $zip = "tap077.zip";
  }
  if ($zip) {
    system ("cd $pkgdir && unzip -q $zip && rm $zip");
  }
  
  # the pl package in TeX Live is a combination of the pl-mf and
  # plpsfont packages on CTAN, per Thomas Esser and GUST as of many
  # years ago.  So combine them.  ctan2tds will flatten everything out,
  # so don't worry about directory levels.
  if ($pkgname eq "pl-mf") {
    my $plpsfont_zip = "$CTAN/language/polish/plpsfont.zip";
    system ("cd $pkgdir && unzip -q $plpsfont_zip");
  }

  return $pkgdir;
}