1# 2# This file is part of the LibreOffice project. 3# 4# This Source Code Form is subject to the terms of the Mozilla Public 5# License, v. 2.0. If a copy of the MPL was not distributed with this 6# file, You can obtain one at http://mozilla.org/MPL/2.0/. 7# 8# This file incorporates work covered by the following license notice: 9# 10# Licensed to the Apache Software Foundation (ASF) under one or more 11# contributor license agreements. See the NOTICE file distributed 12# with this work for additional information regarding copyright 13# ownership. The ASF licenses this file to you under the Apache 14# License, Version 2.0 (the "License"); you may not use this file 15# except in compliance with the License. You may obtain a copy of 16# the License at http://www.apache.org/licenses/LICENSE-2.0 . 17# 18 19package installer::windows::mergemodule; 20 21use Cwd; 22use Digest::MD5; 23use installer::converter; 24use installer::exiter; 25use installer::files; 26use installer::globals; 27use installer::logger; 28use installer::pathanalyzer; 29use installer::remover; 30use installer::scriptitems; 31use installer::systemactions; 32use installer::worker; 33use installer::windows::idtglobal; 34use installer::windows::language; 35 36################################################################# 37# Merging the Windows MergeModules into the msi database. 38################################################################# 39 40sub merge_mergemodules_into_msi_database 41{ 42 my ($mergemodules, $filesref, $msifilename, $languagestringref, $allvariables, $includepatharrayref, $allupdatesequences, $allupdatelastsequences, $allupdatediskids) = @_; 43 44 my $domerge = 0; 45 if (( $#{$mergemodules} > -1 ) && ( ! $installer::globals::languagepack ) && ( ! $installer::globals::helppack )) { $domerge = 1; } 46 47 if ( $domerge ) 48 { 49 installer::logger::include_header_into_logfile("Merging merge modules into msi database"); 50 installer::logger::print_message( "... merging msm files into msi database ... \n" ); 51 installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, start"); 52 53 my $msidb = "msidb.exe"; # Has to be in the path 54 my $cabinetfile = "MergeModule.CABinet"; # the name of each cabinet file in a merge file 55 my $infoline = ""; 56 my $systemcall = ""; 57 my $returnvalue = ""; 58 59 # 1. Analyzing the MergeModule (has only to be done once) 60 # a. -> Extracting cabinet file: msidb.exe -d <msmfile> -x MergeModule.CABinet 61 # b. -> Number of files in cabinet file: msidb.exe -d <msmfile> -f <directory> -e File 62 # c. -> List of components: msidb.exe -d <msmfile> -f <directory> -e Component 63 64 if ( ! $installer::globals::mergemodules_analyzed ) 65 { 66 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, start"); 67 $infoline = "Analyzing all Merge Modules\n\n"; 68 push( @installer::globals::logfileinfo, $infoline); 69 70 %installer::globals::mergemodules = (); 71 72 my $mergemoduledir = installer::systemactions::create_directories("mergefiles", $languagestringref); 73 74 my $mergemodule; 75 foreach $mergemodule ( @{$mergemodules} ) 76 { 77 my $filename = $mergemodule->{'Name'}; 78 my $mergefile = $ENV{'MSM_PATH'} . $filename; 79 80 if ( ! -f $mergefile ) { installer::exiter::exit_program("ERROR: msm file not found: $filename ($mergefile)!", "merge_mergemodules_into_msi_database"); } 81 my $completesource = $mergefile; 82 83 my $mergegid = $mergemodule->{'gid'}; 84 my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid; 85 if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); } 86 87 $infoline = "Analyzing Merge Module: $filename\n"; 88 push( @installer::globals::logfileinfo, $infoline); 89 90 # copy msm file into working directory 91 my $completedest = $workdir . $installer::globals::separator . $filename; 92 installer::systemactions::copy_one_file($completesource, $completedest); 93 if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "merge_mergemodules_into_msi_database"); } 94 95 # changing directory 96 my $from = cwd(); 97 my $to = $workdir; 98 chdir($to); 99 100 # remove an existing cabinet file 101 if ( -f $cabinetfile ) { unlink($cabinetfile); } 102 103 # exclude cabinet file 104 $systemcall = $msidb . " -d " . $filename . " -x " . $cabinetfile; 105 $returnvalue = system($systemcall); 106 107 $infoline = "Systemcall: $systemcall\n"; 108 push( @installer::globals::logfileinfo, $infoline); 109 110 if ($returnvalue) 111 { 112 $infoline = "ERROR: Could not execute $systemcall !\n"; 113 push( @installer::globals::logfileinfo, $infoline); 114 installer::exiter::exit_program("ERROR: Could not extract cabinet file from merge file: $completedest !", "merge_mergemodules_into_msi_database"); 115 } 116 else 117 { 118 $infoline = "Success: Executed $systemcall successfully!\n"; 119 push( @installer::globals::logfileinfo, $infoline); 120 } 121 122 # exclude tables from mergefile 123 # Attention: All listed tables have to exist in the database. If they not exist, an error window pops up 124 # and the return value of msidb.exe is not zero. The error window makes it impossible to check the existence 125 # of a table with the help of the return value. 126 # Solution: Export of all tables by using "*" . Some tables must exist (File Component Directory), other 127 # tables do not need to exist (MsiAssembly). 128 129 if ( $^O =~ /cygwin/i ) { 130 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 131 my $localworkdir = $workdir; 132 $localworkdir =~ s/\//\\\\/g; 133 $systemcall = $msidb . " -d " . $filename . " -f " . $localworkdir . " -e \\\*"; 134 } 135 else 136 { 137 $systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e \*"; 138 } 139 140 $returnvalue = system($systemcall); 141 142 $infoline = "Systemcall: $systemcall\n"; 143 push( @installer::globals::logfileinfo, $infoline); 144 145 if ($returnvalue) 146 { 147 $infoline = "ERROR: Could not execute $systemcall !\n"; 148 push( @installer::globals::logfileinfo, $infoline); 149 installer::exiter::exit_program("ERROR: Could not exclude tables from merge file: $completedest !", "merge_mergemodules_into_msi_database"); 150 } 151 else 152 { 153 $infoline = "Success: Executed $systemcall successfully!\n"; 154 push( @installer::globals::logfileinfo, $infoline); 155 } 156 157 # Determining files 158 my $idtfilename = "File.idt"; # must exist 159 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } 160 my $filecontent = installer::files::read_file($idtfilename); 161 my @file_idt_content = (); 162 my $filecounter = 0; 163 my %mergefilesequence = (); 164 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 165 { 166 if ( $i <= 2 ) { next; } # ignoring first three lines 167 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 168 $filecounter++; 169 push(@file_idt_content, ${$filecontent}[$i]); 170 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(\d+?)\s*$/ ) 171 { 172 my $filename = $1; 173 my $filesequence = $8; 174 $mergefilesequence{$filename} = $filesequence; 175 } 176 else 177 { 178 my $linecount = $i + 1; 179 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "merge_mergemodules_into_msi_database"); 180 } 181 } 182 183 # Determining components 184 $idtfilename = "Component.idt"; # must exist 185 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } 186 $filecontent = installer::files::read_file($idtfilename); 187 my %componentnames = (); 188 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 189 { 190 if ( $i <= 2 ) { next; } # ignoring first three lines 191 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 192 if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $componentnames{$1} = 1; } 193 } 194 195 # Determining directories 196 $idtfilename = "Directory.idt"; # must exist 197 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } 198 $filecontent = installer::files::read_file($idtfilename); 199 my %mergedirectories = (); 200 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 201 { 202 if ( $i <= 2 ) { next; } # ignoring first three lines 203 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 204 if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergedirectories{$1} = 1; } 205 } 206 207 # Determining assemblies 208 $idtfilename = "MsiAssembly.idt"; # does not need to exist 209 my $hasmsiassemblies = 0; 210 my %mergeassemblies = (); 211 if ( -f $idtfilename ) 212 { 213 $filecontent = installer::files::read_file($idtfilename); 214 $hasmsiassemblies = 1; 215 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 216 { 217 if ( $i <= 2 ) { next; } # ignoring first three lines 218 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 219 if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergeassemblies{$1} = 1; } 220 } 221 } 222 223 # It is possible, that other tables have to be checked here. This happens, if tables in the 224 # merge module have to know the "Feature" or the "Directory", under which the content of the 225 # msm file is integrated into the msi database. 226 227 # Determining name of cabinet file in installation set 228 my $cabfilename = $mergemodule->{'Cabfilename'}; 229 if ( $cabfilename ) { installer::packagelist::resolve_packagevariables(\$cabfilename, $allvariables, 0); } 230 231 # Analyzing styles 232 # Flag REMOVE_FILE_TABLE is required for msvc9 Merge-Module, because otherwise msidb.exe 233 # fails during integration of msm file into msi database. 234 235 my $styles = ""; 236 my $removefiletable = 0; 237 if ( $mergemodule->{'Styles'} ) { $styles = $mergemodule->{'Styles'}; } 238 if ( $styles =~ /\bREMOVE_FILE_TABLE\b/ ) { $removefiletable = 1; } 239 240 if ( $removefiletable ) 241 { 242 my $removeworkdir = $workdir . $installer::globals::separator . "remove_file_idt"; 243 if ( ! -d $removeworkdir ) { installer::systemactions::create_directory($removeworkdir); } 244 my $completeremovedest = $removeworkdir . $installer::globals::separator . $filename; 245 installer::systemactions::copy_one_file($completedest, $completeremovedest); 246 if ( ! -f $completeremovedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completeremovedest !", "merge_mergemodules_into_msi_database"); } 247 248 # Unpacking msm file 249 if ( $^O =~ /cygwin/i ) { 250 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 251 my $localcompleteremovedest = $completeremovedest; 252 my $localremoveworkdir = $removeworkdir; 253 $localcompleteremovedest =~ s/\//\\\\/g; 254 $localremoveworkdir =~ s/\//\\\\/g; 255 $systemcall = $msidb . " -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -e \\\*"; 256 } 257 else 258 { 259 $systemcall = $msidb . " -d " . $completeremovedest . " -f " . $removeworkdir . " -e \*"; 260 } 261 262 $returnvalue = system($systemcall); 263 264 my $idtfilename = $removeworkdir . $installer::globals::separator . "File.idt"; 265 if ( -f $idtfilename ) { unlink $idtfilename; } 266 unlink $completeremovedest; 267 268 # Packing msm file without "File.idt" 269 if ( $^O =~ /cygwin/i ) { 270 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 271 my $localcompleteremovedest = $completeremovedest; 272 my $localremoveworkdir = $removeworkdir; 273 $localcompleteremovedest =~ s/\//\\\\/g; 274 $localremoveworkdir =~ s/\//\\\\/g; 275 $systemcall = $msidb . " -c -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -i \\\*"; 276 } 277 else 278 { 279 $systemcall = $msidb . " -c -d " . $completeremovedest . " -f " . $removeworkdir . " -i \*"; 280 } 281 $returnvalue = system($systemcall); 282 283 # Using this msm file for merging 284 if ( -f $completeremovedest ) { $completedest = $completeremovedest; } 285 else { installer::exiter::exit_program("ERROR: Could not find msm file without File.idt: $completeremovedest !", "merge_mergemodules_into_msi_database"); } 286 } 287 288 # Saving MergeModule info 289 290 my %onemergemodulehash = (); 291 $onemergemodulehash{'mergefilepath'} = $completedest; 292 $onemergemodulehash{'workdir'} = $workdir; 293 $onemergemodulehash{'cabinetfile'} = $workdir . $installer::globals::separator . $cabinetfile; 294 $onemergemodulehash{'filenumber'} = $filecounter; 295 $onemergemodulehash{'componentnames'} = \%componentnames; 296 $onemergemodulehash{'componentcondition'} = $mergemodule->{'ComponentCondition'}; 297 $onemergemodulehash{'attributes_add'} = $mergemodule->{'Attributes_Add'}; 298 $onemergemodulehash{'cabfilename'} = $cabfilename; 299 $onemergemodulehash{'feature'} = $mergemodule->{'Feature'}; 300 $onemergemodulehash{'rootdir'} = $mergemodule->{'RootDir'}; 301 $onemergemodulehash{'name'} = $mergemodule->{'Name'}; 302 $onemergemodulehash{'mergefilesequence'} = \%mergefilesequence; 303 $onemergemodulehash{'mergeassemblies'} = \%mergeassemblies; 304 $onemergemodulehash{'mergedirectories'} = \%mergedirectories; 305 $onemergemodulehash{'hasmsiassemblies'} = $hasmsiassemblies; 306 $onemergemodulehash{'removefiletable'} = $removefiletable; 307 $onemergemodulehash{'fileidtcontent'} = \@file_idt_content; 308 309 $installer::globals::mergemodules{$mergegid} = \%onemergemodulehash; 310 311 # Collecting all cab files, to copy them into installation set 312 if ( $cabfilename ) { $installer::globals::copy_msm_files{$cabfilename} = $onemergemodulehash{'cabinetfile'}; } 313 314 chdir($from); 315 } 316 317 $infoline = "All Merge Modules successfully analyzed\n"; 318 push( @installer::globals::logfileinfo, $infoline); 319 320 $installer::globals::mergemodules_analyzed = 1; 321 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, stop"); 322 323 $infoline = "\n"; 324 push( @installer::globals::logfileinfo, $infoline); 325 } 326 327 # 2. Change msi database (has to be done for every msi database -> for every language) 328 # a. Merge msm file into msi database: msidb.exe -d <msifile> -m <mergefile> 329 # b. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ... 330 # c. Changing content of msi database in tables: File, Media, Directory, FeatureComponent 331 # d. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ... 332 # e. Copying cabinet file into installation set (later) 333 334 my $counter = 0; 335 my $mergemodulegid; 336 foreach $mergemodulegid (keys %installer::globals::mergemodules) 337 { 338 my $mergemodulehash = $installer::globals::mergemodules{$mergemodulegid}; 339 $counter++; 340 341 installer::logger::include_header_into_logfile("Merging Module: $mergemodulehash->{'name'}"); 342 installer::logger::print_message( "\t... $mergemodulehash->{'name'} ... \n" ); 343 344 $msifilename = installer::converter::make_path_conform($msifilename); 345 my $workdir = $msifilename; 346 installer::pathanalyzer::get_path_from_fullqualifiedname(\$workdir); 347 348 # changing directory 349 my $from = cwd(); 350 my $to = $workdir; 351 chdir($to); 352 353 # Saving original msi database 354 installer::systemactions::copy_one_file($msifilename, "$msifilename\.$counter"); 355 356 # Merging msm file, this is the "real" merge command 357 358 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before merging database"); 359 360 if ( $^O =~ /cygwin/i ) { 361 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 362 my $localmergemodulepath = $mergemodulehash->{'mergefilepath'}; 363 my $localmsifilename = $msifilename; 364 $localmergemodulepath =~ s/\//\\\\/g; 365 $localmsifilename =~ s/\//\\\\/g; 366 $systemcall = $msidb . " -d " . $localmsifilename . " -m " . $localmergemodulepath; 367 } 368 else 369 { 370 $systemcall = $msidb . " -d " . $msifilename . " -m " . $mergemodulehash->{'mergefilepath'}; 371 } 372 $returnvalue = system($systemcall); 373 374 $infoline = "Systemcall: $systemcall\n"; 375 push( @installer::globals::logfileinfo, $infoline); 376 377 if ($returnvalue) 378 { 379 $infoline = "ERROR: Could not execute $systemcall . Returnvalue: $returnvalue!\n"; 380 push( @installer::globals::logfileinfo, $infoline); 381 installer::exiter::exit_program("Could not merge msm file into database: $mergemodulehash->{'mergefilepath'}\n$infoline", "merge_mergemodules_into_msi_database"); 382 } 383 else 384 { 385 $infoline = "Success: Executed $systemcall successfully!\n"; 386 push( @installer::globals::logfileinfo, $infoline); 387 } 388 389 installer::logger::include_timestamp_into_logfile("\nPerformance Info: After merging database"); 390 391 # Saving original idt files 392 if ( -f "File.idt" ) { installer::systemactions::rename_one_file("File.idt", "old.File.idt.$counter"); } 393 if ( -f "Media.idt" ) { installer::systemactions::rename_one_file("Media.idt", "old.Media.idt.$counter"); } 394 if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "old.Directory.idt.$counter"); } 395 if ( -f "Director.idt" ) { installer::systemactions::rename_one_file("Director.idt", "old.Director.idt.$counter"); } 396 if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "old.FeatureComponents.idt.$counter"); } 397 if ( -f "FeatureC.idt" ) { installer::systemactions::rename_one_file("FeatureC.idt", "old.FeatureC.idt.$counter"); } 398 if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "old.MsiAssembly.idt.$counter"); } 399 if ( -f "MsiAssem.idt" ) { installer::systemactions::rename_one_file("MsiAssem.idt", "old.MsiAssem.idt.$counter"); } 400 if ( -f "Componen.idt" ) { installer::systemactions::rename_one_file("Componen.idt", "old.Componen.idt.$counter"); } 401 402 # Extracting tables 403 404 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before extracting tables"); 405 406 my $workingtables = "File Media Directory FeatureComponents"; # required tables 407 # Optional tables can be added now 408 if ( $mergemodulehash->{'hasmsiassemblies'} ) { $workingtables = $workingtables . " MsiAssembly"; } 409 if ( ( $mergemodulehash->{'componentcondition'} ) || ( $mergemodulehash->{'attributes_add'} ) ) { $workingtables = $workingtables . " Component"; } 410 411 # Table "Feature" has to be exported, but it is not necessary to import it. 412 if ( $^O =~ /cygwin/i ) { 413 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 414 my $localmsifilename = $msifilename; 415 my $localworkdir = $workdir; 416 $localmsifilename =~ s/\//\\\\/g; 417 $localworkdir =~ s/\//\\\\/g; 418 $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $workingtables; 419 } 420 else 421 { 422 $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $workingtables; 423 } 424 $returnvalue = system($systemcall); 425 426 $infoline = "Systemcall: $systemcall\n"; 427 push( @installer::globals::logfileinfo, $infoline); 428 429 if ($returnvalue) 430 { 431 $infoline = "ERROR: Could not execute $systemcall !\n"; 432 push( @installer::globals::logfileinfo, $infoline); 433 installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $msifilename !", "merge_mergemodules_into_msi_database"); 434 } 435 else 436 { 437 $infoline = "Success: Executed $systemcall successfully!\n"; 438 push( @installer::globals::logfileinfo, $infoline); 439 } 440 441 installer::logger::include_timestamp_into_logfile("\nPerformance Info: After extracting tables"); 442 443 # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables 444 # creates idt-files, that have long names. 445 446 if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "Director.idt"); } 447 if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "FeatureC.idt"); } 448 if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "MsiAssem.idt"); } 449 if ( -f "Component.idt" ) { installer::systemactions::rename_one_file("Component.idt", "Componen.idt"); } 450 451 # Changing content of tables: File, Media, Directory, FeatureComponent, MsiAssembly, Component 452 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Media table"); 453 change_media_table($mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids); 454 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing File table"); 455 $filesref = change_file_table($mergemodulehash, $workdir, $allupdatesequences, $includepatharrayref, $filesref, $mergemodulegid); 456 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing FeatureComponent table"); 457 change_featurecomponent_table($mergemodulehash, $workdir); 458 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Directory table"); 459 change_directory_table($mergemodulehash, $workdir); 460 if ( $mergemodulehash->{'hasmsiassemblies'} ) 461 { 462 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing MsiAssembly table"); 463 change_msiassembly_table($mergemodulehash, $workdir); 464 } 465 466 if ( ( $mergemodulehash->{'componentcondition'} ) || ( $mergemodulehash->{'attributes_add'} ) ) 467 { 468 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Component table"); 469 change_component_table($mergemodulehash, $workdir); 470 } 471 472 # msidb.exe does not merge InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence. Instead it creates 473 # new tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence that need to be 474 # merged into the three ExecuteSequences with the following process (also into InstallUISequence.idt). 475 476 # Saving original idt files 477 if ( -f "InstallE.idt" ) { installer::systemactions::rename_one_file("InstallE.idt", "old.InstallE.idt.$counter"); } 478 if ( -f "InstallU.idt" ) { installer::systemactions::rename_one_file("InstallU.idt", "old.InstallU.idt.$counter"); } 479 if ( -f "AdminExe.idt" ) { installer::systemactions::rename_one_file("AdminExe.idt", "old.AdminExe.idt.$counter"); } 480 if ( -f "AdvtExec.idt" ) { installer::systemactions::rename_one_file("AdvtExec.idt", "old.AdvtExec.idt.$counter"); } 481 if ( -f "ModuleInstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleInstallExecuteSequence.idt", "old.ModuleInstallExecuteSequence.idt.$counter"); } 482 if ( -f "ModuleAdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdminExecuteSequence.idt", "old.ModuleAdminExecuteSequence.idt.$counter"); } 483 if ( -f "ModuleAdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdvtExecuteSequence.idt", "old.ModuleAdvtExecuteSequence.idt.$counter"); } 484 485 # Extracting tables 486 my $moduleexecutetables = "ModuleInstallExecuteSequence ModuleAdminExecuteSequence ModuleAdvtExecuteSequence"; # new tables 487 my $executetables = "InstallExecuteSequence InstallUISequence AdminExecuteSequence AdvtExecuteSequence"; # tables to be merged 488 489 490 if ( $^O =~ /cygwin/i ) { 491 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 492 my $localmsifilename = $msifilename; 493 my $localworkdir = $workdir; 494 $localmsifilename =~ s/\//\\\\/g; 495 $localworkdir =~ s/\//\\\\/g; 496 $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $moduleexecutetables; 497 } 498 else 499 { 500 $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $moduleexecutetables; 501 } 502 $returnvalue = system($systemcall); 503 504 if ( $^O =~ /cygwin/i ) { 505 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 506 my $localmsifilename = $msifilename; 507 my $localworkdir = $workdir; 508 $localmsifilename =~ s/\//\\\\/g; 509 $localworkdir =~ s/\//\\\\/g; 510 $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $executetables; 511 } 512 else 513 { 514 $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $executetables; 515 } 516 $returnvalue = system($systemcall); 517 518 # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables 519 # creates idt-files, that have long names. 520 521 if ( -f "InstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("InstallExecuteSequence.idt", "InstallE.idt"); } 522 if ( -f "InstallUISequence.idt" ) { installer::systemactions::rename_one_file("InstallUISequence.idt", "InstallU.idt"); } 523 if ( -f "AdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdminExecuteSequence.idt", "AdminExe.idt"); } 524 if ( -f "AdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdvtExecuteSequence.idt", "AdvtExec.idt"); } 525 526 # Merging content of tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence 527 # into tables InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence 528 if ( -f "ModuleInstallExecuteSequence.idt" ) 529 { 530 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallExecuteSequence table"); 531 change_executesequence_table($mergemodulehash, $workdir, "InstallE.idt", "ModuleInstallExecuteSequence.idt"); 532 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallUISequence table"); 533 change_executesequence_table($mergemodulehash, $workdir, "InstallU.idt", "ModuleInstallExecuteSequence.idt"); 534 } 535 536 if ( -f "ModuleAdminExecuteSequence.idt" ) 537 { 538 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdminExecuteSequence table"); 539 change_executesequence_table($mergemodulehash, $workdir, "AdminExe.idt", "ModuleAdminExecuteSequence.idt"); 540 } 541 542 if ( -f "ModuleAdvtExecuteSequence.idt" ) 543 { 544 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdvtExecuteSequence table"); 545 change_executesequence_table($mergemodulehash, $workdir, "AdvtExec.idt", "ModuleAdvtExecuteSequence.idt"); 546 } 547 548 installer::logger::include_timestamp_into_logfile("\nPerformance Info: All tables edited"); 549 550 # Including tables into msi database 551 552 installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before including tables"); 553 554 if ( $^O =~ /cygwin/i ) { 555 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) 556 my $localmsifilename = $msifilename; 557 my $localworkdir = $workdir; 558 $localmsifilename =~ s/\//\\\\/g; 559 $localworkdir =~ s/\//\\\\/g; 560 foreach $table (split / /, $workingtables . ' ' . $executetables) { 561 $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -i " . $table; 562 my $retval = system($systemcall); 563 $infoline = "Systemcall returned $retval: $systemcall\n"; 564 push( @installer::globals::logfileinfo, $infoline); 565 $returnvalue |= $retval; 566 } 567 } 568 else 569 { 570 $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -i " . $workingtables. " " . $executetables; 571 $returnvalue = system($systemcall); 572 $infoline = "Systemcall: $systemcall\n"; 573 push( @installer::globals::logfileinfo, $infoline); 574 575 } 576 577 if ($returnvalue) 578 { 579 $infoline = "ERROR: Could not execute $systemcall !\n"; 580 push( @installer::globals::logfileinfo, $infoline); 581 installer::exiter::exit_program("ERROR: Could not include tables into msi database: $msifilename !", "merge_mergemodules_into_msi_database"); 582 } 583 else 584 { 585 $infoline = "Success: Executed $systemcall successfully!\n"; 586 push( @installer::globals::logfileinfo, $infoline); 587 } 588 589 installer::logger::include_timestamp_into_logfile("\nPerformance Info: After including tables"); 590 591 chdir($from); 592 } 593 594 if ( ! $installer::globals::mergefiles_added_into_collector ) { $installer::globals::mergefiles_added_into_collector = 1; } # Now all mergemodules are merged for one language. 595 596 installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, stop"); 597 } 598 599 return $filesref; 600} 601 602######################################################################### 603# Analyzing the content of the media table. 604######################################################################### 605 606sub analyze_media_file 607{ 608 my ($filecontent, $workdir) = @_; 609 610 my %filehash = (); 611 my $linecount = 0; 612 my $counter = 0; 613 my $filename = "Media.idt"; 614 615 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 616 { 617 if ( $i <= 2 ) { next; } # ignoring first three lines 618 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 619 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\s*$/ ) 620 { 621 my %line = (); 622 # Format: DiskId LastSequence DiskPrompt Cabinet VolumeLabel Source 623 $line{'DiskId'} = $1; 624 $line{'LastSequence'} = $2; 625 $line{'DiskPrompt'} = $3; 626 $line{'Cabinet'} = $4; 627 $line{'VolumeLabel'} = $5; 628 $line{'Source'} = $6; 629 630 $counter++; 631 $filehash{$counter} = \%line; 632 } 633 else 634 { 635 $linecount = $i + 1; 636 installer::exiter::exit_program("ERROR: Unknown line format in table \"$filename\" in \"$workdir\" (line $linecount) !", "analyze_media_file"); 637 } 638 } 639 640 return \%filehash; 641} 642 643######################################################################### 644# Setting the DiskID for the new cabinet file 645######################################################################### 646 647sub get_diskid 648{ 649 my ($mediafile, $allupdatediskids, $cabfilename) = @_; 650 651 my $diskid = 0; 652 my $line; 653 654 if (( $installer::globals::updatedatabase ) && ( exists($allupdatediskids->{$cabfilename}) )) 655 { 656 $diskid = $allupdatediskids->{$cabfilename}; 657 } 658 else 659 { 660 foreach $line ( keys %{$mediafile} ) 661 { 662 if ( $mediafile->{$line}->{'DiskId'} > $diskid ) { $diskid = $mediafile->{$line}->{'DiskId'}; } 663 } 664 665 $diskid++; 666 } 667 668 return $diskid; 669} 670 671######################################################################### 672# Setting the global LastSequence variable 673######################################################################### 674 675sub set_current_last_sequence 676{ 677 my ($mediafile) = @_; 678 679 my $lastsequence = 0; 680 my $line; 681 foreach $line ( keys %{$mediafile} ) 682 { 683 if ( $mediafile->{$line}->{'LastSequence'} > $lastsequence ) { $lastsequence = $mediafile->{$line}->{'LastSequence'}; } 684 } 685 686 $installer::globals::lastsequence_before_merge = $lastsequence; 687} 688 689######################################################################### 690# Setting the LastSequence for the new cabinet file 691######################################################################### 692 693sub get_lastsequence 694{ 695 my ($mergemodulehash, $allupdatelastsequences) = @_; 696 697 my $lastsequence = 0; 698 699 if (( $installer::globals::updatedatabase ) && ( exists($allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}) )) 700 { 701 $lastsequence = $allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}; 702 } 703 else 704 { 705 $lastsequence = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'}; 706 } 707 708 return $lastsequence; 709} 710 711######################################################################### 712# Setting the DiskPrompt for the new cabinet file 713######################################################################### 714 715sub get_diskprompt 716{ 717 my ($mediafile) = @_; 718 719 my $diskprompt = ""; 720 my $line; 721 foreach $line ( keys %{$mediafile} ) 722 { 723 if ( exists($mediafile->{$line}->{'DiskPrompt'}) ) 724 { 725 $diskprompt = $mediafile->{$line}->{'DiskPrompt'}; 726 last; 727 } 728 } 729 730 return $diskprompt; 731} 732 733######################################################################### 734# Setting the VolumeLabel for the new cabinet file 735######################################################################### 736 737sub get_volumelabel 738{ 739 my ($mediafile) = @_; 740 741 my $volumelabel = ""; 742 my $line; 743 foreach $line ( keys %{$mediafile} ) 744 { 745 if ( exists($mediafile->{$line}->{'VolumeLabel'}) ) 746 { 747 $volumelabel = $mediafile->{$line}->{'VolumeLabel'}; 748 last; 749 } 750 } 751 752 return $volumelabel; 753} 754 755######################################################################### 756# Setting the Source for the new cabinet file 757######################################################################### 758 759sub get_source 760{ 761 my ($mediafile) = @_; 762 763 my $source = ""; 764 my $line; 765 foreach $line ( keys %{$mediafile} ) 766 { 767 if ( exists($mediafile->{$line}->{'Source'}) ) 768 { 769 $diskprompt = $mediafile->{$line}->{'Source'}; 770 last; 771 } 772 } 773 774 return $source; 775} 776 777######################################################################### 778# For each Merge Module one new line has to be included into the 779# media table. 780######################################################################### 781 782sub create_new_media_line 783{ 784 my ($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids) = @_; 785 786 my $diskid = get_diskid($mediafile, $allupdatediskids, $mergemodulehash->{'cabfilename'}); 787 my $lastsequence = get_lastsequence($mergemodulehash, $allupdatelastsequences); 788 my $diskprompt = get_diskprompt($mediafile); 789 my $cabinet = $mergemodulehash->{'cabfilename'}; 790 my $volumelabel = get_volumelabel($mediafile); 791 my $source = get_source($mediafile); 792 793 if ( $installer::globals::include_cab_in_msi ) { $cabinet = "\#" . $cabinet; } 794 795 my $newline = "$diskid\t$lastsequence\t$diskprompt\t$cabinet\t$volumelabel\t$source\n"; 796 797 return $newline; 798} 799 800######################################################################### 801# Setting the last diskid in media table. 802######################################################################### 803 804sub get_last_diskid 805{ 806 my ($mediafile) = @_; 807 808 my $lastdiskid = 0; 809 my $line; 810 foreach $line ( keys %{$mediafile} ) 811 { 812 if ( $mediafile->{$line}->{'DiskId'} > $lastdiskid ) { $lastdiskid = $mediafile->{$line}->{'DiskId'}; } 813 } 814 815 return $lastdiskid; 816} 817 818######################################################################### 819# Setting global variable for last cab file name. 820######################################################################### 821 822sub set_last_cabfile_name 823{ 824 my ($mediafile, $lastdiskid) = @_; 825 826 my $line; 827 foreach $line ( keys %{$mediafile} ) 828 { 829 if ( $mediafile->{$line}->{'DiskId'} == $lastdiskid ) { $installer::globals::lastcabfilename = $mediafile->{$line}->{'Cabinet'}; } 830 } 831 my $infoline = "Setting last cabinet file: $installer::globals::lastcabfilename\n"; 832 push( @installer::globals::logfileinfo, $infoline); 833} 834 835######################################################################### 836# In the media table the new cabinet file has to be added or the 837# number of the last cabinet file has to be increased. 838######################################################################### 839 840sub change_media_table 841{ 842 my ( $mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids ) = @_; 843 844 my $infoline = "Changing content of table \"Media\"\n"; 845 push( @installer::globals::logfileinfo, $infoline); 846 847 my $filename = "Media.idt"; 848 if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$workdir\" !", "change_media_table"); } 849 850 my $filecontent = installer::files::read_file($filename); 851 my $mediafile = analyze_media_file($filecontent, $workdir); 852 set_current_last_sequence($mediafile); 853 854 if ( $installer::globals::fix_number_of_cab_files ) 855 { 856 # Determining the line with the highest sequencenumber. That file needs to be updated. 857 my $lastdiskid = get_last_diskid($mediafile); 858 if ( $installer::globals::lastcabfilename eq "" ) { set_last_cabfile_name($mediafile, $lastdiskid); } 859 my $newmaxsequencenumber = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'}; 860 861 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 862 { 863 if ( $i <= 2 ) { next; } # ignoring first three lines 864 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 865 if ( ${$filecontent}[$i] =~ /^\s*(\Q$lastdiskid\E\t)\Q$installer::globals::lastsequence_before_merge\E(\t.*)$/ ) 866 { 867 my $start = $1; 868 my $final = $2; 869 $infoline = "Merge: Old line in media table: ${$filecontent}[$i]\n"; 870 push( @installer::globals::logfileinfo, $infoline); 871 my $newline = $start . $newmaxsequencenumber . $final . "\n"; 872 ${$filecontent}[$i] = $newline; 873 $infoline = "Merge: Changed line in media table: ${$filecontent}[$i]\n"; 874 push( @installer::globals::logfileinfo, $infoline); 875 } 876 } 877 } 878 else 879 { 880 # the new line is identical for all localized databases, but has to be created for each MergeModule ($mergemodulegid) 881 if ( ! exists($installer::globals::merge_media_line{$mergemodulegid}) ) 882 { 883 $installer::globals::merge_media_line{$mergemodulegid} = create_new_media_line($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids); 884 } 885 886 $infoline = "Adding line: $installer::globals::merge_media_line{$mergemodulegid}\n"; 887 push( @installer::globals::logfileinfo, $infoline); 888 889 # adding new line 890 push(@{$filecontent}, $installer::globals::merge_media_line{$mergemodulegid}); 891 } 892 893 # saving file 894 installer::files::save_file($filename, $filecontent); 895} 896 897######################################################################### 898# Putting the directory table content into a hash. 899######################################################################### 900 901sub analyze_directorytable_file 902{ 903 my ($filecontent, $idtfilename) = @_; 904 905 my %dirhash = (); 906 # Iterating over the file content 907 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 908 { 909 if ( $i <= 2 ) { next; } # ignoring first three lines 910 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 911 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) 912 { 913 my %line = (); 914 # Format: Directory Directory_Parent DefaultDir 915 $line{'Directory'} = $1; 916 $line{'Directory_Parent'} = $2; 917 $line{'DefaultDir'} = $3; 918 $line{'linenumber'} = $i; # saving also the line number for direct access 919 920 my $uniquekey = $line{'Directory'}; 921 $dirhash{$uniquekey} = \%line; 922 } 923 else 924 { 925 my $linecount = $i + 1; 926 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_directorytable_file"); 927 } 928 } 929 930 return \%dirhash; 931} 932 933######################################################################### 934# Putting the msi assembly table content into a hash. 935######################################################################### 936 937sub analyze_msiassemblytable_file 938{ 939 my ($filecontent, $idtfilename) = @_; 940 941 my %assemblyhash = (); 942 # Iterating over the file content 943 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 944 { 945 if ( $i <= 2 ) { next; } # ignoring first three lines 946 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 947 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\s*$/ ) 948 { 949 my %line = (); 950 # Format: Component_ Feature_ File_Manifest File_Application Attributes 951 $line{'Component'} = $1; 952 $line{'Feature'} = $2; 953 $line{'File_Manifest'} = $3; 954 $line{'File_Application'} = $4; 955 $line{'Attributes'} = $5; 956 $line{'linenumber'} = $i; # saving also the line number for direct access 957 958 my $uniquekey = $line{'Component'}; 959 $assemblyhash{$uniquekey} = \%line; 960 } 961 else 962 { 963 my $linecount = $i + 1; 964 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_msiassemblytable_file"); 965 } 966 } 967 968 return \%assemblyhash; 969} 970 971######################################################################### 972# Putting the file table content into a hash. 973######################################################################### 974 975sub analyze_filetable_file 976{ 977 my ( $filecontent, $idtfilename ) = @_; 978 979 my %filehash = (); 980 # Iterating over the file content 981 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 982 { 983 if ( $i <= 2 ) { next; } # ignoring first three lines 984 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 985 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.+?)\s*$/ ) 986 { 987 my %line = (); 988 # Format: File Component_ FileName FileSize Version Language Attributes Sequence 989 $line{'File'} = $1; 990 $line{'Component'} = $2; 991 $line{'FileName'} = $3; 992 $line{'FileSize'} = $4; 993 $line{'Version'} = $5; 994 $line{'Language'} = $6; 995 $line{'Attributes'} = $7; 996 $line{'Sequence'} = $8; 997 $line{'linenumber'} = $i; # saving also the line number for direct access 998 999 my $uniquekey = $line{'File'}; 1000 $filehash{$uniquekey} = \%line; 1001 } 1002 else 1003 { 1004 my $linecount = $i + 1; 1005 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_filetable_file"); 1006 } 1007 } 1008 1009 return \%filehash; 1010} 1011 1012######################################################################### 1013# Creating a new line for the directory table. 1014######################################################################### 1015 1016sub get_new_line_for_directory_table 1017{ 1018 my ($dir) = @_; 1019 1020 my $newline = "$dir->{'Directory'}\t$dir->{'Directory_Parent'}\t$dir->{'DefaultDir'}\n"; 1021 1022 return $newline; 1023} 1024 1025######################################################################### 1026# Creating a new line for the file table. 1027######################################################################### 1028 1029sub get_new_line_for_file_table 1030{ 1031 my ($file) = @_; 1032 1033 my $newline = "$file->{'File'}\t$file->{'Component'}\t$file->{'FileName'}\t$file->{'FileSize'}\t$file->{'Version'}\t$file->{'Language'}\t$file->{'Attributes'}\t$file->{'Sequence'}\n"; 1034 1035 return $newline; 1036} 1037 1038######################################################################### 1039# Creating a new line for the msiassembly table. 1040######################################################################### 1041 1042sub get_new_line_for_msiassembly_table 1043{ 1044 my ($assembly) = @_; 1045 1046 my $newline = "$assembly->{'Component'}\t$assembly->{'Feature'}\t$assembly->{'File_Manifest'}\t$assembly->{'File_Application'}\t$assembly->{'Attributes'}\n"; 1047 1048 return $newline; 1049} 1050 1051######################################################################### 1052# Sorting the files collector, if there are files, following 1053# the merge module files. 1054######################################################################### 1055 1056sub sort_files_collector_for_sequence 1057{ 1058 my ($filesref) = @_; 1059 1060 my @sortarray = (); 1061 my %helphash = (); 1062 1063 for ( my $i = 0; $i <= $#{$filesref}; $i++ ) 1064 { 1065 my $onefile = ${$filesref}[$i]; 1066 if ( ! exists($onefile->{'sequencenumber'}) ) { installer::exiter::exit_program("ERROR: Could not find sequencenumber for file: $onefile->{'uniquename'} !", "sort_files_collector_for_sequence"); } 1067 my $sequence = $onefile->{'sequencenumber'}; 1068 $helphash{$sequence} = $onefile; 1069 } 1070 1071 foreach my $seq ( sort { $a <=> $b } keys %helphash ) { push(@sortarray, $helphash{$seq}); } 1072 1073 return \@sortarray; 1074} 1075 1076######################################################################### 1077# In the file table "Sequence" and "Attributes" have to be changed. 1078######################################################################### 1079 1080sub change_file_table 1081{ 1082 my ($mergemodulehash, $workdir, $allupdatesequenceshashref, $includepatharrayref, $filesref, $mergemodulegid) = @_; 1083 1084 my $infoline = "Changing content of table \"File\"\n"; 1085 push( @installer::globals::logfileinfo, $infoline); 1086 1087 my $idtfilename = "File.idt"; 1088 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_file_table"); } 1089 1090 my $filecontent = installer::files::read_file($idtfilename); 1091 1092 # If File.idt needed to be removed before the msm database was merged into the msi database, 1093 # now it is time to add the content into File.idt 1094 if ( $mergemodulehash->{'removefiletable'} ) 1095 { 1096 for ( my $i = 0; $i <= $#{$mergemodulehash->{'fileidtcontent'}}; $i++ ) 1097 { 1098 push(@{$filecontent}, ${$mergemodulehash->{'fileidtcontent'}}[$i]); 1099 } 1100 } 1101 1102 # Unpacking the MergeModule.CABinet (only once) 1103 # Unpacking into temp directory. Warning: expand.exe has problems with very long unpack directories. 1104 1105 my $empty = ""; 1106 my $unpackdir = installer::systemactions::create_directories("cab", \$empty); 1107 push(@installer::globals::removedirs, $unpackdir); 1108 $unpackdir = $unpackdir . $installer::globals::separator . $mergemodulegid; 1109 1110 my %newfileshash = (); 1111 if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector )) 1112 { 1113 if ( ! -d $unpackdir ) { installer::systemactions::create_directory($unpackdir); } 1114 1115 # changing directory 1116 my $from = cwd(); 1117 my $to = $mergemodulehash->{'workdir'}; 1118 if ( $^O =~ /cygwin/i ) { 1119 $to = qx(cygpath -u "$to"); 1120 chomp $to; 1121 } 1122 1123 chdir($to) || die "Could not chdir to \"$to\"\n"; 1124 1125 # Unpack the cab file, so that in can be included into the last office cabinet file. 1126 # Not using cabarc.exe from cabsdk for unpacking cabinet files, but "expand.exe" that 1127 # should be available on every Windows system. 1128 1129 $infoline = "Unpacking cabinet file: $mergemodulehash->{'cabinetfile'}\n"; 1130 push( @installer::globals::logfileinfo, $infoline); 1131 1132 # Avoid the Cygwin expand command 1133 my $expandfile = "expand.exe"; # Has to be in the path 1134 if ( $^O =~ /cygwin/i ) { 1135 $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe); 1136 chomp $expandfile; 1137 } 1138 1139 my $cabfilename = "MergeModule.CABinet"; 1140 1141 my $systemcall = ""; 1142 if ( $^O =~ /cygwin/i ) { 1143 my $localunpackdir = qx(cygpath -m "$unpackdir"); 1144 chomp $localunpackdir; 1145 $systemcall = $expandfile . " " . $cabfilename . " -F:\\\* " . $localunpackdir; 1146 } 1147 else 1148 { 1149 $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " 2\>\&1"; 1150 } 1151 1152 my $returnvalue = system($systemcall); 1153 1154 $infoline = "Systemcall: $systemcall\n"; 1155 push( @installer::globals::logfileinfo, $infoline); 1156 1157 if ($returnvalue) 1158 { 1159 $infoline = "ERROR: Could not execute $systemcall !\n"; 1160 push( @installer::globals::logfileinfo, $infoline); 1161 installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table"); 1162 } 1163 else 1164 { 1165 $infoline = "Success: Executed $systemcall successfully!\n"; 1166 push( @installer::globals::logfileinfo, $infoline); 1167 } 1168 1169 chdir($from); 1170 } 1171 1172 # For performance reasons creating a hash with file names and rows 1173 # The content of File.idt is changed after every merge -> content cannot be saved in global hash 1174 $merge_filetablehashref = analyze_filetable_file($filecontent, $idtfilename); 1175 1176 my $attributes = "16384"; # Always 1177 1178 my $filename; 1179 foreach $filename (keys %{$mergemodulehash->{'mergefilesequence'}} ) 1180 { 1181 my $mergefilesequence = $mergemodulehash->{'mergefilesequence'}->{$filename}; 1182 1183 if ( ! exists($merge_filetablehashref->{$filename}) ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$idtfilename\" !", "change_file_table"); } 1184 my $filehash = $merge_filetablehashref->{$filename}; 1185 my $linenumber = $filehash->{'linenumber'}; 1186 1187 # <- this line has to be changed concerning "Sequence" and "Attributes" 1188 $filehash->{'Attributes'} = $attributes; 1189 1190 # If this is an update process, the sequence numbers have to be reused. 1191 if ( $installer::globals::updatedatabase ) 1192 { 1193 if ( ! exists($allupdatesequenceshashref->{$filehash->{'File'}}) ) { installer::exiter::exit_program("ERROR: Sequence not defined for file \"$filehash->{'File'}\" !", "change_file_table"); } 1194 $filehash->{'Sequence'} = $allupdatesequenceshashref->{$filehash->{'File'}}; 1195 # Saving all mergemodule sequence numbers. This is important for creating ddf files 1196 $installer::globals::allmergemodulefilesequences{$filehash->{'Sequence'}} = 1; 1197 } 1198 else 1199 { 1200 # Important saved data: $installer::globals::lastsequence_before_merge. 1201 # This mechanism keeps the correct order inside the new cabinet file. 1202 $filehash->{'Sequence'} = $filehash->{'Sequence'} + $installer::globals::lastsequence_before_merge; 1203 } 1204 1205 my $oldline = ${$filecontent}[$linenumber]; 1206 my $newline = get_new_line_for_file_table($filehash); 1207 ${$filecontent}[$linenumber] = $newline; 1208 1209 $infoline = "Merge, replacing line:\n"; 1210 push( @installer::globals::logfileinfo, $infoline); 1211 $infoline = "Old: $oldline\n"; 1212 push( @installer::globals::logfileinfo, $infoline); 1213 $infoline = "New: $newline\n"; 1214 push( @installer::globals::logfileinfo, $infoline); 1215 1216 # Adding files to the files collector (but only once) 1217 if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector )) 1218 { 1219 # If the number of cabinet files is kept constant, 1220 # all files from the mergemodule cabinet files will 1221 # be integrated into the last office cabinet file 1222 # (installer::globals::lastcabfilename). 1223 # Therefore the files must now be added to the filescollector, 1224 # so that they will be integrated into the ddf files. 1225 1226 # Problem with very long filenames -> copying to shorter filenames 1227 my $newfilename = "f" . $filehash->{'Sequence'}; 1228 my $completesource = $unpackdir . $installer::globals::separator . $filehash->{'File'}; 1229 my $completedest = $unpackdir . $installer::globals::separator . $newfilename; 1230 installer::systemactions::copy_one_file($completesource, $completedest); 1231 1232 my $locallastcabfilename = $installer::globals::lastcabfilename; 1233 if ( $locallastcabfilename =~ /^\s*\#/ ) { $locallastcabfilename =~ s/^\s*\#//; } # removing beginning hashes 1234 1235 # Create new file hash for file collector 1236 my %newfile = (); 1237 $newfile{'sequencenumber'} = $filehash->{'Sequence'}; 1238 $newfile{'assignedsequencenumber'} = $filehash->{'Sequence'}; 1239 $newfile{'cabinet'} = $locallastcabfilename; 1240 $newfile{'sourcepath'} = $completedest; 1241 $newfile{'componentname'} = $filehash->{'Component'}; 1242 $newfile{'uniquename'} = $filehash->{'File'}; 1243 $newfile{'Name'} = $filehash->{'File'}; 1244 1245 # Saving in globals sequence hash 1246 $installer::globals::uniquefilenamesequence{$filehash->{'File'}} = $filehash->{'Sequence'}; 1247 1248 if ( ! -f $newfile{'sourcepath'} ) { installer::exiter::exit_program("ERROR: File \"$newfile{'sourcepath'}\" must exist!", "change_file_table"); } 1249 1250 # Collecting all new files. Attention: This files must be included into files collector in correct order! 1251 $newfileshash{$filehash->{'Sequence'}} = \%newfile; 1252 # push(@{$filesref}, \%newfile); -> this is not the correct order 1253 } 1254 } 1255 1256 # Now the files can be added to the files collector 1257 # In the case of an update process, there can be new files, that have to be added after the merge module files. 1258 # Warning: In multilingual installation sets, the files only have to be added once to the files collector! 1259 1260 if ( ! $installer::globals::mergefiles_added_into_collector ) 1261 { 1262 foreach my $localsequence ( sort { $a <=> $b } keys %newfileshash ) { push(@{$filesref}, $newfileshash{$localsequence}); } 1263 if ( $installer::globals::newfilesexist ) { $filesref = sort_files_collector_for_sequence($filesref); } 1264 # $installer::globals::mergefiles_added_into_collector = 1; -> Not yet. Only if all mergemodules are merged for one language. 1265 } 1266 1267 # Saving the idt file (for every language) 1268 installer::files::save_file($idtfilename, $filecontent); 1269 1270 return $filesref; 1271} 1272 1273######################################################################### 1274# Reading the file "Director.idt". The Directory, that is defined in scp 1275# has to be defined in this table. 1276######################################################################### 1277 1278sub collect_directories 1279{ 1280 my $idtfilename = "Director.idt"; 1281 my $filecontent = installer::files::read_file($idtfilename); 1282 1283 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 1284 { 1285 if ( $i <= 2 ) { next; } # ignoring first three lines 1286 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 1287 # Format: Directory Directory_Parent DefaultDir 1288 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) 1289 { 1290 $installer::globals::merge_alldirectory_hash{$1} = 1; 1291 } 1292 else 1293 { 1294 my $linecount = $i + 1; 1295 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_directories"); 1296 } 1297 } 1298} 1299 1300######################################################################### 1301# Reading the file "Feature.idt". The Feature, that is defined in scp 1302# has to be defined in this table. 1303######################################################################### 1304 1305sub collect_feature 1306{ 1307 my $idtfilename = "Feature.idt"; 1308 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "collect_feature"); } 1309 my $filecontent = installer::files::read_file($idtfilename); 1310 1311 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 1312 { 1313 if ( $i <= 2 ) { next; } # ignoring first three lines 1314 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 1315 # Format: Feature Feature_Parent Title Description Display Level Directory_ Attributes 1316 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 1317 { 1318 $installer::globals::merge_allfeature_hash{$1} = 1; 1319 } 1320 else 1321 { 1322 my $linecount = $i + 1; 1323 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_feature"); 1324 } 1325 } 1326} 1327 1328######################################################################### 1329# In the featurecomponent table, the new connections have to be added. 1330######################################################################### 1331 1332sub change_featurecomponent_table 1333{ 1334 my ($mergemodulehash, $workdir) = @_; 1335 1336 my $infoline = "Changing content of table \"FeatureComponents\"\n"; 1337 push( @installer::globals::logfileinfo, $infoline); 1338 1339 my $idtfilename = "FeatureC.idt"; 1340 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_featurecomponent_table"); } 1341 1342 my $filecontent = installer::files::read_file($idtfilename); 1343 1344 # Simply adding for each new component one line. The Feature has to be defined in scp project. 1345 my $feature = $mergemodulehash->{'feature'}; 1346 1347 if ( ! $installer::globals::mergefeaturecollected ) 1348 { 1349 collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash 1350 $installer::globals::mergefeaturecollected = 1; 1351 } 1352 1353 if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) ) 1354 { 1355 installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_featurecomponent_table"); 1356 } 1357 1358 my $component; 1359 foreach $component ( keys %{$mergemodulehash->{'componentnames'}} ) 1360 { 1361 my $line = "$feature\t$component\n"; 1362 push(@{$filecontent}, $line); 1363 $infoline = "Adding line: $line\n"; 1364 push( @installer::globals::logfileinfo, $infoline); 1365 } 1366 1367 # saving file 1368 installer::files::save_file($idtfilename, $filecontent); 1369} 1370 1371############################################################################### 1372# In the components table, the conditions or attributes of merge modules should be updated 1373############################################################################### 1374 1375sub change_component_table 1376{ 1377 my ($mergemodulehash, $workdir) = @_; 1378 1379 my $infoline = "Changing content of table \"Component\"\n"; 1380 push( @installer::globals::logfileinfo, $infoline); 1381 1382 my $idtfilename = "Componen.idt"; 1383 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_component_table"); } 1384 1385 my $filecontent = installer::files::read_file($idtfilename); 1386 1387 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 1388 { 1389 my $component; 1390 foreach $component ( keys %{$mergemodulehash->{'componentnames'}} ) 1391 { 1392 if ( my ( $comp_, $compid_, $dir_, $attr_, $cond_, $keyp_ ) = ${$filecontent}[$i] =~ /^\s*($component)\t(.*?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\s*$/) 1393 { 1394 my $newattr_ = ( $attr_ =~ /^\s*0x/ ) ? hex($attr_) : $attr_; 1395 if ( $mergemodulehash->{'attributes_add'} ) 1396 { 1397 $infoline = "Adding attribute(s) ($mergemodulehash->{'attributes_add'}) from scp2 to component $comp_\n"; 1398 push( @installer::globals::logfileinfo, $infoline); 1399 if ( $mergemodulehash->{'attributes_add'} =~ /^\s*0x/ ) 1400 { 1401 $newattr_ = $newattr_ | hex($mergemodulehash->{'attributes_add'}); 1402 } 1403 else 1404 { 1405 $newattr_ = $newattr_ | $mergemodulehash->{'attributes_add'}; 1406 } 1407 $infoline = "Old attribute(s): $attr_\nNew attribute(s): $newattr_\n"; 1408 push( @installer::globals::logfileinfo, $infoline); 1409 } 1410 my $newcond_ = $cond_; 1411 if ( $mergemodulehash->{'componentcondition'} ) 1412 { 1413 $infoline = "Adding condition ($mergemodulehash->{'componentcondition'}) from scp2 to component $comp_\n"; 1414 push( @installer::globals::logfileinfo, $infoline); 1415 if ($cond_) 1416 { 1417 $newcond_ = "($cond_) AND ($mergemodulehash->{'componentcondition'})"; 1418 } 1419 else 1420 { 1421 $newcond_ = "$mergemodulehash->{'componentcondition'}"; 1422 } 1423 $infoline = "Old condition: $cond_\nNew condition: $newcond_\n"; 1424 push( @installer::globals::logfileinfo, $infoline); 1425 } 1426 ${$filecontent}[$i] = "$comp_\t$compid_\t$dir_\t$newattr_\t$newcond_\t$keyp_\n"; 1427 } 1428 } 1429 } 1430 1431 # saving file 1432 installer::files::save_file($idtfilename, $filecontent); 1433} 1434 1435######################################################################### 1436# In the directory table, the directory parent has to be changed, 1437# if it is not TARGETDIR. 1438######################################################################### 1439 1440sub change_directory_table 1441{ 1442 my ($mergemodulehash, $workdir) = @_; 1443 1444 # directory for MergeModule has to be defined in scp project 1445 my $scpdirectory = $mergemodulehash->{'rootdir'}; 1446 1447 if ( $scpdirectory ne "TARGETDIR" ) # TARGETDIR works fine, when using msidb.exe 1448 { 1449 my $infoline = "Changing content of table \"Directory\"\n"; 1450 push( @installer::globals::logfileinfo, $infoline); 1451 1452 my $idtfilename = "Director.idt"; 1453 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_directory_table"); } 1454 1455 my $filecontent = installer::files::read_file($idtfilename); 1456 1457 if ( ! $installer::globals::mergedirectoriescollected ) 1458 { 1459 collect_directories(); # putting content into %installer::globals::merge_alldirectory_hash, only first column! 1460 $installer::globals::mergedirectoriescollected = 1; 1461 } 1462 1463 if ( ! exists($installer::globals::merge_alldirectory_hash{$scpdirectory}) ) 1464 { 1465 installer::exiter::exit_program("ERROR: Unknown directory defined in scp: \"$scpdirectory\" . Not defined in table \"Directory\" !", "change_directory_table"); 1466 } 1467 1468 # If the definition in scp is okay, now the complete content of "Director.idt" can be analyzed 1469 my $merge_directorytablehashref = analyze_directorytable_file($filecontent, $idtfilename); 1470 1471 my $directory; 1472 foreach $directory (keys %{$mergemodulehash->{'mergedirectories'}} ) 1473 { 1474 if ( ! exists($merge_directorytablehashref->{$directory}) ) { installer::exiter::exit_program("ERROR: Could not find directory \"$directory\" in \"$idtfilename\" !", "change_directory_table"); } 1475 my $dirhash = $merge_directorytablehashref->{$directory}; 1476 my $linenumber = $dirhash->{'linenumber'}; 1477 1478 # <- this line has to be changed concerning "Directory_Parent", 1479 # if the current value is "TARGETDIR", which is the default value from msidb.exe 1480 1481 if ( $dirhash->{'Directory_Parent'} eq "TARGETDIR" ) 1482 { 1483 $dirhash->{'Directory_Parent'} = $scpdirectory; 1484 1485 my $oldline = ${$filecontent}[$linenumber]; 1486 my $newline = get_new_line_for_directory_table($dirhash); 1487 ${$filecontent}[$linenumber] = $newline; 1488 1489 $infoline = "Merge, replacing line:\n"; 1490 push( @installer::globals::logfileinfo, $infoline); 1491 $infoline = "Old: $oldline\n"; 1492 push( @installer::globals::logfileinfo, $infoline); 1493 $infoline = "New: $newline\n"; 1494 push( @installer::globals::logfileinfo, $infoline); 1495 } 1496 } 1497 1498 # saving file 1499 installer::files::save_file($idtfilename, $filecontent); 1500 } 1501} 1502 1503######################################################################### 1504# In the msiassembly table, the feature has to be changed. 1505######################################################################### 1506 1507sub change_msiassembly_table 1508{ 1509 my ($mergemodulehash, $workdir) = @_; 1510 1511 my $infoline = "Changing content of table \"MsiAssembly\"\n"; 1512 push( @installer::globals::logfileinfo, $infoline); 1513 1514 my $idtfilename = "MsiAssem.idt"; 1515 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_msiassembly_table"); } 1516 1517 my $filecontent = installer::files::read_file($idtfilename); 1518 1519 # feature has to be defined in scp project 1520 my $feature = $mergemodulehash->{'feature'}; 1521 1522 if ( ! $installer::globals::mergefeaturecollected ) 1523 { 1524 collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash 1525 $installer::globals::mergefeaturecollected = 1; 1526 } 1527 1528 if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) ) 1529 { 1530 installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_msiassembly_table"); 1531 } 1532 1533 my $merge_msiassemblytablehashref = analyze_msiassemblytable_file($filecontent, $idtfilename); 1534 1535 my $component; 1536 foreach $component (keys %{$mergemodulehash->{'mergeassemblies'}} ) 1537 { 1538 if ( ! exists($merge_msiassemblytablehashref->{$component}) ) { installer::exiter::exit_program("ERROR: Could not find component \"$component\" in \"$idtfilename\" !", "change_msiassembly_table"); } 1539 my $assemblyhash = $merge_msiassemblytablehashref->{$component}; 1540 my $linenumber = $assemblyhash->{'linenumber'}; 1541 1542 # <- this line has to be changed concerning "Feature" 1543 $assemblyhash->{'Feature'} = $feature; 1544 1545 my $oldline = ${$filecontent}[$linenumber]; 1546 my $newline = get_new_line_for_msiassembly_table($assemblyhash); 1547 ${$filecontent}[$linenumber] = $newline; 1548 1549 $infoline = "Merge, replacing line:\n"; 1550 push( @installer::globals::logfileinfo, $infoline); 1551 $infoline = "Old: $oldline\n"; 1552 push( @installer::globals::logfileinfo, $infoline); 1553 $infoline = "New: $newline\n"; 1554 push( @installer::globals::logfileinfo, $infoline); 1555 } 1556 1557 # saving file 1558 installer::files::save_file($idtfilename, $filecontent); 1559} 1560 1561######################################################################### 1562# Creating file content hash 1563######################################################################### 1564 1565sub make_executeidtcontent_hash 1566{ 1567 my ($filecontent, $idtfilename) = @_; 1568 1569 my %newhash = (); 1570 1571 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 1572 { 1573 if ( $i <= 2 ) { next; } # ignoring first three lines 1574 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 1575 # Format for all sequence tables: Action Condition Sequence 1576 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) 1577 { 1578 my %onehash = (); 1579 $onehash{'Action'} = $1; 1580 $onehash{'Condition'} = $2; 1581 $onehash{'Sequence'} = $3; 1582 $newhash{$onehash{'Action'}} = \%onehash; 1583 } 1584 else 1585 { 1586 my $linecount = $i + 1; 1587 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash"); 1588 } 1589 } 1590 1591 return \%newhash; 1592} 1593 1594######################################################################### 1595# Creating file content hash 1596######################################################################### 1597 1598sub make_moduleexecuteidtcontent_hash 1599{ 1600 my ($filecontent, $idtfilename) = @_; 1601 1602 my %newhash = (); 1603 1604 for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) 1605 { 1606 if ( $i <= 2 ) { next; } # ignoring first three lines 1607 if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines 1608 # Format for all module sequence tables: Action Sequence BaseAction After Condition 1609 if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) 1610 { 1611 my %onehash = (); 1612 $onehash{'Action'} = $1; 1613 $onehash{'Sequence'} = $2; 1614 $onehash{'BaseAction'} = $3; 1615 $onehash{'After'} = $4; 1616 $onehash{'Condition'} = $5; 1617 $newhash{$onehash{'Action'}} = \%onehash; 1618 } 1619 else 1620 { 1621 my $linecount = $i + 1; 1622 installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash"); 1623 } 1624 } 1625 1626 return \%newhash; 1627} 1628 1629######################################################################### 1630# ExecuteSequence tables need to be merged with 1631# ModuleExecuteSequence tables created by msidb.exe. 1632######################################################################### 1633 1634sub change_executesequence_table 1635{ 1636 my ($mergemodulehash, $workdir, $idtfilename, $moduleidtfilename) = @_; 1637 1638 my $infoline = "Changing content of table \"$idtfilename\"\n"; 1639 push( @installer::globals::logfileinfo, $infoline); 1640 1641 if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_executesequence_table"); } 1642 if ( ! -f $moduleidtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$moduleidtfilename\" in \"$workdir\" !", "change_executesequence_table"); } 1643 1644 # Reading file content 1645 my $idtfilecontent = installer::files::read_file($idtfilename); 1646 my $moduleidtfilecontent = installer::files::read_file($moduleidtfilename); 1647 1648 # Converting to hash 1649 my $idtcontenthash = make_executeidtcontent_hash($idtfilecontent, $idtfilename); 1650 my $moduleidtcontenthash = make_moduleexecuteidtcontent_hash($moduleidtfilecontent, $moduleidtfilename); 1651 1652 # Merging 1653 foreach my $action ( keys %{$moduleidtcontenthash} ) 1654 { 1655 if ( exists($idtcontenthash->{$action}) ) { next; } # Action already exists, can be ignored 1656 1657 if (( $idtfilename eq "InstallU.idt" ) && ( ! ( $action =~ /^\s*WindowsFolder\./ ))) { next; } # Only "WindowsFolder.*" CustomActions for UI Sequence table 1658 1659 my $actionhashref = $moduleidtcontenthash->{$action}; 1660 if ( $actionhashref->{'Sequence'} ne "" ) 1661 { 1662 # Format for all sequence tables: Action Condition Sequence 1663 my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $actionhashref->{'Sequence'} . "\n"; 1664 # Adding to table 1665 push(@{$idtfilecontent}, $newline); 1666 # Also adding to hash 1667 my %idttablehash = (); 1668 $idttablehash{'Action'} = $actionhashref->{'Action'}; 1669 $idttablehash{'Condition'} = $actionhashref->{'Condition'}; 1670 $idttablehash{'Sequence'} = $actionhashref->{'Sequence'}; 1671 $idtcontenthash->{$action} = \%idttablehash; 1672 1673 } 1674 else # no sequence defined, using syntax "BaseAction" and "After" 1675 { 1676 my $baseactionname = $actionhashref->{'BaseAction'}; 1677 # If this baseactionname is not defined in execute idt file, it is not possible to merge 1678 if ( ! exists($idtcontenthash->{$baseactionname}) ) { installer::exiter::exit_program("ERROR: Merge problem: Could not find action \"$baseactionname\" in file \"$idtfilename\" !", "change_executesequence_table"); } 1679 1680 my $baseaction = $idtcontenthash->{$baseactionname}; 1681 my $sequencenumber = $baseaction->{'Sequence'}; 1682 if ( $actionhashref->{'After'} == 1 ) { $sequencenumber = $sequencenumber + 1; } 1683 else { $sequencenumber = $sequencenumber - 1; } 1684 1685 # Format for all sequence tables: Action Condition Sequence 1686 my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $sequencenumber . "\n"; 1687 # Adding to table 1688 push(@{$idtfilecontent}, $newline); 1689 # Also adding to hash 1690 my %idttablehash = (); 1691 $idttablehash{'Action'} = $actionhashref->{'Action'}; 1692 $idttablehash{'Condition'} = $actionhashref->{'Condition'}; 1693 $idttablehash{'Sequence'} = $sequencenumber; 1694 $idtcontenthash->{$action} = \%idttablehash; 1695 } 1696 } 1697 1698 # saving file 1699 installer::files::save_file($idtfilename, $idtfilecontent); 1700} 1701 1702 17031; 1704