#!/usr/bin/env perl
# $Id: tl-check-wrapper-consistency 74507 2025-03-07 23:15:24Z karl $
# Public domain.  Originally written 2008.
# 
# Check that the same set of files are wrappers on windows
# and symlinks on all others.

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

use Getopt::Long;
use Pod::Usage;

our ($mydir, $Master);

my $help = 0;
GetOptions("help|?"       => \$help) or pod2usage(1);
pod2usage(-exitstatus => 0, -verbose => 2) if $help;

exit (&main ());


sub main {
  my $err = 0;
  
  my $bindir = "$Master/bin";
  chdir ($bindir) || die "chdir($bindir) failed: $!";
  
  # get entries from one Unix platform, assuming they're the same everywhere.
  my %w = &unx_wrapper_entries ("x86_64-linux");

  my $windows_dir = "windows";
  $cww = "windows/runscript.exe";
  $err += &check_w32 ($windows_dir, $cww, %w);

  return $err;
}



# return all symlinks starting with ".." in DIR as a hash, with symlink
# targets as the values.  Check that targets are executable.
# 
sub unx_wrapper_entries {
  my ($DIR) = @_;
  my %ret;
  
  chomp (my $olddir = `pwd`);
  chdir ($DIR) || die "chdir($DIR) failed: $!";

  local *DIR;
  opendir (DIR, ".") || die "opendir($DIR) failed: $!";
  while (my $ent = readdir (DIR)) {
    next unless -l $ent;  # skip all but symlinks
    next if -d $ent;      # and skip directories (i.e., man/)

    my $target = readlink ($ent);
    die "readlink($ent) failed: $!" if !defined ($target);
    next unless $target =~ /^\.\./;  # skip all but .. symlinks
    
    # skip {context,mtxrun}.lua:
    next if $ent =~ /\.lua$/;

    # the target of the symlink should otherwise be executable.
    warn "$ent: target $target not executable\n" if ! -x $target;

    $ret{$ent} = $target;  # remember name and link target
  }
  closedir (DIR) || warn "closedir($DIR) failed: $!";
  chdir ($olddir) || die "chdir($olddir) failed: $!";

  return %ret;
}



# Windows is painfully special. Given the list of wrappers in UW, check
# that each of those entries (excluding shell scripts and other spceial
# cases) exists in W32DIR as a .exe, and furthermore is a copy of the
# canonical w32 wrapper exe specified in W32CANONICAL.
# 
# (2023: our windows binaries aren't 32-bit any more, but don't bother
# renaming.)
# 
sub check_w32 {
  my ($w32dir, $w32canonical, %uw) = @_;
  my $diff = 0;
  
  my %is_shell_script = &list_shell_scripts ();
  for my $k (sort keys %uw) {
    next if $is_shell_script{$k};    # skip shell scripts
    #
    # also skip these special cases:
    next if $k =~
      /^(cluttex|epspdftk|extractbb|latexindent|mktex.*
         |texdoctk|tlcockpit|tlmgr|tlshell|xasy)$/x;
    #
    # else do the diff with the canonical wrapper:
    $diff += system ("cmp $w32dir/$k.exe $w32canonical");
  }

  opendir (DIR, $w32dir) || die "opendir($DIR) failed: $!";
  my @binfiles = readdir (DIR);
  closedir (DIR) || warn "closedir($DIR) failed: $!";

  foreach my $f (@binfiles) {
    next unless ($f =~ s/\.(bat|cmd)$//); # only batch files
    next if $uw{$f};                      # already checked
    $diff += system ("cmp $w32dir/$f.exe $w32canonical");
  }

  # extra check for fmtutil-sys.exe, since fmtutil is an executable.
  $diff += system ("cmp $w32dir/fmtutil-sys.exe $w32canonical");
  
  return $diff;
}

# As it happens, we already distinguish sh scripts from others in the
# build process, for basically the same reason.  So return the list
# maintained there by using a target defined in linked_scripts/Makefie
# for us.
# 
sub list_shell_scripts {
  my %sh;
  
  # has to be the Work/ directory to get the Makefile, not Makefile.{in,am}.
  my $Work = "$Master/../Build/source/Work";
  my $Work_linked_scripts = "$Work/texk/texlive/linked_scripts";
  
  -d "$Work_linked_scripts"||die "no linked_scripts dir: $Work_linked_scripts";
  
  # use make; ensure we get only the last line, although that should be
  # all there is.
  my $lst = `make -s -C $Work_linked_scripts echo-shell-scripts | tail -1`;
  
  for my $script (split (" ", $lst)) {
    $script =~ s,^.*/,,;
    $sh{$script} = 1;  # save with extension (for listings-ext.sh)
    #
    $script =~ s,\.[^.]*$,,;
    $sh{$script} = 1;  # save without extension (everything else)
  }

  # more shell scripts, that are not part of linked_scripts.
  $sh{"chkweb"} = 1;
  
  return %sh;
}
