1*9f91b7e3SAndre Fischer#************************************************************** 2*9f91b7e3SAndre Fischer# 3*9f91b7e3SAndre Fischer# Licensed to the Apache Software Foundation (ASF) under one 4*9f91b7e3SAndre Fischer# or more contributor license agreements. See the NOTICE file 5*9f91b7e3SAndre Fischer# distributed with this work for additional information 6*9f91b7e3SAndre Fischer# regarding copyright ownership. The ASF licenses this file 7*9f91b7e3SAndre Fischer# to you under the Apache License, Version 2.0 (the 8*9f91b7e3SAndre Fischer# "License"); you may not use this file except in compliance 9*9f91b7e3SAndre Fischer# with the License. You may obtain a copy of the License at 10*9f91b7e3SAndre Fischer# 11*9f91b7e3SAndre Fischer# http://www.apache.org/licenses/LICENSE-2.0 12*9f91b7e3SAndre Fischer# 13*9f91b7e3SAndre Fischer# Unless required by applicable law or agreed to in writing, 14*9f91b7e3SAndre Fischer# software distributed under the License is distributed on an 15*9f91b7e3SAndre Fischer# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*9f91b7e3SAndre Fischer# KIND, either express or implied. See the License for the 17*9f91b7e3SAndre Fischer# specific language governing permissions and limitations 18*9f91b7e3SAndre Fischer# under the License. 19*9f91b7e3SAndre Fischer# 20*9f91b7e3SAndre Fischer#************************************************************** 21*9f91b7e3SAndre Fischer 22*9f91b7e3SAndre Fischerpackage installer::patch::Msi; 23*9f91b7e3SAndre Fischer 24*9f91b7e3SAndre Fischeruse installer::patch::MsiTable; 25*9f91b7e3SAndre Fischeruse installer::patch::Tools; 26*9f91b7e3SAndre Fischeruse installer::patch::InstallationSet; 27*9f91b7e3SAndre Fischer 28*9f91b7e3SAndre Fischeruse File::Basename; 29*9f91b7e3SAndre Fischeruse File::Copy; 30*9f91b7e3SAndre Fischer 31*9f91b7e3SAndre Fischeruse strict; 32*9f91b7e3SAndre Fischer 33*9f91b7e3SAndre Fischer 34*9f91b7e3SAndre Fischer=head1 NAME 35*9f91b7e3SAndre Fischer 36*9f91b7e3SAndre Fischer package installer::patch::Msi - Class represents a single MSI file and gives access to its tables. 37*9f91b7e3SAndre Fischer 38*9f91b7e3SAndre Fischer=cut 39*9f91b7e3SAndre Fischer 40*9f91b7e3SAndre Fischersub FindAndCreate($$$$$) 41*9f91b7e3SAndre Fischer{ 42*9f91b7e3SAndre Fischer my ($class, $version, $is_current_version, $language, $product_name) = @_; 43*9f91b7e3SAndre Fischer 44*9f91b7e3SAndre Fischer my $condensed_version = $version; 45*9f91b7e3SAndre Fischer $condensed_version =~ s/\.//g; 46*9f91b7e3SAndre Fischer 47*9f91b7e3SAndre Fischer # When $version is the current version we have to search the msi at a different place. 48*9f91b7e3SAndre Fischer my $path; 49*9f91b7e3SAndre Fischer my $filename; 50*9f91b7e3SAndre Fischer my $is_current = 0; 51*9f91b7e3SAndre Fischer $path = installer::patch::InstallationSet::GetUnpackedExePath( 52*9f91b7e3SAndre Fischer $version, 53*9f91b7e3SAndre Fischer $is_current_version, 54*9f91b7e3SAndre Fischer $language, 55*9f91b7e3SAndre Fischer "msi", 56*9f91b7e3SAndre Fischer $product_name); 57*9f91b7e3SAndre Fischer 58*9f91b7e3SAndre Fischer # Find the msi in the path.ls . 59*9f91b7e3SAndre Fischer $filename = File::Spec->catfile($path, "openoffice".$condensed_version.".msi"); 60*9f91b7e3SAndre Fischer $is_current = $is_current_version; 61*9f91b7e3SAndre Fischer 62*9f91b7e3SAndre Fischer return $class->new($filename, $version, $is_current, $language, $product_name); 63*9f91b7e3SAndre Fischer} 64*9f91b7e3SAndre Fischer 65*9f91b7e3SAndre Fischer 66*9f91b7e3SAndre Fischer 67*9f91b7e3SAndre Fischer 68*9f91b7e3SAndre Fischer 69*9f91b7e3SAndre Fischer 70*9f91b7e3SAndre Fischer=head2 new($class, $filename, $version, $is_current_version, $language, $product_name) 71*9f91b7e3SAndre Fischer 72*9f91b7e3SAndre Fischer Create a new object of the Msi class. The values of $version, $language, and $product_name define 73*9f91b7e3SAndre Fischer where to look for the msi file. 74*9f91b7e3SAndre Fischer 75*9f91b7e3SAndre Fischer If construction fails then IsValid() will return false. 76*9f91b7e3SAndre Fischer 77*9f91b7e3SAndre Fischer=cut 78*9f91b7e3SAndre Fischersub new ($$$$$$) 79*9f91b7e3SAndre Fischer{ 80*9f91b7e3SAndre Fischer my ($class, $filename, $version, $is_current_version, $language, $product_name) = @_; 81*9f91b7e3SAndre Fischer 82*9f91b7e3SAndre Fischer if ( ! -f $filename) 83*9f91b7e3SAndre Fischer { 84*9f91b7e3SAndre Fischer installer::logger::PrintError("can not find the .msi file for version %s and language %s at '%s'\n", 85*9f91b7e3SAndre Fischer $version, 86*9f91b7e3SAndre Fischer $language, 87*9f91b7e3SAndre Fischer $filename); 88*9f91b7e3SAndre Fischer return undef; 89*9f91b7e3SAndre Fischer } 90*9f91b7e3SAndre Fischer 91*9f91b7e3SAndre Fischer my $self = { 92*9f91b7e3SAndre Fischer 'filename' => $filename, 93*9f91b7e3SAndre Fischer 'path' => dirname($filename), 94*9f91b7e3SAndre Fischer 'version' => $version, 95*9f91b7e3SAndre Fischer 'is_current_version' => $is_current_version, 96*9f91b7e3SAndre Fischer 'language' => $language, 97*9f91b7e3SAndre Fischer 'package_format' => "msi", 98*9f91b7e3SAndre Fischer 'product_name' => $product_name, 99*9f91b7e3SAndre Fischer 'tmpdir' => File::Temp->newdir(CLEANUP => 1), 100*9f91b7e3SAndre Fischer 'is_valid' => -f $filename 101*9f91b7e3SAndre Fischer }; 102*9f91b7e3SAndre Fischer bless($self, $class); 103*9f91b7e3SAndre Fischer 104*9f91b7e3SAndre Fischer return $self; 105*9f91b7e3SAndre Fischer} 106*9f91b7e3SAndre Fischer 107*9f91b7e3SAndre Fischer 108*9f91b7e3SAndre Fischer 109*9f91b7e3SAndre Fischer 110*9f91b7e3SAndre Fischersub IsValid ($) 111*9f91b7e3SAndre Fischer{ 112*9f91b7e3SAndre Fischer my ($self) = @_; 113*9f91b7e3SAndre Fischer 114*9f91b7e3SAndre Fischer return $self->{'is_valid'}; 115*9f91b7e3SAndre Fischer} 116*9f91b7e3SAndre Fischer 117*9f91b7e3SAndre Fischer 118*9f91b7e3SAndre Fischer 119*9f91b7e3SAndre Fischer 120*9f91b7e3SAndre Fischer=head2 Commit($self) 121*9f91b7e3SAndre Fischer 122*9f91b7e3SAndre Fischer Write all modified tables back into the databse. 123*9f91b7e3SAndre Fischer 124*9f91b7e3SAndre Fischer=cut 125*9f91b7e3SAndre Fischersub Commit ($) 126*9f91b7e3SAndre Fischer{ 127*9f91b7e3SAndre Fischer my $self = shift; 128*9f91b7e3SAndre Fischer 129*9f91b7e3SAndre Fischer my @tables_to_update = (); 130*9f91b7e3SAndre Fischer foreach my $table (values %{$self->{'tables'}}) 131*9f91b7e3SAndre Fischer { 132*9f91b7e3SAndre Fischer push @tables_to_update,$table if ($table->IsModified()); 133*9f91b7e3SAndre Fischer } 134*9f91b7e3SAndre Fischer 135*9f91b7e3SAndre Fischer if (scalar @tables_to_update > 0) 136*9f91b7e3SAndre Fischer { 137*9f91b7e3SAndre Fischer $installer::logger::Info->printf("writing modified tables to database:\n"); 138*9f91b7e3SAndre Fischer foreach my $table (@tables_to_update) 139*9f91b7e3SAndre Fischer { 140*9f91b7e3SAndre Fischer $installer::logger::Info->printf(" %s\n", $table->GetName()); 141*9f91b7e3SAndre Fischer $self->PutTable($table); 142*9f91b7e3SAndre Fischer } 143*9f91b7e3SAndre Fischer 144*9f91b7e3SAndre Fischer foreach my $table (@tables_to_update) 145*9f91b7e3SAndre Fischer { 146*9f91b7e3SAndre Fischer $table->UpdateTimestamp(); 147*9f91b7e3SAndre Fischer $table->MarkAsUnmodified(); 148*9f91b7e3SAndre Fischer } 149*9f91b7e3SAndre Fischer } 150*9f91b7e3SAndre Fischer} 151*9f91b7e3SAndre Fischer 152*9f91b7e3SAndre Fischer 153*9f91b7e3SAndre Fischer 154*9f91b7e3SAndre Fischer 155*9f91b7e3SAndre Fischer=head2 GetTable($seld, $table_name) 156*9f91b7e3SAndre Fischer 157*9f91b7e3SAndre Fischer Return an MsiTable object for $table_name. Table objects are kept 158*9f91b7e3SAndre Fischer alive for the life time of the Msi object. Therefore the second 159*9f91b7e3SAndre Fischer call for the same table is very cheap. 160*9f91b7e3SAndre Fischer 161*9f91b7e3SAndre Fischer=cut 162*9f91b7e3SAndre Fischersub GetTable ($$) 163*9f91b7e3SAndre Fischer{ 164*9f91b7e3SAndre Fischer my ($self, $table_name) = @_; 165*9f91b7e3SAndre Fischer 166*9f91b7e3SAndre Fischer my $table = $self->{'tables'}->{$table_name}; 167*9f91b7e3SAndre Fischer if ( ! defined $table) 168*9f91b7e3SAndre Fischer { 169*9f91b7e3SAndre Fischer my $table_filename = File::Spec->catfile($self->{'tmpdir'}, $table_name .".idt"); 170*9f91b7e3SAndre Fischer if ( ! -f $table_filename 171*9f91b7e3SAndre Fischer || ! EnsureAYoungerThanB($table_filename, $self->{'fullname'})) 172*9f91b7e3SAndre Fischer { 173*9f91b7e3SAndre Fischer # Extract table from database to text file on disk. 174*9f91b7e3SAndre Fischer my $truncated_table_name = length($table_name)>8 ? substr($table_name,0,8) : $table_name; 175*9f91b7e3SAndre Fischer my $command = join(" ", 176*9f91b7e3SAndre Fischer "msidb.exe", 177*9f91b7e3SAndre Fischer "-d", installer::patch::Tools::ToEscapedWindowsPath($self->{'filename'}), 178*9f91b7e3SAndre Fischer "-f", installer::patch::Tools::ToEscapedWindowsPath($self->{'tmpdir'}), 179*9f91b7e3SAndre Fischer "-e", $table_name); 180*9f91b7e3SAndre Fischer my $result = qx($command); 181*9f91b7e3SAndre Fischer print $result; 182*9f91b7e3SAndre Fischer } 183*9f91b7e3SAndre Fischer 184*9f91b7e3SAndre Fischer # Read table into memory. 185*9f91b7e3SAndre Fischer $table = new installer::patch::MsiTable($table_filename, $table_name); 186*9f91b7e3SAndre Fischer $self->{'tables'}->{$table_name} = $table; 187*9f91b7e3SAndre Fischer } 188*9f91b7e3SAndre Fischer 189*9f91b7e3SAndre Fischer return $table; 190*9f91b7e3SAndre Fischer} 191*9f91b7e3SAndre Fischer 192*9f91b7e3SAndre Fischer 193*9f91b7e3SAndre Fischer 194*9f91b7e3SAndre Fischer 195*9f91b7e3SAndre Fischer=head2 PutTable($self, $table) 196*9f91b7e3SAndre Fischer 197*9f91b7e3SAndre Fischer Write the given table back to the databse. 198*9f91b7e3SAndre Fischer 199*9f91b7e3SAndre Fischer=cut 200*9f91b7e3SAndre Fischersub PutTable ($$) 201*9f91b7e3SAndre Fischer{ 202*9f91b7e3SAndre Fischer my ($self, $table) = @_; 203*9f91b7e3SAndre Fischer 204*9f91b7e3SAndre Fischer # Create text file from the current table content. 205*9f91b7e3SAndre Fischer $table->WriteFile(); 206*9f91b7e3SAndre Fischer 207*9f91b7e3SAndre Fischer my $table_name = $table->GetName(); 208*9f91b7e3SAndre Fischer 209*9f91b7e3SAndre Fischer # Store table from text file into database. 210*9f91b7e3SAndre Fischer my $table_filename = $table->{'filename'}; 211*9f91b7e3SAndre Fischer 212*9f91b7e3SAndre Fischer if (length($table_name) > 8) 213*9f91b7e3SAndre Fischer { 214*9f91b7e3SAndre Fischer # The file name of the table data must not be longer than 8 characters (not counting the extension). 215*9f91b7e3SAndre Fischer # The name passed as argument to the -i option may be longer. 216*9f91b7e3SAndre Fischer my $truncated_table_name = substr($table_name,0,8); 217*9f91b7e3SAndre Fischer my $table_truncated_filename = File::Spec->catfile( 218*9f91b7e3SAndre Fischer dirname($table_filename), 219*9f91b7e3SAndre Fischer $truncated_table_name.".idt"); 220*9f91b7e3SAndre Fischer File::Copy::copy($table_filename, $table_truncated_filename) || die("can not create table file with short name"); 221*9f91b7e3SAndre Fischer } 222*9f91b7e3SAndre Fischer 223*9f91b7e3SAndre Fischer my $command = join(" ", 224*9f91b7e3SAndre Fischer "msidb.exe", 225*9f91b7e3SAndre Fischer "-d", installer::patch::Tools::ToEscapedWindowsPath($self->{'filename'}), 226*9f91b7e3SAndre Fischer "-f", installer::patch::Tools::ToEscapedWindowsPath($self->{'tmpdir'}), 227*9f91b7e3SAndre Fischer "-i", $table_name); 228*9f91b7e3SAndre Fischer my $result = system($command); 229*9f91b7e3SAndre Fischer 230*9f91b7e3SAndre Fischer if ($result != 0) 231*9f91b7e3SAndre Fischer { 232*9f91b7e3SAndre Fischer installer::logger::PrintError("writing table '%s' back to database failed", $table_name); 233*9f91b7e3SAndre Fischer # For error messages see http://msdn.microsoft.com/en-us/library/windows/desktop/aa372835%28v=vs.85%29.aspx 234*9f91b7e3SAndre Fischer } 235*9f91b7e3SAndre Fischer} 236*9f91b7e3SAndre Fischer 237*9f91b7e3SAndre Fischer 238*9f91b7e3SAndre Fischer 239*9f91b7e3SAndre Fischer 240*9f91b7e3SAndre Fischer=head2 EnsureAYoungerThanB ($filename_a, $filename_b) 241*9f91b7e3SAndre Fischer 242*9f91b7e3SAndre Fischer Internal function (not a method) that compares to files according 243*9f91b7e3SAndre Fischer to their last modification times (mtime). 244*9f91b7e3SAndre Fischer 245*9f91b7e3SAndre Fischer=cut 246*9f91b7e3SAndre Fischersub EnsureAYoungerThanB ($$) 247*9f91b7e3SAndre Fischer{ 248*9f91b7e3SAndre Fischer my ($filename_a, $filename_b) = @_; 249*9f91b7e3SAndre Fischer 250*9f91b7e3SAndre Fischer die("file $filename_a does not exist") unless -f $filename_a; 251*9f91b7e3SAndre Fischer die("file $filename_b does not exist") unless -f $filename_b; 252*9f91b7e3SAndre Fischer 253*9f91b7e3SAndre Fischer my @stat_a = stat($filename_a); 254*9f91b7e3SAndre Fischer my @stat_b = stat($filename_b); 255*9f91b7e3SAndre Fischer 256*9f91b7e3SAndre Fischer if ($stat_a[9] <= $stat_b[9]) 257*9f91b7e3SAndre Fischer { 258*9f91b7e3SAndre Fischer return 0; 259*9f91b7e3SAndre Fischer } 260*9f91b7e3SAndre Fischer else 261*9f91b7e3SAndre Fischer { 262*9f91b7e3SAndre Fischer return 1; 263*9f91b7e3SAndre Fischer } 264*9f91b7e3SAndre Fischer} 265*9f91b7e3SAndre Fischer 266*9f91b7e3SAndre Fischer 267*9f91b7e3SAndre Fischer 268*9f91b7e3SAndre Fischer 269*9f91b7e3SAndre Fischer=head2 SplitLongShortName($name) 270*9f91b7e3SAndre Fischer 271*9f91b7e3SAndre Fischer Split $name (typically from the 'FileName' column in the 'File' 272*9f91b7e3SAndre Fischer table or 'DefaultDir' column in the 'Directory' table) at the '|' 273*9f91b7e3SAndre Fischer into short (8.3) and long names. If there is no '|' in $name then 274*9f91b7e3SAndre Fischer $name is returned as both short and long name. 275*9f91b7e3SAndre Fischer 276*9f91b7e3SAndre Fischer Returns long and short name (in this order) as array. 277*9f91b7e3SAndre Fischer 278*9f91b7e3SAndre Fischer=cut 279*9f91b7e3SAndre Fischersub SplitLongShortName ($) 280*9f91b7e3SAndre Fischer{ 281*9f91b7e3SAndre Fischer my ($name) = @_; 282*9f91b7e3SAndre Fischer 283*9f91b7e3SAndre Fischer if ($name =~ /^([^\|]*)\|(.*)$/) 284*9f91b7e3SAndre Fischer { 285*9f91b7e3SAndre Fischer return ($2,$1); 286*9f91b7e3SAndre Fischer } 287*9f91b7e3SAndre Fischer else 288*9f91b7e3SAndre Fischer { 289*9f91b7e3SAndre Fischer return ($name,$name); 290*9f91b7e3SAndre Fischer } 291*9f91b7e3SAndre Fischer} 292*9f91b7e3SAndre Fischer 293*9f91b7e3SAndre Fischer 294*9f91b7e3SAndre Fischer 295*9f91b7e3SAndre Fischer=head2 SplitTargetSourceLongShortName ($name) 296*9f91b7e3SAndre Fischer 297*9f91b7e3SAndre Fischer Split $name first at the ':' into target and source parts and each 298*9f91b7e3SAndre Fischer of those at the '|'s into long and short parts. Names that follow 299*9f91b7e3SAndre Fischer this pattern come from the 'DefaultDir' column in the 'Directory' 300*9f91b7e3SAndre Fischer table. 301*9f91b7e3SAndre Fischer 302*9f91b7e3SAndre Fischer=cut 303*9f91b7e3SAndre Fischersub SplitTargetSourceLongShortName ($) 304*9f91b7e3SAndre Fischer{ 305*9f91b7e3SAndre Fischer my ($name) = @_; 306*9f91b7e3SAndre Fischer 307*9f91b7e3SAndre Fischer if ($name =~ /^([^:]*):(.*)$/) 308*9f91b7e3SAndre Fischer { 309*9f91b7e3SAndre Fischer return (installer::patch::Msi::SplitLongShortName($1), installer::patch::Msi::SplitLongShortName($2)); 310*9f91b7e3SAndre Fischer } 311*9f91b7e3SAndre Fischer else 312*9f91b7e3SAndre Fischer { 313*9f91b7e3SAndre Fischer my ($long,$short) = installer::patch::Msi::SplitLongShortName($name); 314*9f91b7e3SAndre Fischer return ($long,$short,$long,$short); 315*9f91b7e3SAndre Fischer } 316*9f91b7e3SAndre Fischer} 317*9f91b7e3SAndre Fischer 318*9f91b7e3SAndre Fischer 319*9f91b7e3SAndre Fischer=head2 GetDirectoryMap($self) 320*9f91b7e3SAndre Fischer 321*9f91b7e3SAndre Fischer Return a map that maps directory unique names (column 'Directory' in table 'Directory') 322*9f91b7e3SAndre Fischer to hashes that contains short and long source and target names. 323*9f91b7e3SAndre Fischer 324*9f91b7e3SAndre Fischer=cut 325*9f91b7e3SAndre Fischersub GetDirectoryMap ($) 326*9f91b7e3SAndre Fischer{ 327*9f91b7e3SAndre Fischer my ($self) = @_; 328*9f91b7e3SAndre Fischer 329*9f91b7e3SAndre Fischer if (defined $self->{'DirectoryMap'}) 330*9f91b7e3SAndre Fischer { 331*9f91b7e3SAndre Fischer return $self->{'DirectoryMap'}; 332*9f91b7e3SAndre Fischer } 333*9f91b7e3SAndre Fischer 334*9f91b7e3SAndre Fischer my $directory_table = $self->GetTable("Directory"); 335*9f91b7e3SAndre Fischer my %dir_map = (); 336*9f91b7e3SAndre Fischer foreach my $row (@{$directory_table->GetAllRows()}) 337*9f91b7e3SAndre Fischer { 338*9f91b7e3SAndre Fischer my ($target_long_name, $target_short_name, $source_long_name, $source_short_name) 339*9f91b7e3SAndre Fischer = installer::patch::Msi::SplitTargetSourceLongShortName($row->GetValue("DefaultDir")); 340*9f91b7e3SAndre Fischer my $unique_name = $row->GetValue("Directory"); 341*9f91b7e3SAndre Fischer $dir_map{$unique_name} = 342*9f91b7e3SAndre Fischer { 343*9f91b7e3SAndre Fischer 'unique_name' => $unique_name, 344*9f91b7e3SAndre Fischer 'parent' => $row->GetValue("Directory_Parent"), 345*9f91b7e3SAndre Fischer 'default_dir' => $row->GetValue("DefaultDir"), 346*9f91b7e3SAndre Fischer 'source_long_name' => $source_long_name, 347*9f91b7e3SAndre Fischer 'source_short_name' => $source_short_name, 348*9f91b7e3SAndre Fischer 'target_long_name' => $target_long_name, 349*9f91b7e3SAndre Fischer 'target_short_name' => $target_short_name 350*9f91b7e3SAndre Fischer }; 351*9f91b7e3SAndre Fischer } 352*9f91b7e3SAndre Fischer 353*9f91b7e3SAndre Fischer # Set up full names for all directories. 354*9f91b7e3SAndre Fischer my @todo = map {$_} (keys %dir_map); 355*9f91b7e3SAndre Fischer while (scalar @todo > 0) 356*9f91b7e3SAndre Fischer { 357*9f91b7e3SAndre Fischer my $key = shift @todo; 358*9f91b7e3SAndre Fischer my $item = $dir_map{$key}; 359*9f91b7e3SAndre Fischer next if defined $item->{'full_source_name'}; 360*9f91b7e3SAndre Fischer 361*9f91b7e3SAndre Fischer if ($item->{'parent'} eq "") 362*9f91b7e3SAndre Fischer { 363*9f91b7e3SAndre Fischer # Directory has no parent => full names are the same as the name. 364*9f91b7e3SAndre Fischer $item->{'full_source_long_name'} = $item->{'source_long_name'}; 365*9f91b7e3SAndre Fischer $item->{'full_source_short_name'} = $item->{'source_short_name'}; 366*9f91b7e3SAndre Fischer $item->{'full_target_long_name'} = $item->{'target_long_name'}; 367*9f91b7e3SAndre Fischer $item->{'full_target_short_name'} = $item->{'target_short_name'}; 368*9f91b7e3SAndre Fischer } 369*9f91b7e3SAndre Fischer else 370*9f91b7e3SAndre Fischer { 371*9f91b7e3SAndre Fischer my $parent = $dir_map{$item->{'parent'}}; 372*9f91b7e3SAndre Fischer if ( defined $parent->{'full_source_long_name'}) 373*9f91b7e3SAndre Fischer { 374*9f91b7e3SAndre Fischer # Parent aleady has full names => we can create the full name of the current item. 375*9f91b7e3SAndre Fischer $item->{'full_source_long_name'} 376*9f91b7e3SAndre Fischer = $parent->{'full_source_long_name'} . "/" . $item->{'source_long_name'}; 377*9f91b7e3SAndre Fischer $item->{'full_source_short_name'} 378*9f91b7e3SAndre Fischer = $parent->{'full_source_short_name'} . "/" . $item->{'source_short_name'}; 379*9f91b7e3SAndre Fischer $item->{'full_target_long_name'} 380*9f91b7e3SAndre Fischer = $parent->{'full_target_long_name'} . "/" . $item->{'target_long_name'}; 381*9f91b7e3SAndre Fischer $item->{'full_target_short_name'} 382*9f91b7e3SAndre Fischer = $parent->{'full_target_short_name'} . "/" . $item->{'target_short_name'}; 383*9f91b7e3SAndre Fischer } 384*9f91b7e3SAndre Fischer else 385*9f91b7e3SAndre Fischer { 386*9f91b7e3SAndre Fischer # Parent has to be processed before the current item can be processed. 387*9f91b7e3SAndre Fischer # Push both to the head of the list. 388*9f91b7e3SAndre Fischer unshift @todo, $key; 389*9f91b7e3SAndre Fischer unshift @todo, $item->{'parent'}; 390*9f91b7e3SAndre Fischer } 391*9f91b7e3SAndre Fischer } 392*9f91b7e3SAndre Fischer } 393*9f91b7e3SAndre Fischer 394*9f91b7e3SAndre Fischer # Postprocess the path names for cleanup. 395*9f91b7e3SAndre Fischer foreach my $item (values %dir_map) 396*9f91b7e3SAndre Fischer { 397*9f91b7e3SAndre Fischer foreach my $id ( 398*9f91b7e3SAndre Fischer 'full_source_long_name', 399*9f91b7e3SAndre Fischer 'full_source_short_name', 400*9f91b7e3SAndre Fischer 'full_target_long_name', 401*9f91b7e3SAndre Fischer 'full_target_short_name') 402*9f91b7e3SAndre Fischer { 403*9f91b7e3SAndre Fischer $item->{$id} =~ s/\/(\.\/)+/\//g; 404*9f91b7e3SAndre Fischer $item->{$id} =~ s/^SourceDir\///; 405*9f91b7e3SAndre Fischer $item->{$id} =~ s/^\.$//; 406*9f91b7e3SAndre Fischer } 407*9f91b7e3SAndre Fischer } 408*9f91b7e3SAndre Fischer 409*9f91b7e3SAndre Fischer $self->{'DirectoryMap'} = \%dir_map; 410*9f91b7e3SAndre Fischer return $self->{'DirectoryMap'}; 411*9f91b7e3SAndre Fischer} 412*9f91b7e3SAndre Fischer 413*9f91b7e3SAndre Fischer 414*9f91b7e3SAndre Fischer 415*9f91b7e3SAndre Fischer 416*9f91b7e3SAndre Fischer=head2 GetFileMap ($) 417*9f91b7e3SAndre Fischer 418*9f91b7e3SAndre Fischer Return a map (hash) that maps the unique name (column 'File' in 419*9f91b7e3SAndre Fischer the 'File' table) to data that is associated with that file, like 420*9f91b7e3SAndre Fischer the directory or component. 421*9f91b7e3SAndre Fischer 422*9f91b7e3SAndre Fischer The map is kept alive for the lifetime of the Msi object. All 423*9f91b7e3SAndre Fischer calls but the first are cheap. 424*9f91b7e3SAndre Fischer 425*9f91b7e3SAndre Fischer=cut 426*9f91b7e3SAndre Fischersub GetFileMap ($) 427*9f91b7e3SAndre Fischer{ 428*9f91b7e3SAndre Fischer my ($self) = @_; 429*9f91b7e3SAndre Fischer 430*9f91b7e3SAndre Fischer if (defined $self->{'FileMap'}) 431*9f91b7e3SAndre Fischer { 432*9f91b7e3SAndre Fischer return $self->{'FileMap'}; 433*9f91b7e3SAndre Fischer } 434*9f91b7e3SAndre Fischer 435*9f91b7e3SAndre Fischer my $file_table = $self->GetTable("File"); 436*9f91b7e3SAndre Fischer my $component_table = $self->GetTable("Component"); 437*9f91b7e3SAndre Fischer my $dir_map = $self->GetDirectoryMap(); 438*9f91b7e3SAndre Fischer 439*9f91b7e3SAndre Fischer # Setup a map from component names to directory items. 440*9f91b7e3SAndre Fischer my %component_to_directory_map = 441*9f91b7e3SAndre Fischer map 442*9f91b7e3SAndre Fischer {$_->GetValue('Component') => $_->GetValue('Directory_')} 443*9f91b7e3SAndre Fischer @{$component_table->GetAllRows()}; 444*9f91b7e3SAndre Fischer 445*9f91b7e3SAndre Fischer # Finally, create the map from files to directories. 446*9f91b7e3SAndre Fischer my $file_map = {}; 447*9f91b7e3SAndre Fischer my $file_component_index = $file_table->GetColumnIndex("Component_"); 448*9f91b7e3SAndre Fischer my $file_file_index = $file_table->GetColumnIndex("File"); 449*9f91b7e3SAndre Fischer foreach my $file_row (@{$file_table->GetAllRows()}) 450*9f91b7e3SAndre Fischer { 451*9f91b7e3SAndre Fischer my $component_name = $file_row->GetValue($file_component_index); 452*9f91b7e3SAndre Fischer my $directory_name = $component_to_directory_map{$component_name}; 453*9f91b7e3SAndre Fischer my $unique_name = $file_row->GetValue($file_file_index); 454*9f91b7e3SAndre Fischer $file_map->{$unique_name} = { 455*9f91b7e3SAndre Fischer 'directory' => $dir_map->{$directory_name}, 456*9f91b7e3SAndre Fischer 'component_name' => $component_name 457*9f91b7e3SAndre Fischer }; 458*9f91b7e3SAndre Fischer } 459*9f91b7e3SAndre Fischer 460*9f91b7e3SAndre Fischer $self->{'FileMap'} = $file_map; 461*9f91b7e3SAndre Fischer return $file_map; 462*9f91b7e3SAndre Fischer} 463*9f91b7e3SAndre Fischer 464*9f91b7e3SAndre Fischer 465*9f91b7e3SAndre Fischer1; 466