windows-nt/Source/XPSP1/NT/tools/sp/findinfdata.cmd
2020-09-26 16:20:57 +08:00

692 lines
21 KiB
Batchfile

@REM -----------------------------------------------------------------
@REM
@REM findinfdata - jtolman
@REM Go through inf files and find install data for each file.
@REM
@REM Copyright (c) Microsoft Corporation. All rights reserved.
@REM
@REM -----------------------------------------------------------------
@perl -x "%~f0" %*
@goto :EOF
#!perl
use strict;
use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
use lib $ENV{RAZZLETOOLPATH} . "\\sp";
use lib $ENV{RAZZLETOOLPATH};
use Logmsg;
use ParseArgs;
use InfData;
use InfParse;
use CanonDir qw(%misses addPrefix removePrefix);
sub Usage { print<<USAGE; exit(1) }
Usage: findinfdata [options] <bin_dir> /f <out_file>
/f: File to output the results to.
/c: Use information from a change file.
/w Include WOW files.
/xs Ignore server skus.
/xc Ignore client skus.
/i Remake infscan.lst using infscan before reading it.
/o Write using original directory structure.
/s Read infs from service pack directory structure.
/b Read infs from the binaries directory.
/l Language to run for.
USAGE
my ($outfile, $dir, $infscan, $altpath, $template, $newfile, $wow, $binaries, $lang, $servpack);
my @changes;
my ($xserver, $xclient);
parseargs('?' => \&Usage,
'f:'=> \$outfile,
'c:'=> \@changes,
'w' => \$wow,
'xs'=> \$xserver,
'xc'=> \$xclient,
'i' => \$infscan,
'o' => \$altpath,
's' => \$servpack,
'b' => \$binaries,
'l:'=> \$lang,
\$dir
);
undef $wow if $binaries and lc$ENV{_BUILDARCH} ne "ia64";
$wow = 1 if $binaries and lc$ENV{_BUILDARCH} eq "ia64";
if ($altpath) { undef $altpath; }
else { $altpath = 1; }
my $arch = $wow ? "ia64":"i386";
my $barch = $wow ? "ia64":"x86";
my $archd = $wow ? "ia64":"w32x86";
$lang = $ENV{LANG} if !defined $lang;
$lang = "usa" if !defined $lang or $lang eq "";
my $slp = "";
my $slpdir;
my $bindir;
my $wowdir;
if ( $binaries ) {
$bindir = "$dir";
$wowdir = "$dir\\wowbins";
}
elsif ( $servpack ) {
$bindir = "$dir\\bin";
$slpdir = "$dir\\slp";
$wowdir = "$dir\\bin\\wowbins";
}
else {
$bindir = "$dir\\bin";
$slpdir = "$dir";
$wowdir = "$dir\\bin\\wowbins_uncomp";
}
######################################################################
# Define main data structures.
my %files; # Main table of files and directories.
my %relfiles; # All files with multiple versions.
# List of skus to examine: BitMask SkuName CongealScriptsName
my $skumax = 7; # Max # of bits in the this list. Change as needed.
my @skus = ();
my @skuletters = ();
if ( !$xclient ) {
push @skus, "1, pro, wks";
push @skus, "2, per, per" if $arch ne "ia64";
push @skuletters, ("p", "c");
}
if ( !$xserver ) {
push @skus, "4, srv, srv" if $arch ne "ia64";
push @skus, "8, ads, ent";
push @skus, "16, dtc, dtc";
# push @skus, "32, sbs, sbs" if $arch ne "ia64";
# push @skus, "64, bla, bla" if $arch ne "ia64";
push @skuletters, ("s", "a", "d");
# push @skuletters, ("l", "b");
}
my $skumask = 0;
my $skucount = 0;
my $type;
my $prod;
my $boot;
CanonDir::setup( $archd, $arch );
# List of info on relative paths to use with /r.
my %relpath;
if ( $altpath ) {
%relpath = (
0x1 => "ip\\",
0x2 => "ic\\",
0x4 => "is\\",
0x8 => "ia\\",
0x1c => "is\\", # Remove when il and ib are used.
0x7c => "is\\",
0x18 => "ia\\",
0x10 => "id\\",
0x20 => "il\\",
0x40 => "ib\\",
);
} else {
%relpath = (
0x2 => "perinf\\",
0x7c => "srvinf\\",
0x18 => "entinf\\",
0x10 => "dtcinf\\",
0x20 => "sbsinf\\",
0x40 => "blainf\\",
);
}
my %infpath = (
"proinf" => 0x1,
"perinf" => 0x2,
"srvinf" => 0x7c,
"entinf" => 0x18,
"dtcinf" => 0x10,
"blainf" => 0x20,
"sbsinf" => 0x40
);
my @pathorder = ("sbsinf","blainf","dtcinf","perinf","entinf","srvinf", "");
my %buildorder = (
"pro" => "\\",
"per" => "\\ \\perinf\\",
"srv" => "\\ \\srvinf\\",
"bla" => "\\ \\srvinf\\ \\blainf\\",
"sbs" => "\\ \\srvinf\\ \\sbsinf\\",
"ads" => "\\ \\srvinf\\ \\entinf\\",
"dtc" => "\\ \\srvinf\\ \\entinf\\ \\dtcinf\\",
);
# Format: ANSI_Codepage LCID
my %langinfo = (
"ara" => 1256,
"br" => 1252,
"chh" => 950,
"chs" => 936,
"cht" => 950,
"cs" => 1250,
"da" => 1252,
"el" => 1253,
"es" => 1252,
"fi" => 1252,
"fr" => 1252,
"ger" => 1252,
"heb" => 1255,
"hu" => 1250,
"it" => 1252,
"jpn" => 932,
"kor" => 949,
"nl" => 1252,
"no" => 1252,
"pl" => 1250,
"psu" => 1253,
"pt" => 1252,
"ru" => 1251,
"sv" => 1252,
"tr" => 1254,
"usa" => 1252
);
my $cpage = 1252;
$cpage = $langinfo{$lang} if exists $langinfo{$lang};
my %layids; # List of dirid definitions for layout.inf.
my %unknowns; # Used to keep track of warnings.
######################################################################
# Subroutines used for managing data in the %files data structure.
# Add an installation directory to a file's list.
sub addEntry {
my ($source, $dirid, $path, $name, $bit) = @_;
$source = lc $source;
$name = lc $name;
my $src = $source;
$src = "$3$1" if $source =~ /^([^\\]*)(\\(.*\\))?/;
my $entry = InfData->new($src, $dirid, $path, $name, $bit);
foreach my $ent ( @{ $files{$source} } ) {
return if $ent->addBit($entry);
}
if ( !exists $files{$source} ) {
if ($dirid != 65619) {
$files{$source} = [ () ];
} else {
$unknowns{"$source\t$name"} = "";
return;
}
}
push @{ $files{$source} }, $entry;
}
# Get a list of a file's installation directories.
sub getEntries {
my ($source) = @_;
if ( !exists $files{$source} ) {
print LOG "Unknown file: $source\n";
return ();
}
return @{ $files{$source} };
}
######################################################################
# Start processing.
# Create a log file.
$outfile =~ /^(.*\\)?([^\\]*)$/;
my $outdir = $1;
my $logfile;
$logfile = "$outdir\Efindinfdata.$arch.$lang.log" if !defined $binaries;
$logfile = "$ENV{TEMP}\\findinfdata.$arch$ENV{_BuildType}.$lang.out" if defined $binaries;
if ( !open LOG, ">$logfile" ) {
errmsg( "Unable to open logfile: $logfile" );
die;
}
# Run infscan for each sku, as needed.
my $curbuild = "";
my $tmp = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang";
foreach my $skudata ( @skus ) {
my ($bit, $sku, $cgsku) = split(/\s*,\s*/, $skudata);
my $infscanfile;
$infscanfile = "$outdir\Einfscan.$arch.$lang.$sku.lst" if !defined $binaries;
$infscanfile = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang.$cgsku.lst" if defined $binaries;
my $infdir;
$infdir = "$slpdir\\$sku\\$arch";
if ( $infscan or !-f $infscanfile ) {
logmsg( "Scanning infs for $sku..." );
my $lg;
$lg = "$outdir\Escan.$arch.$lang.$sku.log" if !defined $binaries;
$lg = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang.$cgsku.log" if defined $binaries;
my $filt;
$filt = "$outdir\Efilter.$arch.$lang.$sku.inf" if !defined $binaries;
$filt = "$ENV{TEMP}\\filter.$arch$ENV{_BuildType}.$lang.$cgsku.inf" if defined $binaries;
if ( !$binaries ) {
system "rmdir /s /q $tmp >nul 2>&1";
system "mkdir $tmp >nul 2>&1";
system "copy $infdir\\*.inf $tmp > $lg 2>nul";
system "copy $infdir\\*.in_ $tmp >> $lg 2>nul";
system "copy $infdir\\lang\\*.inf $tmp >> $lg 2>nul";
system "copy $infdir\\lang\\*.in_ $tmp >> $lg 2>nul";
system "expand -r $tmp\\*.in_ >nul 2>nul";
system "del /q $tmp\\*.in_ >nul 2>nul";
} else {
my $build = $buildorder{$sku};
my $temp = $build;
$build =~ s/^\Q$curbuild \E//i;
$curbuild = $temp;
my @build = split(" ", $build);
foreach my $loc ( @build ) {
logmsg "Copying from $loc.";
if ( $loc eq "\\" ) {
system "rmdir /s /q $tmp >nul 2>&1";
system "mkdir $tmp >nul 2>&1";
system "copy $bindir\\lang\\*.inf $tmp >> $lg 2>nul";
}
my $infdir = "$bindir$loc";
system "copy $infdir*.inf $tmp > $lg 2>nul";
}
}
if ( $cpage ) {
foreach my $file ( `dir /b /s $tmp\\*.inf` ) {
chomp $file;
system "unitext -u -$cpage -z $file $file\.2 >nul 2>nul";
system "move /y $file\.2 $file >nul 2>nul";
}
}
logmsg( "Generating filter.inf..." );
system "infscan /R /G /V ntx86 /D /C $filt $tmp >> $lg 2>&1";
logmsg( "Running infscan..." );
system "infscan /G /F $filt /Q $infscanfile $tmp >> $lg 2>&1";
}
}
system "rmdir /s /q $tmp >nul 2>nul";
# Do infscan and layout.inf parse for each sku.
foreach my $skudata ( @skus ) {
my ($bit, $sku, $cgsku) = split(/\s*,\s*/, $skudata);
logmsg( "Processing sku $sku:" );
$skumask |= $bit;
$skucount++;
# Read in the infscan information.
logmsg( "Reading infscan information..." );
my $infscanfile;
$infscanfile = "$outdir\Einfscan.$arch.$lang.$sku.lst" if !defined $binaries;
$infscanfile = "$ENV{TEMP}\\infscan.$arch$ENV{_BuildType}.$lang.$cgsku.lst" if defined $binaries;
if ( !open INFSCAN, $infscanfile ) {
errmsg( "Couldn't find $infscanfile" );
die;
}
while ( <INFSCAN> ) {
if ( /^\"([^\"]*)\",(\d*),(\"([^\"]*)\")?,\"([^\"]*)\",(\d),\"([^\"]*)\"(,\"([^\"]*)\")?\s*$/ ) { # "
my ($source, $dirid, $subdir, $target, $flag, $inf, $sect) = ($1,$2,$4,$5,$6,$7,$9);
if ( $dirid eq "" ) {
$unknowns{"$source\t$target"} = "";
next;
}
$source = "$2\\$1" if $source =~ /^(.*\\)([^\\]*)$/;
$subdir = addPrefix($dirid, lc $subdir, %CanonDir::scanids);
($dirid, $subdir) = removePrefix($subdir, %CanonDir::revids);
addEntry($source, $dirid, $subdir, $target, $bit);
} else {
chomp;
print LOG "Line omitted: $_\n"
}
}
close INFSCAN;
# Read in the layout.inf information.
logmsg( "Reading layout.inf information..." );
my $infdir;
$infdir = "$bindir" if $sku eq "pro";
$infdir = "$bindir\\$cgsku\Einf" if $sku ne "pro";
my %layf;
if ( open INF, "$infdir\\layout.inf" ) {
$_ = parseSect(sub { return; });
while ( /^\[/ ) {
if ( /^\s*\[SourceDisksFiles(\.\w*)?\]\s*$/i and ($1 eq "" or lc$1 eq ".$barch") ) {
# Process a list of files and data.
$_ = parseSect(sub {
my ($layf, $data) = split(/\s*=\s*/,$_[0]);
my @fields = split(/\s*,\s*/, $data);
$layf{lc $layf} = [ @fields ];
} );
}
elsif ( /^\s*\[WinntDirectories\]\s*$/i ) {
# Process a list of directories and their numbers.
$_ = parseSect(sub {
my ($num, $dest) = split(/\s*=\s*/);
$dest = $1 if $dest=~/^\"\\?([^\"]*)\"$/; # "
$layids{$num} = lc $dest;
} );
}
elsif ( /^\s*\[Strings\]\s*$/i ) {
# Process a list of localization strings.
$_ = parseSect( \&parseStr );
}
else {
$_ = parseSect(sub { return; });
}
}
close INF;
# Insert strings into directory names.
foreach my $num (keys %layids) {
$layids{$num} = strSub($layids{$num});
}
# Figure out where the files go.
foreach my $file (keys %layf) {
my @data = @{ $layf{$file} };
my $target = strSub($data[10]);
$target = $file if $target eq "";
if ( $file =~ /\.cat$/i ) {
addEntry($file, 65619, "", $target, $bit);
addEntry($file, 65535, ".cat", $target, $bit);
next;
}
my $ofile = $file;
$file = "$2\\$1" if $file =~ /^(.*\\)([^\\]*)$/;
my $dirid = $data[7];
if ( $dirid ne "" ) {
my $path = addPrefix($dirid, "", %layids);
($dirid, $path) = removePrefix($path, %CanonDir::revids);
addEntry($file, $dirid, $path, $target, $bit);
} else {
$unknowns{"$ofile\t$target"} = "";
}
}
undef %InfParse::strings;
undef %layids;
}
# Determine what entries go in Cache.files.
logmsg( "Reading cache file information..." );
my $cachedir = "$bindir\\congeal_scripts\\autogen\\";
if ( open CACHE, "$cachedir$barch\_$cgsku.h" ) {
while ( <CACHE> ) {
next if !/^\s*{\s*(NULL|L\"([^\"]*)\"\s*),\s*L\".*\\\\([^\\\"]*)\"\s*,.*},?\s*$/; # "
my $src = $3;
$src = $2 if defined $2 and $2 ne "";
$src =~ /^(.*\\)?([^\\]*)$/;
my $dest = $2;
$src = "$2\\$1" if defined $1;
addEntry($src, 65619, "", $dest, $bit);
}
close CACHE;
}
}
# Print various warnings.
foreach my $key (keys %misses) {
my $count = $misses{$key} / $skucount;
print LOG "Unknown dirID ($count+ files affected): $key\n";
}
undef %misses;
foreach my $file (sort keys %unknowns) {
$file = lc $file;
my ($source, $target) = split(/\t/, $file);
if ( !defined $files{$source} ) {
print LOG "Directory not known: $source\n";
next;
}
next if $target eq $source;
my $test = 0;
foreach my $entry (getEntries($source)) {
$test = 1 if lc $entry->{NAME} eq $target;
}
print LOG "Lost target name for $source: $target\n" if !$test;
}
undef %unknowns;
# Handle WOW files.
if ( $arch eq "ia64" ) {
# Get a list of wow files to generate.
logmsg( "Examining WOW files..." );
if ( !-d $wowdir ) {
errmsg( "Unable to find wow directory." );
die;
}
foreach my $wow (`dir /b /s /a-d $wowdir\\w*`) {
$wow =~ s/^\Q$wowdir\E\\//;
if ( $wow =~ /^\s*(.*\\)?w([^\\]*[^\\\s])\s*$/ ) {
my $file = $2;
my $wfile = "w$2";
my $dir = $altpath ? "wow\\$1":"wowbins_uncomp\\$1";
my $key = $wfile;
$key = "$wfile\\$dir" if exists $files{"$wfile\\$dir"};
if ( exists $files{$key} ) {
foreach my $entry ( @{ $files{$key} } ) {
$entry->setSrcDir($dir);
}
my $entries = $files{$key};
delete $files{$key};
$files{"$wfile\\$dir"} = $entries;
}
}
}
}
# Put appropriate stuff in the lang directory.
my %temp;
if ( !$binaries ) {
my $prodir = "$slpdir\\pro\\$arch";
foreach my $file ( `dir /a-d /b $prodir` ) {
$file =~ s/^\Q$prodir\E\\//i;
$file =~ s/\s*$//;
$temp{lc $file} = "";
}
}
my $langdir = "$bindir\\lang";
foreach my $file ( `dir /a-d /b $langdir` ) {
$file =~ s/^\Q$langdir\\//i;
$file =~ s/\s*$//;
$file = lc $file;
next if $file =~ /\\/;
next if !exists $files{$file};
$file =~ /^([^\.]*(\..?.?)).?$/;
next if exists $temp{$file} or exists $temp{"$1_"};
my $list = $files{$file};
delete $files{$file};
foreach my $entry ( @$list ) {
$entry->setSrcDir("lang");
}
$files{"$file\\lang\\"} = $list;
}
undef %temp;
if ( $altpath ) {
foreach my $val ( keys %relpath ) {
delete $relpath{$val} if $val == $skumask;
}
}
# Parse a command file, if specified.
logmsg( "Processing change files..." );
@changes = () if !defined @changes;
foreach my $changefile ( @changes ) {
if ( !open CHANGE, $changefile ) {
errmsg( "Unable to open $changefile." );
die;
}
while ( <CHANGE> ) {
next if /^\s*(\#.*)?$/;
if ( /^\s*([^\=]*\S)\s*=\s*(.*\S)\s*$/ ) {
my $filename = lc $1;
my $file = $filename;
my $cmd = lc $2;
$file = "$2\\$1\\" if $file =~ /^(.*)\\([^\\]*)$/;
if ( defined $files{$file} ) {
if ( $cmd eq "dontwait" ) {
# This file should be copied before reboot.
foreach my $entry ( @{ $files{$file} } ) {
$entry->{WHEN} = "dontdelayuntilreboot";
}
}
elsif ( $cmd =~ /^type\s*:\s*(.*\S)\s*$/ ) {
# This file belongs to a non-obvious inf section group.
my $spec = lc $1;
foreach my $entry ( @{ $files{$file} } ) {
$entry->{SPEC} = $spec;
}
}
elsif ( $cmd =~ /^dir\s*:\s*(.*)$/ ) {
# This file goes in an unusual source directory.
my $newdir = lc $1;
$newdir = "" if !defined $newdir;
my $entries = $files{$file};
foreach my $entry ( @$entries ) {
$entry->setSrcDir($newdir);
}
delete $files{$file};
$file =~ /^([^\\]*)(\\.*)?$/;
$file = $1;
$files{"$file\\$newdir\\"} = $entries;
}
elsif ( $cmd =~ /^flag\s*:\s*(.*)$/ ) {
my $flag = lc $1;
foreach my $entry ( @{ $files{$file} } ) {
next if $entry->{DIRID} == 65619;
$entry->{FLAG} = $flag;
}
}
elsif ( $cmd =~ /^temp\s*:\s*(.*)$/ ) {
my $temp = lc $1;
foreach my $entry ( @{ $files{$file} } ) {
$entry->{TEMP} = $temp;
}
}
else {
wrnmsg "Unknown change file command: $cmd";
}
} else {
wrnmsg "Unknown entry in change file: $filename";
next;
}
}
elsif ( /^\s*ADD\s*/ ) {
while ( <CHANGE> ) {
last if /^\s*END\s*$/;
next if /^\s*(\#.*)?$/;
chomp;
my $entry = InfData->new();
if ( !$entry->readLine($_,\@skuletters) ) {
wrnmsg "Line omitted:\n$_";
next;
}
my $file = $entry->getSrcDir();
$file = $entry->getSrc() . "\\$file" if defined $file;
$file = $entry->getSrc() if !defined $file;
my $test = 0;
foreach my $ent ( @{ $files{$file} } ) {
$test = 1 if $ent->addBit($entry);
}
next if $test;
$files{$file} = [ () ] if !exists $files{$file};
push @{ $files{$file} }, $entry;
}
}
elsif ( /^\s*DELETE\s*/ ) {
while ( <CHANGE> ) {
last if /^\s*END\s*$/;
next if /^\s*(\#.*)?$/;
chomp;
my $entry = InfData->new();
if ( !$entry->readLine($_,\@skuletters) ) {
wrnmsg "Line omitted:\n$_";
next;
}
my $file = $entry->getSrcDir();
if ($file ne "") { $file = $entry->getSrc() . "\\$file"; }
else { $file = $entry->getSrc(); }
next if !exists $files{$file};
my @newlist = ();
foreach my $ent ( @{ $files{$file} } ) {
my $newent = $ent->delBit($entry);
push @newlist, $newent if defined $newent;
}
if ($#newlist >= 0) { $files{$file} = [ @newlist ]; }
else { delete $files{$file}; }
}
}
}
close CHANGE;
}
# If need relative paths, need to find all of the affected files.
logmsg( "Finding different file versions..." );
foreach my $sku ( @pathorder ) {
next if ($infpath{$sku} & $skumask) == 0;
foreach my $file ( `dir /a-d /b /s $bindir\\$sku` ) {
$file =~ s/^\Q$bindir\E\\//i;
chomp $file;
$file =~ /\\([^\\]*)$/;
next if !exists $files{lc $1} and !exists $files{lc $file};
$relfiles{lc $file} = "";
}
}
# Generate the table.
logmsg( "Generating the table..." );
if ( !open OUT, ">$outfile" ) {
errmsg( "Unable to open $outfile." );
die;
}
# Step through the files.
foreach my $file (sort keys %files) {
# Figure out what versions of the file exist.
my @vers = ();
foreach my $entry ( getEntries($file) ) {
my @list = ();
my $mask = $entry->{MASK};
foreach my $ver (@vers) {
$ver = $mask if ($ver|$mask) == $mask;
$mask = 0 if ($ver|$mask) == $ver;
push @list, $ver;
}
push @list, $mask if $mask != 0;
@vers = @list;
}
# Process each entry for each file.
foreach my $entry ( getEntries($file) ) {
my $fullmask;
my $mask = $entry->{MASK};
foreach my $ver (@vers) {
$fullmask = $ver if ($ver|$mask) == $ver;
}
my $fullpath = $entry->getFullPath();
my $myfile = $entry->getSrc();
my $mypath = $entry->getSrcDir();
# Break up by directory if more than one version exists.
foreach my $skudir ( @pathorder ) {
my $pathmask = 0;
my $subdir;
if ( exists $infpath{$skudir} ) {
$pathmask = $infpath{$skudir};
} else {
$pathmask = $fullmask;
}
$subdir = $relpath{$pathmask};
$subdir = "" if !$subdir or (!$altpath and $skudir eq "") or $mypath ne "";
$subdir = "$mypath$subdir" if $mypath ne "\\";
my $filename = "$subdir$myfile";
$pathmask &= $skumask;
$pathmask &= $fullmask;
next if ($pathmask & $mask) == 0;
next if $skudir ne "" and !exists $relfiles{lc "$skudir\\$myfile"};
$fullmask &= ~$pathmask;
# Add the entry needed for this version.
$entry->{MASK} = $pathmask & $mask;
$entry->{NAME} = $filename;
print OUT $entry->getLine(\@skuletters);
print OUT "\n";
}
}
}
close OUT;