Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

984 lines
30 KiB

  1. @REM -----------------------------------------------------------------
  2. @REM
  3. @REM updater.cmd
  4. @REM Add entries to update.inf
  5. @REM
  6. @REM Copyright (c) Microsoft Corporation. All rights reserved.
  7. @REM
  8. @REM -----------------------------------------------------------------
  9. @perl -x "%~f0" %*
  10. @goto :EOF
  11. #!perl
  12. #line 13
  13. use strict;
  14. use Win32::OLE qw(in);
  15. use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
  16. use lib $ENV{RAZZLETOOLPATH} . "\\sp";
  17. use lib $ENV{RAZZLETOOLPATH};
  18. use PbuildEnv;
  19. use ParseArgs;
  20. use Logmsg;
  21. use InfData;
  22. use InfParse;
  23. use CanonDir;
  24. # Clear error flag
  25. $ENV{ERRORS} = 0;
  26. sub Usage { print<<USAGE; exit(1) }
  27. updater.cmd -entries:<files> [-trim] [-qfe] [-c <change>]
  28. files: file containing list of files to search for
  29. trim minimize size of resultant INF by compressing and removing entries
  30. qfe build an inf for a qfe instead of a service pack
  31. change: make changes listed in a change file
  32. dirs: make a list of dirs of all of the files
  33. USAGE
  34. my ($file_list, $ftrim_inf, $change_file, $qfe);
  35. parseargs('?' => \&Usage,
  36. 'entries:' => \$file_list,
  37. 'c:' => \$change_file,
  38. 'qfe' => \$qfe,
  39. 'trim' => \$ftrim_inf );
  40. my $db_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\infsect.tbl";
  41. my $alt_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\newinf.tbl";
  42. my $diff_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\infdiff.tbl";
  43. my $inf_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\" . ($qfe ? "hotfix1.inf":"update.inf");
  44. my $dirs_file = "$ENV{_NTPOSTBLD}\\idw\\srvpack\\filelist.txt";
  45. my $out_file = "$ENV{_NTPOSTBLD}\\update.inf";
  46. #
  47. # Data
  48. #
  49. my $archd = (lc$ENV{_BUILDARCH} eq "ia64") ? "ia64":"w32x86";
  50. my $arch = (lc$ENV{_BUILDARCH} eq "ia64") ? "ia64":"i386";
  51. CanonDir::setup( $archd, $arch );
  52. my $type;
  53. my $prod;
  54. my $boot;
  55. my %sects;
  56. my %sections;
  57. my %missing;
  58. my %masks;
  59. my %fdirs;
  60. my %sectnames;
  61. my %addentries;
  62. my %catmasks = (
  63. "ip" => 0x1,
  64. "ic" => 0x2,
  65. "is" => 0x4,
  66. "ia" => 0x8,
  67. "id" => 0x10,
  68. "il" => 0x20,
  69. "ib" => 0x40,
  70. "" => 0x7f
  71. );
  72. my @skuletters = ("p", "c");
  73. my $skumask = ($arch eq "ia64") ? 1:3;
  74. my $skucount = ($arch eq "ia64") ? 1:2;
  75. my $skumax = ($arch eq "ia64") ? 1:2;
  76. # Matching between product names and sku combinations.
  77. my %skus = (
  78. "professional" => 0x1,
  79. "consumer" => 0x2,
  80. "server" => 0x4,
  81. "advanced" => 0x8,
  82. "datacenter" => 0x10,
  83. "business" => 0x20,
  84. "blade" => 0x40,
  85. "" => 0x7f
  86. );
  87. my %revskus = CanonDir::reverseHash(%skus); # Reverse lookup for %skus.
  88. my %shortskus = (
  89. 0x1 => "Prf",
  90. 0x2 => "Con",
  91. 0x4 => "Srv",
  92. 0x8 => "Adv",
  93. 0x10 => "Dtc",
  94. 0x20 => "Sbs",
  95. 0x40 => "Bld"
  96. );
  97. #
  98. # Setup connection to InfGenerator
  99. #
  100. sub die_ole_errmsg( $ ) {
  101. my $text = shift;
  102. errmsg( "$text (". Win32::OLE->LastError(). ")" );
  103. exit 1;
  104. }
  105. system("regsvr32 /s $ENV{RAZZLETOOLPATH}\\sp\\infgen.dll");
  106. my $inf_generator = Win32::OLE->new('InfGenerator');
  107. die_ole_errmsg "Could not instatiate InfGenerator" if ( !defined $inf_generator );
  108. $inf_generator->SetDB( "", "", "", "" );
  109. $inf_generator->InitGen( $inf_file, $out_file );
  110. if ( Win32::OLE->LastError() ) {
  111. my $save_ole_error = Win32::OLE->LastError();
  112. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  113. errmsg "Error starting up InfGenerator (". ($errstr?$errstr:$save_ole_error). ")";
  114. exit 1;
  115. }
  116. #
  117. # Subroutines
  118. #
  119. # Parse a line from an install section of the inf template.
  120. sub parseNames {
  121. my ($cmd, $sect) = split(/\s*=\s*/);
  122. if ( $cmd =~ /^(\w*)Files$/ ) {
  123. $cmd = $1;
  124. my $spec = "";
  125. my $mask = lc $prod;
  126. if ( !exists $skus{$mask} ) {
  127. $spec = $mask;
  128. $spec =~ s/\.[^\.]*$//;
  129. $mask =~ s/^\Q$spec\E.?//;
  130. }
  131. ($spec, $mask) = split(/\t/, lc $prod) if $prod =~ /\t/;
  132. $mask = $skus{$mask};
  133. $mask = 0 if !defined $mask;
  134. $boot="dontdelayuntilreboot" if $sect =~ /drivers\.files$/i;
  135. $sects{lc $sect} = InfSect->new($sect) if !exists $sects{lc $sect};
  136. $sects{lc $sect}->setAtts( $type, $mask, $spec, $boot, $cmd );
  137. }
  138. }
  139. # Parse a database file.
  140. sub readDB {
  141. my ($file) = @_;
  142. my %db = ();
  143. if ( !open (DBFILE, $file) ) {
  144. errmsg( "Unable to read $file: $!" );
  145. exit 1;
  146. }
  147. foreach ( <DBFILE> ) {
  148. chomp;
  149. my $line = InfData->new();
  150. if ( !$line->readLine($_, \@skuletters) ) {
  151. wrnmsg( "Unrecognized DB-file entry: $_" );
  152. next;
  153. }
  154. my $dir = $line->getSrcDir();
  155. my $key = $line->getSrc();
  156. $key = "$dir$key" if $dir =~ /^wow\\/i;
  157. $db{$key} = [ () ] if !exists $db{$key};
  158. push @{ $db{$key} }, $line;
  159. }
  160. close DBFILE;
  161. return %db;
  162. }
  163. # Subtract lists of entries.
  164. sub subEnts {
  165. my ($list1, $list2) = @_;
  166. foreach my $entry ( @$list1 ) {
  167. my @temp = ();
  168. foreach my $ent ( @$list2 ) {
  169. my $newent = $ent->delBit2($entry);
  170. push @temp, $newent if defined $newent;
  171. }
  172. $list2 = \@temp;
  173. }
  174. return $list2;
  175. }
  176. # Add lists of entries.
  177. sub addEnts {
  178. my ($list1, $list2) = @_;
  179. foreach my $entry ( @$list1 ) {
  180. my $test = undef;
  181. foreach my $ent ( @$list2 ) {
  182. $test = $ent->addBit($entry);
  183. last if $test;
  184. }
  185. push @$list2, $entry if !$test;
  186. }
  187. return $list2;
  188. }
  189. # Create a new inf section.
  190. sub createSect {
  191. my ($entry, $type) = @_;
  192. # Figure out what sku(s) we are going to make this section for.
  193. my $skubit = 0;
  194. foreach my $sku (keys %revskus) {
  195. $skubit = $sku if ($sku & $entry->{MASK}) == $sku;
  196. }
  197. $skubit = $skumask if ($skumask & $entry->{MASK}) == $skumask;
  198. return undef if $skubit == 0;
  199. return undef if $entry->{PATH} =~ /^\!/;
  200. # Figure out the new section's name.
  201. my $sectname = addPrefix($entry->{DIRID}, $entry->{PATH}, %CanonDir::updids);
  202. if ( $entry->{CMD} ne "copy" ) {
  203. $sectname .= ".Delfiles"
  204. } else {
  205. $sectname .= ".Files" if $sectname !~ /^\.\.\\/;
  206. }
  207. $sectname =~ s/^system32//i;
  208. $sectname =~ s/^\.\.//;
  209. $sectname =~ s/^\\//;
  210. $sectname =~ s/\s/\_/g;
  211. $sectname =~ s/\\/\./g;
  212. $sectname =~ s/[\(\)\[\]\!]//g;
  213. $sectname = ucfirst $sectname;
  214. if ( $type eq "copyfilesalways" ) {
  215. $sectname = $shortskus{$skubit}.".".$sectname if $skubit != $skumask;
  216. $sectname = "CopyAlways." . $sectname;
  217. } else {
  218. $sectname = (ucfirst $revskus{$skubit}).".".$sectname if $skubit != $skumask;
  219. }
  220. if ( exists $sectnames{lc $sectname} ) {
  221. my $num = 1;
  222. while ( exists $sectnames{lc "$sectname\.$num"} ) {
  223. ++$num;
  224. }
  225. $sectname = "$sectname\.$num";
  226. }
  227. # Create the section object.
  228. ++$sectnames{lc $sectname};
  229. my $sect = InfSect->new($sectname);
  230. $sect->setAtts( $type, $skubit, "", "", $entry->{CMD} );
  231. $sect->setPath( $entry->{DIRID}, $entry->{PATH} );
  232. my $path = lc "$entry->{DIRID},\"$entry->{PATH}\"";
  233. $sections{$path} = [] if !exists $sections{$path};
  234. push @{ $sections{$path} }, $sect;
  235. # Create the install section entry.
  236. my $instsect = "ProductInstall.";
  237. if ( $type eq "copyfilesalways" ) {
  238. $instsect .= "CopyFilesAlways";
  239. $instsect .= "." . (ucfirst $revskus{$skubit}) if $skubit != $skumask;
  240. } else {
  241. if ( $skubit == $skumask ) {
  242. $instsect .= "ReplaceFilesIfExist";
  243. } else {
  244. $instsect .= (ucfirst $revskus{$skubit}) . "Files";
  245. }
  246. }
  247. my $instline = " " . (ucfirst $entry->{CMD}) . "Files=" . $sectname . "\n";
  248. $addentries{lc$instsect} = [ () ] if !exists $addentries{lc$instsect};
  249. push @{ $addentries{lc$instsect} }, $instline;
  250. # Create the DestinationDirs entry.
  251. $inf_generator->AddEquality( "DestinationDirs", $sectname, $path );
  252. if ( Win32::OLE->LastError() ) {
  253. my $save_ole_error = Win32::OLE->LastError();
  254. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  255. errmsg( "Failed writing to section DestinationDirs" );
  256. errmsg( "Content was: $sectname\=$path" );
  257. errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) );
  258. }
  259. return $sect;
  260. }
  261. # Add lines to the inf.
  262. sub addLines {
  263. my ($list, $type)= @_;
  264. foreach my $entry ( @$list ) {
  265. my $path = lc $entry->{NAME};
  266. $masks{$path} = InfData->new($path, 65621, "", "", 0) if !exists $masks{$path};
  267. $masks{$path}->{MASK} |= $entry->{MASK};
  268. $masks{$path}->{CMD} = "del" if $entry->{CMD} eq "del";
  269. if ( $entry->{DIRID}==65619 and $entry->{PATH} eq "" ) {
  270. #$masks{$path}->setDest($entry->getDest());
  271. $masks{$path}->{SPEC} = $entry->{SPEC};
  272. }
  273. while ( $entry->{MASK} != 0 ) {
  274. my $section = undef;
  275. my $match = 0;
  276. foreach my $sect ( @{ $sections{$entry->getFullPath()} } ) {
  277. my $temp = $sect->match($entry, $type);
  278. if ( $temp > $match ) {
  279. $match = $temp;
  280. $section = $sect;
  281. }
  282. }
  283. if ( !defined $section ) {
  284. $section = createSect($entry, $type);
  285. }
  286. if ( !defined $section ) {
  287. wrnmsg( "No matching section for ".$entry->getLine(\@skuletters) );
  288. last;
  289. }
  290. $entry->{MASK} &= ~$section->{MASK};
  291. my $data = $entry->getInfLine($section->{FLAG});
  292. if ( $data =~ /=/ ) {
  293. errmsg( "Can't have '=' in entry: $entry" );
  294. next;
  295. }
  296. $inf_generator->WriteSectionData( $section->{NAME}, $data );
  297. if ( Win32::OLE->LastError() ) {
  298. my $save_ole_error = Win32::OLE->LastError();
  299. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  300. errmsg( "Failed writing to section $section->{NAME}" );
  301. errmsg( "Content was: ". $data );
  302. errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) );
  303. }
  304. }
  305. }
  306. }
  307. # Read out entries for a file.
  308. sub getEntries {
  309. my ($hash, $file, $path) = @_;
  310. my @list;
  311. my $dir = $path;
  312. $dir = "" if $dir eq "\\";
  313. foreach my $entry ( @{ $hash->{$file} } ) {
  314. my $srcdir = $entry->getSrcDir();
  315. next if $path !~ /^(lang\\)?$/i and $dir ne $srcdir;
  316. push @list, $entry;
  317. $fdirs{$srcdir}++;
  318. }
  319. return \@list;
  320. }
  321. # Get a file's compressed file name.
  322. sub compName {
  323. my $file = shift;
  324. return $file if $file =~ /\_$/;
  325. $file = $1 if $file =~ /^(.*\...).$/;
  326. $file .= "." if $file !~ /\...?$/;
  327. $file .= "_";
  328. return $file;
  329. }
  330. #
  331. # Read in DB files
  332. #
  333. my (%base, %new, %diff);
  334. %diff = ();
  335. %base = readDB($db_file);
  336. %new = readDB($alt_file);
  337. %diff = readDB($diff_file) if -f $diff_file;
  338. #
  339. # Get info on inf sections
  340. #
  341. logmsg( "Parsing template file..." );
  342. if ( !open INF, $inf_file ) {
  343. errmsg( "Unable to open $inf_file." );
  344. die;
  345. }
  346. $_ = parseSect(sub { return; });
  347. while ( /^\[/ ) {
  348. my ($sectname) = /^\s*\[([^\]]*)\]\s*$/;
  349. $sectnames{lc $sectname}++;
  350. if ( /^\s*\[ProductInstall\.(\w*)\.(\w*)\]\s*$/ ) {
  351. # Process a list of install section names.
  352. $type = lc $1;
  353. $prod = lc $2;
  354. $boot = "";
  355. if ( $type eq "dontdelayuntilreboot" ) {
  356. $boot = $type;
  357. $type = "replacefilesifexist";
  358. }
  359. $_ = parseSect( \&parseNames );
  360. }
  361. elsif ( /^\s*\[ProductInstall\.(\w*)Files\]\s*$/ ) {
  362. # Process a list of install section names.
  363. $type = "replacefilesifexist";
  364. $prod = lc $1;
  365. $boot = "";
  366. $_ = parseSect( \&parseNames );
  367. }
  368. elsif ( /^\s*\[ProductInstall\.(\w*)\]\s*$/ ) {
  369. # Process a list of install section names.
  370. $type = lc $1;
  371. $prod = "";
  372. $boot = "";
  373. if ( $type eq "dontdelayuntilreboot" ) {
  374. $boot = $type;
  375. $type = "replacefilesifexist";
  376. }
  377. $_ = parseSect( \&parseNames );
  378. }
  379. elsif ( /^\s*\[(\w*)Section(\w*)\]\s*$/ ) {
  380. # Process a product specific list of section names.
  381. $type = "replacefilesifexist";
  382. $prod = lc $1;
  383. $prod .= lc "\t$2" if $2 ne "";
  384. $boot = "";
  385. $_ = parseSect( \&parseNames );
  386. }
  387. elsif ( /^\s*\[BuildType\.([IM].)(\.[^\.]*)?\.Section\]\s*$/i ) {
  388. # Process a list of section names for volume licensing.
  389. $type = "";
  390. $prod = lc $1;
  391. $prod = "" if !defined $prod;
  392. $prod = $revskus{$catmasks{$prod}};
  393. $prod = "buildtype".(lc$2).".$prod";
  394. $boot = "";
  395. $_ = parseSect( \&parseNames );
  396. }
  397. elsif ( /^\s*\[MSN\.no\.ver\]\s*$/ ) {
  398. # Special install section.
  399. $type = "replacefilesifexist";
  400. $prod = "";
  401. $boot = "";
  402. $_ = parseSect( \&parseNames );
  403. }
  404. elsif ( /^\s*\[JVMInstall\]\s*$/ ) {
  405. # Special install section.
  406. $type = "copyfilesalways";
  407. $prod = "";
  408. $boot = "";
  409. $_ = parseSect( \&parseNames );
  410. }
  411. elsif ( /^\s*\[DestinationDirs\]\s*$/ ) {
  412. # Find destinations for all these sections.
  413. $_ = parseSect(sub {
  414. my ($sect, $dir) = split(/\s*=\s*/,$_[0]);
  415. if ( $dir !~ /^(\d+)(,\"?([^\"]*)\"?)?$/ ) { # "
  416. logmsg "Template line omitted: $_";
  417. return;
  418. }
  419. my $dirid;
  420. my $subdir = addPrefix($1, lc $3, %CanonDir::updids);
  421. ($dirid, $subdir) = removePrefix($subdir, %CanonDir::revids);
  422. $sects{lc $sect} = InfSect->new($sect) if !exists $sects{lc $sect};
  423. $sects{lc $sect}->setPath( $dirid, $subdir );
  424. } );
  425. }
  426. elsif ( /^\s*\[(ProductCatalogsToInstall(\.([^\]]*))?)\]\s*$/i ) {
  427. # Add a section for catalog files.
  428. my $sect = $1;
  429. my $prod = $3;
  430. $prod = "" if !defined $prod;
  431. my $mask = $catmasks{lc $prod};
  432. if ( defined $mask ) {
  433. $sects{lc $sect} = InfSect->new($sect);
  434. $sects{lc $sect}->setPath( 65535, ".cat" );
  435. $sects{lc $sect}->setAtts( "", $mask, "", "", "Copy" );
  436. }
  437. $_ = parseSect(sub { return; });
  438. }
  439. elsif ( /^\s*\[Strings\]\s*$/ ) {
  440. # Process a list of localization strings.
  441. $_ = parseSect( \&parseStr );
  442. }
  443. else {
  444. $_ = parseSect(sub { return; });
  445. }
  446. }
  447. close INF;
  448. # Add special sections.
  449. $sects{"hals"} = InfSect->new("HALS");
  450. $sects{"hals"}->setPath( 65535, "hals" );
  451. $sects{"hals"}->setAtts( "", $skumask, "", "", "Copy" );
  452. $sects{"windows.driver_cache.i386"} = InfSect->new("Windows.Driver_Cache.i386");
  453. $sects{"windows.driver_cache.i386"}->setPath( 65535, "wdrvc" );
  454. $sects{"windows.driver_cache.i386"}->setAtts( "", $skumask, "", "", "Copy" );
  455. # Process the results.
  456. foreach my $sect (values %sects) {
  457. my $test = $sect->isComplete();
  458. my $name = $sect->getName();
  459. wrnmsg "Section omitted (sect. unused): $name" if $test & 1;
  460. wrnmsg "Section omitted (no directory): $name" if $test & 2;
  461. next if $test;
  462. my ($dirid, $path) = $sect->getPath();
  463. $sect->resetPath();
  464. $path = addPrefix($dirid, strSub($path), %CanonDir::updids);
  465. ($dirid, $path) = removePrefix($path, %CanonDir::revids);
  466. $sect->setPath($dirid, $path);
  467. $path = lc "$dirid,\"$path\"";
  468. $sections{$path} = [] if !exists $sections{$path};
  469. push @{ $sections{$path} }, $sect;
  470. }
  471. #
  472. # Read in a change file
  473. #
  474. my %oldfiles = ();
  475. my %newfiles = ();
  476. if ( defined $change_file ) {
  477. if ( !open CHANGE, $change_file ) {
  478. errmsg( "Unable to open $change_file." );
  479. die;
  480. }
  481. while ( <CHANGE> ) {
  482. next if /^\s*(\#.*)?$/;
  483. if ( /^\s*([^\=]*\S)\s*=\s*(.*\S)\s*$/ ) {
  484. my $filename = lc $1;
  485. my $cmd = lc $2;
  486. $filename =~ /^(.*\\)?([^\\]*)$/;
  487. my $dir = $1;
  488. my $file = $2;
  489. $file = "$dir$file" if $dir ne "lang\\";
  490. if ( defined $base{$file} or defined $new{$file} ) {
  491. if ( $cmd eq "old" ) {
  492. # This file is old, merge the new stuff into the old.
  493. $oldfiles{$file}++;
  494. }
  495. elsif ( $cmd =~ /^renamed\s*:\s*(.*\\)?([^\\]*)$/ ) {
  496. # This file has been renamed; move the data over to the new name.
  497. my ($path, $name) = ($1, $2);
  498. $newfiles{$file}++;
  499. if ( exists $base{$name} ) {
  500. my $list = $base{$name};
  501. foreach my $entry ( @$list ) {
  502. $entry->setSrc( $entry->getSrcDir() . $file );
  503. }
  504. delete $base{$name};
  505. $base{$file} = [ () ] if !exists $base{$file};
  506. push @{ $base{$file} }, @$list;
  507. }
  508. }
  509. else {
  510. wrnmsg "Unknown change file command: $cmd";
  511. }
  512. }
  513. elsif ( $file =~ /^\[(.*)\]$/ and exists $sects{lc$1} ) {
  514. my $sect = lc$1;
  515. if ( $cmd =~ /^flag\s*:\s*(.*)$/ ) {
  516. $sects{$sect}->{FLAG} = $1;
  517. }
  518. elsif ( $cmd =~ /^spec\s*:\s*(.*)$/ ) {
  519. $sects{$sect}->{SPEC} = $1;
  520. }
  521. elsif ( $cmd =~ /^when\s*:\s*(.*)$/ ) {
  522. $sects{$sect}->{WHEN} = $1;
  523. }
  524. elsif ( $cmd =~ /^type\s*:\s*(.*)$/ ) {
  525. $sects{$sect}->{TYPE} = $1;
  526. }
  527. else {
  528. wrnmsg "Unknown change file command: $cmd";
  529. }
  530. } else {
  531. wrnmsg "Unknown entry in change file: $filename";
  532. next;
  533. }
  534. }
  535. }
  536. close CHANGE;
  537. }
  538. #
  539. # Read in list of files to find
  540. #
  541. if ( defined $dirs_file ) {
  542. if ( !open (DIRS, ">>$dirs_file") ) {
  543. errmsg( "Unable to read $dirs_file: $!" );
  544. exit 1;
  545. }
  546. }
  547. if ( !open (INFILE, "$file_list") ) {
  548. errmsg( "Unable to read $file_list: $!" );
  549. exit 1;
  550. }
  551. while ( <INFILE> ) {
  552. chomp;
  553. $_ = lc $_;
  554. my ($tag, $dir, $key) = /^(\S*\s+)?(\S*\\)?([^\\]*)$/;
  555. $dir = "" if $tag =~ /u/ or ($dir eq "new\\" and $tag !~ /m/);
  556. my $path = $dir;
  557. my $name = $key;
  558. $dir =~ s/^\\$//;
  559. %fdirs = ();
  560. $fdirs{$dir}++ if $path !~ /^(lang\\)?$/i;
  561. $key = "$dir$key" if $dir =~ /^wow\\/i;
  562. # Get the install information for the file.
  563. my $list1 = getEntries(\%base, $key, $path);
  564. if ( $dir eq "lang\\" ) {
  565. if ( exists $fdirs{""} and !exists $fdirs{"lang\\"} ) {
  566. foreach my $entry ( @{ $new{$key} } ) {
  567. $entry->clearSrcDir() if lc$entry->getSrcDir() eq "lang\\";
  568. }
  569. foreach my $entry ( @{ $diff{$key} } ) {
  570. $entry->clearSrcDir() if lc$entry->getSrcDir() eq "lang\\";
  571. }
  572. }
  573. $fdirs{"lang\\"}++;
  574. }
  575. my $list2 = getEntries(\%new, $key, $path);
  576. my $list3 = getEntries(\%diff, $key, $path);
  577. # Put everything into the right directory if it's a new file.
  578. my $old = ($#$list1 >= 0) && !exists $newfiles{$key};
  579. if ( !$old and exists $fdirs{""} ) {
  580. delete $fdirs{""};
  581. $fdirs{"new\\"}++;
  582. foreach my $entry ( @$list1 ) {
  583. $entry->setSrcDir("new\\") if lc$entry->getSrcDir() eq "";
  584. }
  585. foreach my $entry ( @$list2 ) {
  586. $entry->setSrcDir("new\\") if lc$entry->getSrcDir() eq "";
  587. }
  588. foreach my $entry ( @$list3 ) {
  589. $entry->setSrcDir("new\\") if lc$entry->getSrcDir() eq "";
  590. }
  591. }
  592. if ( $tag =~ /u/i ) {
  593. my @dirs = keys %fdirs;
  594. if ( $#dirs == 0 and $dirs[0] =~ /^(([im].|new)\\)?$/i ) {
  595. %fdirs = (""=>1);
  596. foreach my $entry ( @$list1 ) {
  597. $entry->clearSrcDir();
  598. }
  599. foreach my $entry ( @$list2 ) {
  600. $entry->clearSrcDir();
  601. }
  602. foreach my $entry ( @$list3 ) {
  603. $entry->clearSrcDir();
  604. }
  605. } else {
  606. wrnmsg( "'u' tag not used: $path$name" ) if $#dirs >= 0;
  607. }
  608. }
  609. # Handle file deletions.
  610. my $del = ($tag =~ /d/i);
  611. if ( $del ) {
  612. if ( $#$list2 >= 0 ) {
  613. wrnmsg "Delete file still in use, skipped: $path$name";
  614. next;
  615. }
  616. if ( $path =~ /^wow\\/i ) {
  617. if ( $#$list1 >= 0 or $#$list3 >= 0 ) {
  618. logmsg "Deleting WOW file: $path$name";
  619. } else {
  620. next;
  621. }
  622. }
  623. foreach my $entry ( @$list1 ) {
  624. $entry->{CMD} = "del";
  625. }
  626. foreach my $entry ( @$list3 ) {
  627. $entry->{CMD} = "del";
  628. }
  629. }
  630. # If this is media only, do not attempt to install it.
  631. my $med = ($tag =~ /m/i);
  632. if ( $med ) {
  633. my $file = "$dir$name";
  634. my $mask = $skumask;
  635. $mask = $catmasks{$1} if $dir =~ /^(.*)\\$/ and exists $catmasks{$1};
  636. $masks{$file} = InfData->new($file, 65621, "", "", $mask);
  637. if ( !$del ) {
  638. print DIRS "$file\n";
  639. next;
  640. } else {
  641. $masks{$file}->{CMD} = "del";
  642. }
  643. ++$fdirs{$dir};
  644. $list1 = [ () ];
  645. $list2 = [ () ];
  646. $list3 = [ () ];
  647. }
  648. # See what files we got.
  649. if ( defined $dirs_file and !$del ) {
  650. foreach my $key ( keys %fdirs ) {
  651. my $file = "$key$name";
  652. print DIRS "$file\n";
  653. }
  654. }
  655. if ( $#$list1 < 0 and $#$list2 < 0 and $#$list3 < 0 and (!$del or !$med) ) {
  656. logmsg "File omitted: $key";
  657. next;
  658. }
  659. # Break the lists into copy always and replace if exists.
  660. $list2 = subEnts($list1, $list2);
  661. $list3 = subEnts($list2, $list3);
  662. $list1 = addEnts($list3, $list1);
  663. # Add lines for the file.
  664. my $type = "copyfilesalways";
  665. $type = "replacefilesifexist" if exists $oldfiles{$key};
  666. addLines($list1, "replacefilesifexist");
  667. addLines($list2, $type);
  668. }
  669. close INFILE;
  670. close DIRS if defined $dirs_file;
  671. # Generate SourceDisksFiles entries and ServicePackFiles entries.
  672. my %delfiles;
  673. my %catfiles;
  674. foreach my $path ( keys %masks ) {
  675. my $entry = $masks{$path};
  676. if ( $entry->{CMD} ne "del" ) {
  677. my $wow = ($entry->getSrcDir() =~ /^wow\\/i ) ? ".WOW":"";
  678. my $lang = ($entry->getSrcDir() =~ /lang\\/i ) ? ".Lang":"";
  679. my $data = $entry->getInfLine(0);
  680. my $mask = $entry->{MASK};
  681. if ( !$qfe and lc $entry->{NAME} ne "msjavx86.exe" ) {
  682. foreach my $sku ( keys %catmasks ) {
  683. next if $sku eq "";
  684. next if ($mask & $catmasks{$sku}) == 0;
  685. my $section = "ServicePackFiles$wow$lang\.".(uc $sku).".Files";
  686. $inf_generator->WriteSectionData( $section, $data );
  687. if ( Win32::OLE->LastError() ) {
  688. my $save_ole_error = Win32::OLE->LastError();
  689. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  690. errmsg( "Failed writing to section $section" );
  691. errmsg( "Content was: ". $data );
  692. errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) );
  693. }
  694. }
  695. }
  696. $inf_generator->AddSourceDisksFilesEntry( $path, 1 );
  697. if ( Win32::OLE->LastError() ) {
  698. my $save_ole_error = Win32::OLE->LastError();
  699. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  700. errmsg( "Failed writing to section SourceDisksFiles" );
  701. errmsg( "Content was: $path" );
  702. errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) );
  703. }
  704. } else {
  705. if ( !$qfe ) {
  706. my $file = $entry->{NAME};
  707. $file =~ s/^([im].|new)\\//i;
  708. $file =~ s/^wow\\/..\\i386\\/i;
  709. next if exists $delfiles{$file};
  710. $delfiles{$file}++;
  711. $inf_generator->WriteSectionData( "ServicePackFilesDelete.files", $file );
  712. if ( Win32::OLE->LastError() ) {
  713. my $save_ole_error = Win32::OLE->LastError();
  714. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  715. errmsg( "Failed writing to section ServicePackFilesDelete.files" );
  716. errmsg( "Content was: ". $file );
  717. errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) );
  718. }
  719. }
  720. }
  721. if ( $path =~ /^(.*\\)?([^\\]*\.cat)$/i ) {
  722. my $file = $2;
  723. next if exists $catfiles{$file};
  724. ++$catfiles{$file};
  725. $inf_generator->WriteSectionData( "ArchiveCatalogFilesOnly", $file );
  726. if ( Win32::OLE->LastError() ) {
  727. my $save_ole_error = Win32::OLE->LastError();
  728. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  729. errmsg( "Failed writing to section ArchiveCatalogFilesOnly" );
  730. errmsg( "Content was: ". $file );
  731. errmsg( "Error was: ". ($errstr?$errstr:$save_ole_error) );
  732. }
  733. }
  734. }
  735. logmsg( "Saving new INF file ..." );
  736. # Trim and save new INF file
  737. $inf_generator->CloseGen( $ftrim_inf?1:0 );
  738. if ( Win32::OLE->LastError() ) {
  739. my $save_ole_error = Win32::OLE->LastError();
  740. my $errstr = $inf_generator->{InfGenError}; chomp $errstr;
  741. errmsg( "Failed trimming/saving file (". ($errstr?$errstr:$save_ole_error). ")" );
  742. }
  743. # Add final entries.
  744. if ( !open OUT, $out_file ) {
  745. errmsg( "Unable to find outputted update.inf." );
  746. }
  747. my @inf = <OUT>;
  748. close OUT;
  749. if ( !open OUT, ">$out_file" ) {
  750. errmsg( "Unable to edit outputted update.inf." );
  751. }
  752. foreach (@inf) {
  753. print OUT;
  754. if ( /^\s*\[([^\]]*)\]\s*$/ ) {
  755. my $sect = lc $1;
  756. next if !exists $addentries{$sect};
  757. foreach my $line ( @{ $addentries{$sect} } ) {
  758. print OUT $line;
  759. }
  760. delete $addentries{$sect};
  761. }
  762. }
  763. foreach my $sect (keys %addentries) {
  764. print OUT "\n\[$sect\]\n";
  765. foreach my $line ( @{ $addentries{$sect} } ) {
  766. print OUT $line;
  767. }
  768. }
  769. close OUT;
  770. if ( $ENV{ERRORS} ) {
  771. logmsg( "Errors during execution" );
  772. exit 1;
  773. } else {
  774. logmsg( "Success" );
  775. exit 0;
  776. }
  777. ############################################################################
  778. package InfSect;
  779. use strict;
  780. use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
  781. use lib $ENV{RAZZLETOOLPATH};
  782. use Logmsg;
  783. use InfData;
  784. # Create new inf section with just a name.
  785. sub new {
  786. my $class = shift;
  787. my ($name) = @_;
  788. my $self = {};
  789. $self->{NAME} = $name; # Section name.
  790. $self->{TYPE} = undef; # Replace or copy always.
  791. $self->{MASK} = 0; # Skus that applies to.
  792. $self->{SPEC} = undef; # Special groups.
  793. $self->{WHEN} = undef; # Before or after reboot.
  794. $self->{CMD} = undef; # Copy or delete.
  795. $self->{DIRID} = undef; # DirID for install directory.
  796. $self->{PATH} = undef; # Rest of install path.
  797. $self->{FLAG} = 0; # Flag to insert for files in this section.
  798. return bless $self;
  799. }
  800. # Set the section's basic attributes.
  801. sub setAtts {
  802. my $self = shift;
  803. my ($type, $mask, $spec, $when, $cmd) = @_;
  804. if ( defined $self->{TYPE} and $self->{TYPE} ne $type) {
  805. wrnmsg "Differing section type for $self->{NAME}: $self->{TYPE} $type";
  806. }
  807. if ( $self->{MASK} != 0 and $self->{MASK} ne $mask) {
  808. wrnmsg "Differing sku set for $self->{NAME}: $self->{MASK} $mask";
  809. }
  810. if ( defined $self->{SPEC} and $self->{SPEC} ne $spec) {
  811. wrnmsg "Differing section group for $self->{NAME}: $self->{SPEC} $spec";
  812. }
  813. if ( defined $self->{WHEN} and $self->{WHEN} ne $when) {
  814. wrnmsg "Differing section install time for $self->{NAME}: $self->{WHEN} $when";
  815. }
  816. if ( defined $self->{CMD} and $self->{CMD} ne $cmd) {
  817. wrnmsg "Differing section command for $self->{CMD}: $self->{CMD} $cmd";
  818. }
  819. $self->{TYPE} = $type;
  820. $self->{MASK} = $mask;
  821. $self->{SPEC} = $spec;
  822. $self->{WHEN} = $when;
  823. $self->{CMD} = $cmd;
  824. }
  825. # Set the section's installation directory.
  826. sub setPath {
  827. my $self = shift;
  828. my ($dirid, $path) = @_;
  829. if ( (defined $self->{DIRID} and $self->{DIRID} ne $dirid) or
  830. (defined $self->{PATH} and $self->{PATH} ne $path) ) {
  831. wrnmsg "Differing path for $self->{NAME}: $self->{DIRID},$self->{PATH} $dirid,$path";
  832. }
  833. $self->{DIRID} = $dirid;
  834. $self->{PATH} = $path;
  835. }
  836. # Clear the section's directory information.
  837. sub resetPath {
  838. my $self = shift;
  839. $self->{DIRID} = undef;
  840. $self->{PATH} = undef;
  841. }
  842. # Find a mask for the product field.
  843. sub getMask {
  844. my $self = shift;
  845. return $self->{MASK};
  846. }
  847. # Get the section's installation directory.
  848. sub getPath {
  849. my $self = shift;
  850. return ($self->{DIRID}, $self->{PATH});
  851. }
  852. # Get the inf section's name.
  853. sub getName {
  854. my $self = shift;
  855. return $self->{NAME};
  856. }
  857. # See if all the fields have been specified.
  858. sub isComplete {
  859. my $self = shift;
  860. my $flags = 0;
  861. $flags |= 1 if !defined $self->{CMD};
  862. $flags |= 1 if !defined $self->{WHEN};
  863. $flags |= 1 if !defined $self->{TYPE};
  864. $flags |= 1 if $self->{MASK} == 0;
  865. $flags |= 1 if !defined $self->{SPEC};
  866. $flags |= 2 if !defined $self->{DIRID};
  867. return $flags;
  868. }
  869. # See how well this item matches a template.
  870. sub match {
  871. my $self = shift;
  872. my ($template, $type) = @_; # $skumask, $skumax, $skucount, $all
  873. # Basic comparison.
  874. return 0 if lc $self->{CMD} ne $template->{CMD};
  875. return 0 if lc $self->{DIRID} ne lc $template->{DIRID};
  876. return 0 if lc $self->{PATH} ne lc $template->{PATH};
  877. return 0 if lc $self->{TYPE} ne $type and $self->{TYPE} ne "";
  878. my $count = 1;
  879. # Test to see if the skus match.
  880. my $mask = $self->getMask();
  881. return 0 if ($template->{MASK} & $mask & $skumask) == 0;
  882. my $match = ($template->{MASK} ^ $mask) & $skumask;
  883. if ( $match == 0 ) {
  884. # Section matches.
  885. $count += $skucount;
  886. }
  887. elsif ( ($match & $mask) != 0 ) {
  888. # Section is a superset. Bad.
  889. return 0;
  890. }
  891. elsif ( ($match & $template->{MASK}) != 0 && $mask != 0 ) {
  892. # Section is a subset. Prefer the closest matches.
  893. $mask = $match & $template->{MASK};
  894. for (my $i=0; $i<$skumax; ++$i) {
  895. $count -= 1 if ($mask & 1) == 0;
  896. $mask = $mask >> 1;
  897. }
  898. $count += $skucount;
  899. }
  900. # Make sure they belong to the same group.
  901. if ( lc $self->{SPEC} eq lc $template->{SPEC} ) {
  902. $count += 0.5;
  903. }
  904. elsif ( $self->{SPEC} eq "" ) {
  905. $count += 0.2;
  906. }
  907. # Are both copy before reboot?
  908. if ( lc $self->{WHEN} eq lc $template->{WHEN} ) {
  909. $count += 0.005;
  910. }
  911. # Prefer broader product groups.
  912. if ( $self->{MASK} == $skus{""} ) {
  913. $count += 0.0005;
  914. }
  915. return $count;
  916. }
  917. 1;