@REM ----------------------------------------------------------------- @REM @REM updater.cmd @REM Add entries to update.inf @REM @REM Copyright (c) Microsoft Corporation. All rights reserved. @REM @REM ----------------------------------------------------------------- @perl -x "%~f0" %* @goto :EOF #!perl #line 13 use strict; use Win32::OLE qw(in); use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts"; use lib $ENV{RAZZLETOOLPATH} . "\\sp"; use lib $ENV{RAZZLETOOLPATH}; use PbuildEnv; use ParseArgs; use Logmsg; use InfData; use InfParse; use CanonDir; # Clear error flag $ENV{ERRORS} = 0; sub Usage { print< [-trim] [-qfe] [-c ] files: file containing list of files to search for trim minimize size of resultant INF by compressing and removing entries qfe build an inf for a qfe instead of a service pack change: make changes listed in a change file dirs: make a list of dirs of all of the files USAGE my ($file_list, $ftrim_inf, $change_file, $qfe); parseargs('?' => \&Usage, 'entries:' => \$file_list, 'c:' => \$change_file, 'qfe' => \$qfe, 'trim' => \$ftrim_inf ); my $db_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\infsect.tbl"; my $alt_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\newinf.tbl"; my $diff_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\infdiff.tbl"; my $inf_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\" . ($qfe ? "hotfix1.inf":"update.inf"); my $dirs_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\filelist.txt"; my $out_file = "$ENV{_NTPOSTBLD}\\update.inf"; # # Data # my $archd = (lc$ENV{_BUILDARCH} eq "ia64") ? "ia64":"w32x86"; my $arch = (lc$ENV{_BUILDARCH} eq "ia64") ? "ia64":"i386"; CanonDir::setup( $archd, $arch ); my $type; my $prod; my $boot; my %sects; my %sections; my %missing; my %masks; my %fdirs; my %sectnames; my %addentries; my %catmasks = ( "ip" => 0x1, "ic" => 0x2, "is" => 0x4, "ia" => 0x8, "id" => 0x10, "il" => 0x20, "ib" => 0x40, "" => 0x7f ); my @skuletters = ("p", "c"); my $skumask = ($arch eq "ia64") ? 1:3; my $skucount = ($arch eq "ia64") ? 1:2; my $skumax = ($arch eq "ia64") ? 1:2; # Matching between product names and sku combinations. my %skus = ( "professional" => 0x1, "consumer" => 0x2, "server" => 0x4, "advanced" => 0x8, "datacenter" => 0x10, "business" => 0x20, "blade" => 0x40, "" => 0x7f ); my %revskus = CanonDir::reverseHash(%skus); # Reverse lookup for %skus. my %shortskus = ( 0x1 => "Prf", 0x2 => "Con", 0x4 => "Srv", 0x8 => "Adv", 0x10 => "Dtc", 0x20 => "Sbs", 0x40 => "Bld" ); # # Setup connection to InfGenerator # sub die_ole_errmsg( $ ) { my $text = shift; errmsg( "$text (". Win32::OLE->LastError(). ")" ); exit 1; } system("regsvr32 /s $ENV{RAZZLETOOLPATH}\\sp\\infgen.dll"); my $inf_generator = Win32::OLE->new('InfGenerator'); die_ole_errmsg "Could not instatiate InfGenerator" if ( !defined $inf_generator ); $inf_generator->SetDB( "", "", "", "" ); $inf_generator->InitGen( $inf_file, $out_file ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg "Error starting up InfGenerator (". ($errstr?$errstr:$save_ole_error). ")"; exit 1; } # # Subroutines # # Parse a line from an install section of the inf template. sub parseNames { my ($cmd, $sect) = split(/\s*=\s*/); if ( $cmd =~ /^(\w*)Files$/ ) { $cmd = $1; my $spec = ""; my $mask = lc $prod; if ( !exists $skus{$mask} ) { $spec = $mask; $spec =~ s/\.[^\.]*$//; $mask =~ s/^\Q$spec\E.?//; } ($spec, $mask) = split(/\t/, lc $prod) if $prod =~ /\t/; $mask = $skus{$mask}; $mask = 0 if !defined $mask; $boot="dontdelayuntilreboot" if $sect =~ /drivers\.files$/i; $sects{lc $sect} = InfSect->new($sect) if !exists $sects{lc $sect}; $sects{lc $sect}->setAtts( $type, $mask, $spec, $boot, $cmd ); } } # Parse a database file. sub readDB { my ($file) = @_; my %db = (); if ( !open (DBFILE, $file) ) { errmsg( "Unable to read $file: $!" ); exit 1; } foreach ( ) { chomp; my $line = InfData->new(); if ( !$line->readLine($_, \@skuletters) ) { wrnmsg( "Unrecognized DB-file entry: $_" ); next; } my $dir = $line->getSrcDir(); my $key = $line->getSrc(); $key = "$dir$key" if $dir =~ /^wow\\/i; $db{$key} = [ () ] if !exists $db{$key}; push @{ $db{$key} }, $line; } close DBFILE; return %db; } # Subtract lists of entries. sub subEnts { my ($list1, $list2) = @_; foreach my $entry ( @$list1 ) { my @temp = (); foreach my $ent ( @$list2 ) { my $newent = $ent->delBit2($entry); push @temp, $newent if defined $newent; } $list2 = \@temp; } return $list2; } # Add lists of entries. sub addEnts { my ($list1, $list2) = @_; foreach my $entry ( @$list1 ) { my $test = undef; foreach my $ent ( @$list2 ) { $test = $ent->addBit($entry); last if $test; } push @$list2, $entry if !$test; } return $list2; } # Create a new inf section. sub createSect { my ($entry, $type) = @_; # Figure out what sku(s) we are going to make this section for. my $skubit = 0; foreach my $sku (keys %revskus) { $skubit = $sku if ($sku & $entry->{MASK}) == $sku; } $skubit = $skumask if ($skumask & $entry->{MASK}) == $skumask; return undef if $skubit == 0; return undef if $entry->{PATH} =~ /^\!/; # Figure out the new section's name. my $sectname = addPrefix($entry->{DIRID}, $entry->{PATH}, %CanonDir::updids); if ( $entry->{CMD} ne "copy" ) { $sectname .= ".Delfiles" } else { $sectname .= ".Files" if $sectname !~ /^\.\.\\/; } $sectname =~ s/^system32//i; $sectname =~ s/^\.\.//; $sectname =~ s/^\\//; $sectname =~ s/\s/\_/g; $sectname =~ s/\\/\./g; $sectname =~ s/[\(\)\[\]\!]//g; $sectname = ucfirst $sectname; if ( $type eq "copyfilesalways" ) { $sectname = $shortskus{$skubit}.".".$sectname if $skubit != $skumask; $sectname = "CopyAlways." . $sectname; } else { $sectname = (ucfirst $revskus{$skubit}).".".$sectname if $skubit != $skumask; } if ( exists $sectnames{lc $sectname} ) { my $num = 1; while ( exists $sectnames{lc "$sectname\.$num"} ) { ++$num; } $sectname = "$sectname\.$num"; } # Create the section object. ++$sectnames{lc $sectname}; my $sect = InfSect->new($sectname); $sect->setAtts( $type, $skubit, "", "", $entry->{CMD} ); $sect->setPath( $entry->{DIRID}, $entry->{PATH} ); my $path = lc "$entry->{DIRID},\"$entry->{PATH}\""; $sections{$path} = [] if !exists $sections{$path}; push @{ $sections{$path} }, $sect; # Create the install section entry. my $instsect = "ProductInstall."; if ( $type eq "copyfilesalways" ) { $instsect .= "CopyFilesAlways"; $instsect .= "." . (ucfirst $revskus{$skubit}) if $skubit != $skumask; } else { if ( $skubit == $skumask ) { $instsect .= "ReplaceFilesIfExist"; } else { $instsect .= (ucfirst $revskus{$skubit}) . "Files"; } } my $instline = " " . (ucfirst $entry->{CMD}) . "Files=" . $sectname . "\n"; $addentries{lc$instsect} = [ () ] if !exists $addentries{lc$instsect}; push @{ $addentries{lc$instsect} }, $instline; # Create the DestinationDirs entry. $inf_generator->AddEquality( "DestinationDirs", $sectname, $path ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed writing to section DestinationDirs" ); errmsg( "Content was: $sectname\=$path" ); errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) ); } return $sect; } # Add lines to the inf. sub addLines { my ($list, $type)= @_; foreach my $entry ( @$list ) { my $path = lc $entry->{NAME}; $masks{$path} = InfData->new($path, 65621, "", "", 0) if !exists $masks{$path}; $masks{$path}->{MASK} |= $entry->{MASK}; $masks{$path}->{CMD} = "del" if $entry->{CMD} eq "del"; if ( $entry->{DIRID}==65619 and $entry->{PATH} eq "" ) { #$masks{$path}->setDest($entry->getDest()); $masks{$path}->{SPEC} = $entry->{SPEC}; } while ( $entry->{MASK} != 0 ) { my $section = undef; my $match = 0; foreach my $sect ( @{ $sections{$entry->getFullPath()} } ) { my $temp = $sect->match($entry, $type); if ( $temp > $match ) { $match = $temp; $section = $sect; } } if ( !defined $section ) { $section = createSect($entry, $type); } if ( !defined $section ) { wrnmsg( "No matching section for ".$entry->getLine(\@skuletters) ); last; } $entry->{MASK} &= ~$section->{MASK}; my $data = $entry->getInfLine($section->{FLAG}); if ( $data =~ /=/ ) { errmsg( "Can't have '=' in entry: $entry" ); next; } $inf_generator->WriteSectionData( $section->{NAME}, $data ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed writing to section $section->{NAME}" ); errmsg( "Content was: ". $data ); errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) ); } } } } # Read out entries for a file. sub getEntries { my ($hash, $file, $path) = @_; my @list; my $dir = $path; $dir = "" if $dir eq "\\"; foreach my $entry ( @{ $hash->{$file} } ) { my $srcdir = $entry->getSrcDir(); next if $path !~ /^(lang\\)?$/i and $dir ne $srcdir; push @list, $entry; $fdirs{$srcdir}++; } return \@list; } # Get a file's compressed file name. sub compName { my $file = shift; return $file if $file =~ /\_$/; $file = $1 if $file =~ /^(.*\...).$/; $file .= "." if $file !~ /\...?$/; $file .= "_"; return $file; } # # Read in DB files # my (%base, %new, %diff); %diff = (); %base = readDB($db_file); %new = readDB($alt_file); %diff = readDB($diff_file) if -f $diff_file; # # Get info on inf sections # logmsg( "Parsing template file..." ); if ( !open INF, $inf_file ) { errmsg( "Unable to open $inf_file." ); die; } $_ = parseSect(sub { return; }); while ( /^\[/ ) { my ($sectname) = /^\s*\[([^\]]*)\]\s*$/; $sectnames{lc $sectname}++; if ( /^\s*\[ProductInstall\.(\w*)\.(\w*)\]\s*$/ ) { # Process a list of install section names. $type = lc $1; $prod = lc $2; $boot = ""; if ( $type eq "dontdelayuntilreboot" ) { $boot = $type; $type = "replacefilesifexist"; } $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[ProductInstall\.(\w*)Files\]\s*$/ ) { # Process a list of install section names. $type = "replacefilesifexist"; $prod = lc $1; $boot = ""; $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[ProductInstall\.(\w*)\]\s*$/ ) { # Process a list of install section names. $type = lc $1; $prod = ""; $boot = ""; if ( $type eq "dontdelayuntilreboot" ) { $boot = $type; $type = "replacefilesifexist"; } $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[(\w*)Section(\w*)\]\s*$/ ) { # Process a product specific list of section names. $type = "replacefilesifexist"; $prod = lc $1; $prod .= lc "\t$2" if $2 ne ""; $boot = ""; $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[BuildType\.([IM].)(\.[^\.]*)?\.Section\]\s*$/i ) { # Process a list of section names for volume licensing. $type = ""; $prod = lc $1; $prod = "" if !defined $prod; $prod = $revskus{$catmasks{$prod}}; $prod = "buildtype".(lc$2).".$prod"; $boot = ""; $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[MSN\.no\.ver\]\s*$/ ) { # Special install section. $type = "replacefilesifexist"; $prod = ""; $boot = ""; $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[JVMInstall\]\s*$/ ) { # Special install section. $type = "copyfilesalways"; $prod = ""; $boot = ""; $_ = parseSect( \&parseNames ); } elsif ( /^\s*\[DestinationDirs\]\s*$/ ) { # Find destinations for all these sections. $_ = parseSect(sub { my ($sect, $dir) = split(/\s*=\s*/,$_[0]); if ( $dir !~ /^(\d+)(,\"?([^\"]*)\"?)?$/ ) { # " logmsg "Template line omitted: $_"; return; } my $dirid; my $subdir = addPrefix($1, lc $3, %CanonDir::updids); ($dirid, $subdir) = removePrefix($subdir, %CanonDir::revids); $sects{lc $sect} = InfSect->new($sect) if !exists $sects{lc $sect}; $sects{lc $sect}->setPath( $dirid, $subdir ); } ); } elsif ( /^\s*\[(ProductCatalogsToInstall(\.([^\]]*))?)\]\s*$/i ) { # Add a section for catalog files. my $sect = $1; my $prod = $3; $prod = "" if !defined $prod; my $mask = $catmasks{lc $prod}; if ( defined $mask ) { $sects{lc $sect} = InfSect->new($sect); $sects{lc $sect}->setPath( 65535, ".cat" ); $sects{lc $sect}->setAtts( "", $mask, "", "", "Copy" ); } $_ = parseSect(sub { return; }); } elsif ( /^\s*\[Strings\]\s*$/ ) { # Process a list of localization strings. $_ = parseSect( \&parseStr ); } else { $_ = parseSect(sub { return; }); } } close INF; # Add special sections. $sects{"hals"} = InfSect->new("HALS"); $sects{"hals"}->setPath( 65535, "hals" ); $sects{"hals"}->setAtts( "", $skumask, "", "", "Copy" ); $sects{"windows.driver_cache.i386"} = InfSect->new("Windows.Driver_Cache.i386"); $sects{"windows.driver_cache.i386"}->setPath( 65535, "wdrvc" ); $sects{"windows.driver_cache.i386"}->setAtts( "", $skumask, "", "", "Copy" ); # Process the results. foreach my $sect (values %sects) { my $test = $sect->isComplete(); my $name = $sect->getName(); wrnmsg "Section omitted (sect. unused): $name" if $test & 1; wrnmsg "Section omitted (no directory): $name" if $test & 2; next if $test; my ($dirid, $path) = $sect->getPath(); $sect->resetPath(); $path = addPrefix($dirid, strSub($path), %CanonDir::updids); ($dirid, $path) = removePrefix($path, %CanonDir::revids); $sect->setPath($dirid, $path); $path = lc "$dirid,\"$path\""; $sections{$path} = [] if !exists $sections{$path}; push @{ $sections{$path} }, $sect; } # # Read in a change file # my %oldfiles = (); my %newfiles = (); if ( defined $change_file ) { if ( !open CHANGE, $change_file ) { errmsg( "Unable to open $change_file." ); die; } while ( ) { next if /^\s*(\#.*)?$/; if ( /^\s*([^\=]*\S)\s*=\s*(.*\S)\s*$/ ) { my $filename = lc $1; my $cmd = lc $2; $filename =~ /^(.*\\)?([^\\]*)$/; my $dir = $1; my $file = $2; $file = "$dir$file" if $dir ne "lang\\"; if ( defined $base{$file} or defined $new{$file} ) { if ( $cmd eq "old" ) { # This file is old, merge the new stuff into the old. $oldfiles{$file}++; } elsif ( $cmd =~ /^renamed\s*:\s*(.*\\)?([^\\]*)$/ ) { # This file has been renamed; move the data over to the new name. my ($path, $name) = ($1, $2); $newfiles{$file}++; if ( exists $base{$name} ) { my $list = $base{$name}; foreach my $entry ( @$list ) { $entry->setSrc( $entry->getSrcDir() . $file ); } delete $base{$name}; $base{$file} = [ () ] if !exists $base{$file}; push @{ $base{$file} }, @$list; } } else { wrnmsg "Unknown change file command: $cmd"; } } elsif ( $file =~ /^\[(.*)\]$/ and exists $sects{lc$1} ) { my $sect = lc$1; if ( $cmd =~ /^flag\s*:\s*(.*)$/ ) { $sects{$sect}->{FLAG} = $1; } elsif ( $cmd =~ /^spec\s*:\s*(.*)$/ ) { $sects{$sect}->{SPEC} = $1; } elsif ( $cmd =~ /^when\s*:\s*(.*)$/ ) { $sects{$sect}->{WHEN} = $1; } elsif ( $cmd =~ /^type\s*:\s*(.*)$/ ) { $sects{$sect}->{TYPE} = $1; } else { wrnmsg "Unknown change file command: $cmd"; } } else { wrnmsg "Unknown entry in change file: $filename"; next; } } } close CHANGE; } # # Read in list of files to find # if ( defined $dirs_file ) { if ( !open (DIRS, ">>$dirs_file") ) { errmsg( "Unable to read $dirs_file: $!" ); exit 1; } } if ( !open (INFILE, "$file_list") ) { errmsg( "Unable to read $file_list: $!" ); exit 1; } while ( ) { chomp; $_ = lc $_; my ($tag, $dir, $key) = /^(\S*\s+)?(\S*\\)?([^\\]*)$/; $dir = "" if $tag =~ /u/ or ($dir eq "new\\" and $tag !~ /m/); my $path = $dir; my $name = $key; $dir =~ s/^\\$//; %fdirs = (); $fdirs{$dir}++ if $path !~ /^(lang\\)?$/i; $key = "$dir$key" if $dir =~ /^wow\\/i; # Get the install information for the file. my $list1 = getEntries(\%base, $key, $path); if ( $dir eq "lang\\" ) { if ( exists $fdirs{""} and !exists $fdirs{"lang\\"} ) { foreach my $entry ( @{ $new{$key} } ) { $entry->clearSrcDir() if lc$entry->getSrcDir() eq "lang\\"; } foreach my $entry ( @{ $diff{$key} } ) { $entry->clearSrcDir() if lc$entry->getSrcDir() eq "lang\\"; } } $fdirs{"lang\\"}++; } my $list2 = getEntries(\%new, $key, $path); my $list3 = getEntries(\%diff, $key, $path); # Put everything into the right directory if it's a new file. my $old = ($#$list1 >= 0) && !exists $newfiles{$key}; if ( !$old and exists $fdirs{""} ) { delete $fdirs{""}; $fdirs{"new\\"}++; foreach my $entry ( @$list1 ) { $entry->setSrcDir("new\\") if lc$entry->getSrcDir() eq ""; } foreach my $entry ( @$list2 ) { $entry->setSrcDir("new\\") if lc$entry->getSrcDir() eq ""; } foreach my $entry ( @$list3 ) { $entry->setSrcDir("new\\") if lc$entry->getSrcDir() eq ""; } } if ( $tag =~ /u/i ) { my @dirs = keys %fdirs; if ( $#dirs == 0 and $dirs[0] =~ /^(([im].|new)\\)?$/i ) { %fdirs = (""=>1); foreach my $entry ( @$list1 ) { $entry->clearSrcDir(); } foreach my $entry ( @$list2 ) { $entry->clearSrcDir(); } foreach my $entry ( @$list3 ) { $entry->clearSrcDir(); } } else { wrnmsg( "'u' tag not used: $path$name" ) if $#dirs >= 0; } } # Handle file deletions. my $del = ($tag =~ /d/i); if ( $del ) { if ( $#$list2 >= 0 ) { wrnmsg "Delete file still in use, skipped: $path$name"; next; } if ( $path =~ /^wow\\/i ) { if ( $#$list1 >= 0 or $#$list3 >= 0 ) { logmsg "Deleting WOW file: $path$name"; } else { next; } } foreach my $entry ( @$list1 ) { $entry->{CMD} = "del"; } foreach my $entry ( @$list3 ) { $entry->{CMD} = "del"; } } # If this is media only, do not attempt to install it. my $med = ($tag =~ /m/i); if ( $med ) { my $file = "$dir$name"; my $mask = $skumask; $mask = $catmasks{$1} if $dir =~ /^(.*)\\$/ and exists $catmasks{$1}; $masks{$file} = InfData->new($file, 65621, "", "", $mask); if ( !$del ) { print DIRS "$file\n"; next; } else { $masks{$file}->{CMD} = "del"; } ++$fdirs{$dir}; $list1 = [ () ]; $list2 = [ () ]; $list3 = [ () ]; } # See what files we got. if ( defined $dirs_file and !$del ) { foreach my $key ( keys %fdirs ) { my $file = "$key$name"; print DIRS "$file\n"; } } if ( $#$list1 < 0 and $#$list2 < 0 and $#$list3 < 0 and (!$del or !$med) ) { logmsg "File omitted: $key"; next; } # Break the lists into copy always and replace if exists. $list2 = subEnts($list1, $list2); $list3 = subEnts($list2, $list3); $list1 = addEnts($list3, $list1); # Add lines for the file. my $type = "copyfilesalways"; $type = "replacefilesifexist" if exists $oldfiles{$key}; addLines($list1, "replacefilesifexist"); addLines($list2, $type); } close INFILE; close DIRS if defined $dirs_file; # Generate SourceDisksFiles entries and ServicePackFiles entries. my %delfiles; my %catfiles; foreach my $path ( keys %masks ) { my $entry = $masks{$path}; if ( $entry->{CMD} ne "del" ) { my $wow = ($entry->getSrcDir() =~ /^wow\\/i ) ? ".WOW":""; my $lang = ($entry->getSrcDir() =~ /lang\\/i ) ? ".Lang":""; my $data = $entry->getInfLine(0); my $mask = $entry->{MASK}; if ( !$qfe and lc $entry->{NAME} ne "msjavx86.exe" ) { foreach my $sku ( keys %catmasks ) { next if $sku eq ""; next if ($mask & $catmasks{$sku}) == 0; my $section = "ServicePackFiles$wow$lang\.".(uc $sku).".Files"; $inf_generator->WriteSectionData( $section, $data ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed writing to section $section" ); errmsg( "Content was: ". $data ); errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) ); } } } $inf_generator->AddSourceDisksFilesEntry( $path, 1 ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed writing to section SourceDisksFiles" ); errmsg( "Content was: $path" ); errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) ); } } else { if ( !$qfe ) { my $file = $entry->{NAME}; $file =~ s/^([im].|new)\\//i; $file =~ s/^wow\\/..\\i386\\/i; next if exists $delfiles{$file}; $delfiles{$file}++; $inf_generator->WriteSectionData( "ServicePackFilesDelete.files", $file ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed writing to section ServicePackFilesDelete.files" ); errmsg( "Content was: ". $file ); errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) ); } } } if ( $path =~ /^(.*\\)?([^\\]*\.cat)$/i ) { my $file = $2; next if exists $catfiles{$file}; ++$catfiles{$file}; $inf_generator->WriteSectionData( "ArchiveCatalogFilesOnly", $file ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed writing to section ArchiveCatalogFilesOnly" ); errmsg( "Content was: ". $file ); errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) ); } } } logmsg( "Saving new INF file ..." ); # Trim and save new INF file $inf_generator->CloseGen( $ftrim_inf?1:0 ); if ( Win32::OLE->LastError() ) { my $save_ole_error = Win32::OLE->LastError(); my $errstr = $inf_generator->{InfGenError}; chomp $errstr; errmsg( "Failed trimming/saving file (". ($errstr?$errstr:$save_ole_error). ")" ); } # Add final entries. if ( !open OUT, $out_file ) { errmsg( "Unable to find outputted update.inf." ); } my @inf = ; close OUT; if ( !open OUT, ">$out_file" ) { errmsg( "Unable to edit outputted update.inf." ); } foreach (@inf) { print OUT; if ( /^\s*\[([^\]]*)\]\s*$/ ) { my $sect = lc $1; next if !exists $addentries{$sect}; foreach my $line ( @{ $addentries{$sect} } ) { print OUT $line; } delete $addentries{$sect}; } } foreach my $sect (keys %addentries) { print OUT "\n\[$sect\]\n"; foreach my $line ( @{ $addentries{$sect} } ) { print OUT $line; } } close OUT; if ( $ENV{ERRORS} ) { logmsg( "Errors during execution" ); exit 1; } else { logmsg( "Success" ); exit 0; } ############################################################################ package InfSect; use strict; use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts"; use lib $ENV{RAZZLETOOLPATH}; use Logmsg; use InfData; # Create new inf section with just a name. sub new { my $class = shift; my ($name) = @_; my $self = {}; $self->{NAME} = $name; # Section name. $self->{TYPE} = undef; # Replace or copy always. $self->{MASK} = 0; # Skus that applies to. $self->{SPEC} = undef; # Special groups. $self->{WHEN} = undef; # Before or after reboot. $self->{CMD} = undef; # Copy or delete. $self->{DIRID} = undef; # DirID for install directory. $self->{PATH} = undef; # Rest of install path. $self->{FLAG} = 0; # Flag to insert for files in this section. return bless $self; } # Set the section's basic attributes. sub setAtts { my $self = shift; my ($type, $mask, $spec, $when, $cmd) = @_; if ( defined $self->{TYPE} and $self->{TYPE} ne $type) { wrnmsg "Differing section type for $self->{NAME}: $self->{TYPE} $type"; } if ( $self->{MASK} != 0 and $self->{MASK} ne $mask) { wrnmsg "Differing sku set for $self->{NAME}: $self->{MASK} $mask"; } if ( defined $self->{SPEC} and $self->{SPEC} ne $spec) { wrnmsg "Differing section group for $self->{NAME}: $self->{SPEC} $spec"; } if ( defined $self->{WHEN} and $self->{WHEN} ne $when) { wrnmsg "Differing section install time for $self->{NAME}: $self->{WHEN} $when"; } if ( defined $self->{CMD} and $self->{CMD} ne $cmd) { wrnmsg "Differing section command for $self->{CMD}: $self->{CMD} $cmd"; } $self->{TYPE} = $type; $self->{MASK} = $mask; $self->{SPEC} = $spec; $self->{WHEN} = $when; $self->{CMD} = $cmd; } # Set the section's installation directory. sub setPath { my $self = shift; my ($dirid, $path) = @_; if ( (defined $self->{DIRID} and $self->{DIRID} ne $dirid) or (defined $self->{PATH} and $self->{PATH} ne $path) ) { wrnmsg "Differing path for $self->{NAME}: $self->{DIRID},$self->{PATH} $dirid,$path"; } $self->{DIRID} = $dirid; $self->{PATH} = $path; } # Clear the section's directory information. sub resetPath { my $self = shift; $self->{DIRID} = undef; $self->{PATH} = undef; } # Find a mask for the product field. sub getMask { my $self = shift; return $self->{MASK}; } # Get the section's installation directory. sub getPath { my $self = shift; return ($self->{DIRID}, $self->{PATH}); } # Get the inf section's name. sub getName { my $self = shift; return $self->{NAME}; } # See if all the fields have been specified. sub isComplete { my $self = shift; my $flags = 0; $flags |= 1 if !defined $self->{CMD}; $flags |= 1 if !defined $self->{WHEN}; $flags |= 1 if !defined $self->{TYPE}; $flags |= 1 if $self->{MASK} == 0; $flags |= 1 if !defined $self->{SPEC}; $flags |= 2 if !defined $self->{DIRID}; return $flags; } # See how well this item matches a template. sub match { my $self = shift; my ($template, $type) = @_; # $skumask, $skumax, $skucount, $all # Basic comparison. return 0 if lc $self->{CMD} ne $template->{CMD}; return 0 if lc $self->{DIRID} ne lc $template->{DIRID}; return 0 if lc $self->{PATH} ne lc $template->{PATH}; return 0 if lc $self->{TYPE} ne $type and $self->{TYPE} ne ""; my $count = 1; # Test to see if the skus match. my $mask = $self->getMask(); return 0 if ($template->{MASK} & $mask & $skumask) == 0; my $match = ($template->{MASK} ^ $mask) & $skumask; if ( $match == 0 ) { # Section matches. $count += $skucount; } elsif ( ($match & $mask) != 0 ) { # Section is a superset. Bad. return 0; } elsif ( ($match & $template->{MASK}) != 0 && $mask != 0 ) { # Section is a subset. Prefer the closest matches. $mask = $match & $template->{MASK}; for (my $i=0; $i<$skumax; ++$i) { $count -= 1 if ($mask & 1) == 0; $mask = $mask >> 1; } $count += $skucount; } # Make sure they belong to the same group. if ( lc $self->{SPEC} eq lc $template->{SPEC} ) { $count += 0.5; } elsif ( $self->{SPEC} eq "" ) { $count += 0.2; } # Are both copy before reboot? if ( lc $self->{WHEN} eq lc $template->{WHEN} ) { $count += 0.005; } # Prefer broader product groups. if ( $self->{MASK} == $skus{""} ) { $count += 0.0005; } return $count; } 1;