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