diff options
Diffstat (limited to 'solenv/bin/modules/installer/windows/mergemodule.pm')
-rwxr-xr-x | solenv/bin/modules/installer/windows/mergemodule.pm | 1636 |
1 files changed, 0 insertions, 1636 deletions
diff --git a/solenv/bin/modules/installer/windows/mergemodule.pm b/solenv/bin/modules/installer/windows/mergemodule.pm deleted file mode 100755 index 833b476f1..000000000 --- a/solenv/bin/modules/installer/windows/mergemodule.pm +++ /dev/null @@ -1,1636 +0,0 @@ -#************************************************************************* -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# Copyright 2000, 2010 Oracle and/or its affiliates. -# -# OpenOffice.org - a multi-platform office productivity suite -# -# This file is part of OpenOffice.org. -# -# OpenOffice.org is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# only, as published by the Free Software Foundation. -# -# OpenOffice.org is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License version 3 for more details -# (a copy is included in the LICENSE file that accompanied this code). -# -# You should have received a copy of the GNU Lesser General Public License -# version 3 along with OpenOffice.org. If not, see -# <http://www.openoffice.org/license.html> -# for a copy of the LGPLv3 License. -# -#************************************************************************* - -package installer::windows::mergemodule; - -use Cwd; -use Digest::MD5; -use installer::converter; -use installer::exiter; -use installer::files; -use installer::globals; -use installer::logger; -use installer::pathanalyzer; -use installer::remover; -use installer::scriptitems; -use installer::systemactions; -use installer::worker; -use installer::windows::idtglobal; -use installer::windows::language; - -################################################################# -# Merging the Windows MergeModules into the msi database. -################################################################# - -sub merge_mergemodules_into_msi_database -{ - my ($mergemodules, $filesref, $msifilename, $languagestringref, $language, $languagefile, $allvariables, $includepatharrayref, $allupdatesequences, $allupdatelastsequences, $allupdatediskids) = @_; - - my $domerge = 0; - if (( $#{$mergemodules} > -1 ) && ( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack ) && ( ! $installer::globals::helppack )) { $domerge = 1; } - - if ( $domerge ) - { - installer::logger::include_header_into_logfile("Merging merge modules into msi database"); - installer::logger::print_message( "... merging msm files into msi database ... \n" ); - installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, start"); - - my $msidb = "msidb.exe"; # Has to be in the path - my $cabinetfile = "MergeModule.CABinet"; # the name of each cabinet file in a merge file - my $infoline = ""; - my $systemcall = ""; - my $returnvalue = ""; - - # 1. Analyzing the MergeModule (has only to be done once) - # a. -> Extracting cabinet file: msidb.exe -d <msmfile> -x MergeModule.CABinet - # b. -> Number of files in cabinet file: msidb.exe -d <msmfile> -f <directory> -e File - # c. -> List of components: msidb.exe -d <msmfile> -f <directory> -e Component - - if ( ! $installer::globals::mergemodules_analyzed ) - { - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, start"); - $infoline = "Analyzing all Merge Modules\n\n"; - push( @installer::globals::logfileinfo, $infoline); - - %installer::globals::mergemodules = (); - - my $mergemoduledir = installer::systemactions::create_directories("mergefiles", $languagestringref); - - my $mergemodule; - foreach $mergemodule ( @{$mergemodules} ) - { - my $filename = $mergemodule->{'Name'}; - my $mergefile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1); - - if ( ! -f $$mergefile ) { installer::exiter::exit_program("ERROR: msm file not found: $filename !", "merge_mergemodules_into_msi_database"); } - my $completesource = $$mergefile; - - my $mergegid = $mergemodule->{'gid'}; - my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid; - if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); } - - $infoline = "Analyzing Merge Module: $filename\n"; - push( @installer::globals::logfileinfo, $infoline); - - # copy msm file into working directory - my $completedest = $workdir . $installer::globals::separator . $filename; - installer::systemactions::copy_one_file($completesource, $completedest); - if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "merge_mergemodules_into_msi_database"); } - - # changing directory - my $from = cwd(); - my $to = $workdir; - chdir($to); - - # remove an existing cabinet file - if ( -f $cabinetfile ) { unlink($cabinetfile); } - - # exclude cabinet file - $systemcall = $msidb . " -d " . $filename . " -x " . $cabinetfile; - $returnvalue = system($systemcall); - - $infoline = "Systemcall: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall !\n"; - push( @installer::globals::logfileinfo, $infoline); - installer::exiter::exit_program("ERROR: Could not extract cabinet file from merge file: $completedest !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - # exclude tables from mergefile - # Attention: All listed tables have to exist in the database. If they not exist, an error window pops up - # and the return value of msidb.exe is not zero. The error window makes it impossible to check the existence - # of a table with the help of the return value. - # Solution: Export of all tables by using "*" . Some tables must exist (File Component Directory), other - # tables do not need to exist (MsiAssembly). - - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localworkdir = $workdir; - $localworkdir =~ s/\//\\\\/g; - $systemcall = $msidb . " -d " . $filename . " -f " . $localworkdir . " -e \\\*"; - } - else - { - $systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e \*"; - } - - $returnvalue = system($systemcall); - - $infoline = "Systemcall: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall !\n"; - push( @installer::globals::logfileinfo, $infoline); - installer::exiter::exit_program("ERROR: Could not exclude tables from merge file: $completedest !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - # Determining files - my $idtfilename = "File.idt"; # must exist - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } - my $filecontent = installer::files::read_file($idtfilename); - my @file_idt_content = (); - my $filecounter = 0; - my %mergefilesequence = (); - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - $filecounter++; - push(@file_idt_content, ${$filecontent}[$i]); - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(\d+?)\s*$/ ) - { - my $filename = $1; - my $filesequence = $8; - $mergefilesequence{$filename} = $filesequence; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "merge_mergemodules_into_msi_database"); - } - } - - # Determining components - $idtfilename = "Component.idt"; # must exist - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } - $filecontent = installer::files::read_file($idtfilename); - my %componentnames = (); - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $componentnames{$1} = 1; } - } - - # Determining directories - $idtfilename = "Directory.idt"; # must exist - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: File \"$idtfilename\" not found in directory \"$workdir\" !", "merge_mergemodules_into_msi_database"); } - $filecontent = installer::files::read_file($idtfilename); - my %mergedirectories = (); - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergedirectories{$1} = 1; } - } - - # Determining assemblies - $idtfilename = "MsiAssembly.idt"; # does not need to exist - my $hasmsiassemblies = 0; - my %mergeassemblies = (); - if ( -f $idtfilename ) - { - $filecontent = installer::files::read_file($idtfilename); - $hasmsiassemblies = 1; - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(\S+)\s+/ ) { $mergeassemblies{$1} = 1; } - } - } - - # It is possible, that other tables have to be checked here. This happens, if tables in the - # merge module have to know the "Feature" or the "Directory", under which the content of the - # msm file is integrated into the msi database. - - # Determining name of cabinet file in installation set - my $cabfilename = $mergemodule->{'Cabfilename'}; - installer::packagelist::resolve_packagevariables(\$cabfilename, $allvariables, 0); - - # Analyzing styles - # Flag REMOVE_FILE_TABLE is required for msvc9 Merge-Module, because otherwise msidb.exe - # fails during integration of msm file into msi database. - - my $styles = ""; - my $removefiletable = 0; - if ( $mergemodule->{'Styles'} ) { $styles = $mergemodule->{'Styles'}; } - if ( $styles =~ /\bREMOVE_FILE_TABLE\b/ ) { $removefiletable = 1; } - - if ( $removefiletable ) - { - my $removeworkdir = $workdir . $installer::globals::separator . "remove_file_idt"; - if ( ! -d $removeworkdir ) { installer::systemactions::create_directory($removeworkdir); } - my $completeremovedest = $removeworkdir . $installer::globals::separator . $filename; - installer::systemactions::copy_one_file($completedest, $completeremovedest); - if ( ! -f $completeremovedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completeremovedest !", "merge_mergemodules_into_msi_database"); } - - # Unpacking msm file - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localcompleteremovedest = $completeremovedest; - my $localremoveworkdir = $removeworkdir; - $localcompleteremovedest =~ s/\//\\\\/g; - $localremoveworkdir =~ s/\//\\\\/g; - $systemcall = $msidb . " -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -e \\\*"; - } - else - { - $systemcall = $msidb . " -d " . $completeremovedest . " -f " . $removeworkdir . " -e \*"; - } - - $returnvalue = system($systemcall); - - my $idtfilename = $removeworkdir . $installer::globals::separator . "File.idt"; - if ( -f $idtfilename ) { unlink $idtfilename; } - unlink $completeremovedest; - - # Packing msm file without "File.idt" - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localcompleteremovedest = $completeremovedest; - my $localremoveworkdir = $removeworkdir; - $localcompleteremovedest =~ s/\//\\\\/g; - $localremoveworkdir =~ s/\//\\\\/g; - $systemcall = $msidb . " -c -d " . $localcompleteremovedest . " -f " . $localremoveworkdir . " -i \\\*"; - } - else - { - $systemcall = $msidb . " -c -d " . $completeremovedest . " -f " . $removeworkdir . " -i \*"; - } - $returnvalue = system($systemcall); - - # Using this msm file for merging - if ( -f $completeremovedest ) { $completedest = $completeremovedest; } - else { installer::exiter::exit_program("ERROR: Could not find msm file without File.idt: $completeremovedest !", "merge_mergemodules_into_msi_database"); } - } - - # Saving MergeModule info - - my %onemergemodulehash = (); - $onemergemodulehash{'mergefilepath'} = $completedest; - $onemergemodulehash{'workdir'} = $workdir; - $onemergemodulehash{'cabinetfile'} = $workdir . $installer::globals::separator . $cabinetfile; - $onemergemodulehash{'filenumber'} = $filecounter; - $onemergemodulehash{'componentnames'} = \%componentnames; - $onemergemodulehash{'cabfilename'} = $cabfilename; - $onemergemodulehash{'feature'} = $mergemodule->{'Feature'}; - $onemergemodulehash{'rootdir'} = $mergemodule->{'RootDir'}; - $onemergemodulehash{'name'} = $mergemodule->{'Name'}; - $onemergemodulehash{'mergefilesequence'} = \%mergefilesequence; - $onemergemodulehash{'mergeassemblies'} = \%mergeassemblies; - $onemergemodulehash{'mergedirectories'} = \%mergedirectories; - $onemergemodulehash{'hasmsiassemblies'} = $hasmsiassemblies; - $onemergemodulehash{'removefiletable'} = $removefiletable; - $onemergemodulehash{'fileidtcontent'} = \@file_idt_content; - - $installer::globals::mergemodules{$mergegid} = \%onemergemodulehash; - - # Collecting all cab files, to copy them into installation set - $installer::globals::copy_msm_files{$cabfilename} = $onemergemodulehash{'cabinetfile'}; - - chdir($from); - } - - $infoline = "All Merge Modules successfully analyzed\n"; - push( @installer::globals::logfileinfo, $infoline); - - $installer::globals::mergemodules_analyzed = 1; - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Analyzing MergeModules, stop"); - - $infoline = "\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - # 2. Change msi database (has to be done for every msi database -> for every language) - # a. Merge msm file into msi database: msidb.exe -d <msifile> -m <mergefile> - # b. Extracting tables from msi database: msidb.exe -d <msifile> -f <directory> -e File Media, ... - # c. Changing content of msi database in tables: File, Media, Directory, FeatureComponent - # d. Including tables into msi database: msidb.exe -d <msifile> -f <directory> -i File Media, ... - # e. Copying cabinet file into installation set (later) - - my $counter = 0; - my $mergemodulegid; - foreach $mergemodulegid (keys %installer::globals::mergemodules) - { - my $mergemodulehash = $installer::globals::mergemodules{$mergemodulegid}; - $counter++; - - installer::logger::include_header_into_logfile("Merging Module: $mergemodulehash->{'name'}"); - installer::logger::print_message( "\t... $mergemodulehash->{'name'} ... \n" ); - - $msifilename = installer::converter::make_path_conform($msifilename); - my $workdir = $msifilename; - installer::pathanalyzer::get_path_from_fullqualifiedname(\$workdir); - - # changing directory - my $from = cwd(); - my $to = $workdir; - chdir($to); - - # Saving original msi database - installer::systemactions::copy_one_file($msifilename, "$msifilename\.$counter"); - - # Merging msm file, this is the "real" merge command - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before merging database"); - - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localmergemodulepath = $mergemodulehash->{'mergefilepath'}; - my $localmsifilename = $msifilename; - $localmergemodulepath =~ s/\//\\\\/g; - $localmsifilename =~ s/\//\\\\/g; - $systemcall = $msidb . " -d " . $localmsifilename . " -m " . $localmergemodulepath; - } - else - { - $systemcall = $msidb . " -d " . $msifilename . " -m " . $mergemodulehash->{'mergefilepath'}; - } - $returnvalue = system($systemcall); - - $infoline = "Systemcall: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall . Returnvalue: $returnvalue!\n"; - push( @installer::globals::logfileinfo, $infoline); - installer::exiter::exit_program("ERROR: Could not merge msm file into database: $mergemodulehash->{'mergefilepath'} !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: After merging database"); - - # Saving original idt files - if ( -f "File.idt" ) { installer::systemactions::rename_one_file("File.idt", "old.File.idt.$counter"); } - if ( -f "Media.idt" ) { installer::systemactions::rename_one_file("Media.idt", "old.Media.idt.$counter"); } - if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "old.Directory.idt.$counter"); } - if ( -f "Director.idt" ) { installer::systemactions::rename_one_file("Director.idt", "old.Director.idt.$counter"); } - if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "old.FeatureComponents.idt.$counter"); } - if ( -f "FeatureC.idt" ) { installer::systemactions::rename_one_file("FeatureC.idt", "old.FeatureC.idt.$counter"); } - if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "old.MsiAssembly.idt.$counter"); } - if ( -f "MsiAssem.idt" ) { installer::systemactions::rename_one_file("MsiAssem.idt", "old.MsiAssem.idt.$counter"); } - - # Extracting tables - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before extracting tables"); - - my $workingtables = "File Media Directory FeatureComponents"; # required tables - # Optional tables can be added now - if ( $mergemodulehash->{'hasmsiassemblies'} ) { $workingtables = $workingtables . " MsiAssembly"; } - - # Table "Feature" has to be exported, but it is not necessary to import it. - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localmsifilename = $msifilename; - my $localworkdir = $workdir; - $localmsifilename =~ s/\//\\\\/g; - $localworkdir =~ s/\//\\\\/g; - $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $workingtables; - } - else - { - $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $workingtables; - } - $returnvalue = system($systemcall); - - $infoline = "Systemcall: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall !\n"; - push( @installer::globals::logfileinfo, $infoline); - installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $msifilename !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: After extracting tables"); - - # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables - # creates idt-files, that have long names. - - if ( -f "Directory.idt" ) { installer::systemactions::rename_one_file("Directory.idt", "Director.idt"); } - if ( -f "FeatureComponents.idt" ) { installer::systemactions::rename_one_file("FeatureComponents.idt", "FeatureC.idt"); } - if ( -f "MsiAssembly.idt" ) { installer::systemactions::rename_one_file("MsiAssembly.idt", "MsiAssem.idt"); } - - # Changing content of tables: File, Media, Directory, FeatureComponent, MsiAssembly - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Media table"); - change_media_table($mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids); - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing File table"); - $filesref = change_file_table($mergemodulehash, $workdir, $allupdatesequences, $includepatharrayref, $filesref, $mergemodulegid); - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing FeatureComponent table"); - change_featurecomponent_table($mergemodulehash, $workdir); - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Directory table"); - change_directory_table($mergemodulehash, $workdir); - if ( $mergemodulehash->{'hasmsiassemblies'} ) - { - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing MsiAssembly table"); - change_msiassembly_table($mergemodulehash, $workdir); - } - - # msidb.exe does not merge InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence. Instead it creates - # new tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence that need to be - # merged into the three ExecuteSequences with the following process (also into InstallUISequence.idt). - - # Saving original idt files - if ( -f "InstallE.idt" ) { installer::systemactions::rename_one_file("InstallE.idt", "old.InstallE.idt.$counter"); } - if ( -f "InstallU.idt" ) { installer::systemactions::rename_one_file("InstallU.idt", "old.InstallU.idt.$counter"); } - if ( -f "AdminExe.idt" ) { installer::systemactions::rename_one_file("AdminExe.idt", "old.AdminExe.idt.$counter"); } - if ( -f "AdvtExec.idt" ) { installer::systemactions::rename_one_file("AdvtExec.idt", "old.AdvtExec.idt.$counter"); } - if ( -f "ModuleInstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleInstallExecuteSequence.idt", "old.ModuleInstallExecuteSequence.idt.$counter"); } - if ( -f "ModuleAdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdminExecuteSequence.idt", "old.ModuleAdminExecuteSequence.idt.$counter"); } - if ( -f "ModuleAdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("ModuleAdvtExecuteSequence.idt", "old.ModuleAdvtExecuteSequence.idt.$counter"); } - - # Extracting tables - my $moduleexecutetables = "ModuleInstallExecuteSequence ModuleAdminExecuteSequence ModuleAdvtExecuteSequence"; # new tables - my $executetables = "InstallExecuteSequence InstallUISequence AdminExecuteSequence AdvtExecuteSequence"; # tables to be merged - - - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localmsifilename = $msifilename; - my $localworkdir = $workdir; - $localmsifilename =~ s/\//\\\\/g; - $localworkdir =~ s/\//\\\\/g; - $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $moduleexecutetables; - } - else - { - $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $moduleexecutetables; - } - $returnvalue = system($systemcall); - - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localmsifilename = $msifilename; - my $localworkdir = $workdir; - $localmsifilename =~ s/\//\\\\/g; - $localworkdir =~ s/\//\\\\/g; - $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -e " . "Feature " . $executetables; - } - else - { - $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $executetables; - } - $returnvalue = system($systemcall); - - # Using 8+3 table names, that are used, when tables are integrated into database. The export of tables - # creates idt-files, that have long names. - - if ( -f "InstallExecuteSequence.idt" ) { installer::systemactions::rename_one_file("InstallExecuteSequence.idt", "InstallE.idt"); } - if ( -f "InstallUISequence.idt" ) { installer::systemactions::rename_one_file("InstallUISequence.idt", "InstallU.idt"); } - if ( -f "AdminExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdminExecuteSequence.idt", "AdminExe.idt"); } - if ( -f "AdvtExecuteSequence.idt" ) { installer::systemactions::rename_one_file("AdvtExecuteSequence.idt", "AdvtExec.idt"); } - - # Merging content of tables ModuleInstallExecuteSequence, ModuleAdminExecuteSequence and ModuleAdvtExecuteSequence - # into tables InstallExecuteSequence, AdminExecuteSequence and AdvtExecuteSequence - if ( -f "ModuleInstallExecuteSequence.idt" ) - { - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallExecuteSequence table"); - change_executesequence_table($mergemodulehash, $workdir, "InstallE.idt", "ModuleInstallExecuteSequence.idt"); - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing InstallUISequence table"); - change_executesequence_table($mergemodulehash, $workdir, "InstallU.idt", "ModuleInstallExecuteSequence.idt"); - } - - if ( -f "ModuleAdminExecuteSequence.idt" ) - { - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdminExecuteSequence table"); - change_executesequence_table($mergemodulehash, $workdir, "AdminExe.idt", "ModuleAdminExecuteSequence.idt"); - } - - if ( -f "ModuleAdvtExecuteSequence.idt" ) - { - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing AdvtExecuteSequence table"); - change_executesequence_table($mergemodulehash, $workdir, "AdvtExec.idt", "ModuleAdvtExecuteSequence.idt"); - } - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: All tables edited"); - - # Including tables into msi database - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: Before including tables"); - - if ( $^O =~ /cygwin/i ) { - # msidb.exe really wants backslashes. (And double escaping because system() expands the string.) - my $localmsifilename = $msifilename; - my $localworkdir = $workdir; - $localmsifilename =~ s/\//\\\\/g; - $localworkdir =~ s/\//\\\\/g; - foreach $table (split / /, $workingtables . ' ' . $executetables) { - $systemcall = $msidb . " -d " . $localmsifilename . " -f " . $localworkdir . " -i " . $table; - my $retval = system($systemcall); - $infoline = "Systemcall returned $retval: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - $returnvalue |= $retval; - } - } - else - { - $systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -i " . $workingtables. " " . $executetables; - $returnvalue = system($systemcall); - $infoline = "Systemcall: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - - } - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall !\n"; - push( @installer::globals::logfileinfo, $infoline); - installer::exiter::exit_program("ERROR: Could not include tables into msi database: $msifilename !", "merge_mergemodules_into_msi_database"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: After including tables"); - - chdir($from); - } - - if ( ! $installer::globals::mergefiles_added_into_collector ) { $installer::globals::mergefiles_added_into_collector = 1; } # Now all mergemodules are merged for one language. - - installer::logger::include_timestamp_into_logfile("\nPerformance Info: MergeModule into msi database, stop"); - } - - return $filesref; -} - -######################################################################### -# Analyzing the content of the media table. -######################################################################### - -sub analyze_media_file -{ - my ($filecontent, $workdir) = @_; - - my %filehash = (); - my $linecount = 0; - my $counter = 0; - my $filename = "Media.idt"; - - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.+?)\t(.*?)\s*$/ ) - { - my %line = (); - # Format: DiskId LastSequence DiskPrompt Cabinet VolumeLabel Source - $line{'DiskId'} = $1; - $line{'LastSequence'} = $2; - $line{'DiskPrompt'} = $3; - $line{'Cabinet'} = $4; - $line{'VolumeLabel'} = $5; - $line{'Source'} = $6; - - $counter++; - $filehash{$counter} = \%line; - } - else - { - $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$filename\" in \"$workdir\" (line $linecount) !", "analyze_media_file"); - } - } - - return \%filehash; -} - -######################################################################### -# Setting the DiskID for the new cabinet file -######################################################################### - -sub get_diskid -{ - my ($mediafile, $allupdatediskids, $cabfilename) = @_; - - my $diskid = 0; - my $line; - - if (( $installer::globals::updatedatabase ) && ( exists($allupdatediskids->{$cabfilename}) )) - { - $diskid = $allupdatediskids->{$cabfilename}; - } - else - { - foreach $line ( keys %{$mediafile} ) - { - if ( $mediafile->{$line}->{'DiskId'} > $diskid ) { $diskid = $mediafile->{$line}->{'DiskId'}; } - } - - $diskid++; - } - - return $diskid; -} - -######################################################################### -# Setting the global LastSequence variable -######################################################################### - -sub set_current_last_sequence -{ - my ($mediafile) = @_; - - my $lastsequence = 0; - my $line; - foreach $line ( keys %{$mediafile} ) - { - if ( $mediafile->{$line}->{'LastSequence'} > $lastsequence ) { $lastsequence = $mediafile->{$line}->{'LastSequence'}; } - } - - $installer::globals::lastsequence_before_merge = $lastsequence; -} - -######################################################################### -# Setting the LastSequence for the new cabinet file -######################################################################### - -sub get_lastsequence -{ - my ($mergemodulehash, $allupdatelastsequences) = @_; - - my $lastsequence = 0; - - if (( $installer::globals::updatedatabase ) && ( exists($allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}) )) - { - $lastsequence = $allupdatelastsequences->{$mergemodulehash->{'cabfilename'}}; - } - else - { - $lastsequence = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'}; - } - - return $lastsequence; -} - -######################################################################### -# Setting the DiskPrompt for the new cabinet file -######################################################################### - -sub get_diskprompt -{ - my ($mediafile) = @_; - - my $diskprompt = ""; - my $line; - foreach $line ( keys %{$mediafile} ) - { - if ( exists($mediafile->{$line}->{'DiskPrompt'}) ) - { - $diskprompt = $mediafile->{$line}->{'DiskPrompt'}; - last; - } - } - - return $diskprompt; -} - -######################################################################### -# Setting the VolumeLabel for the new cabinet file -######################################################################### - -sub get_volumelabel -{ - my ($mediafile) = @_; - - my $volumelabel = ""; - my $line; - foreach $line ( keys %{$mediafile} ) - { - if ( exists($mediafile->{$line}->{'VolumeLabel'}) ) - { - $volumelabel = $mediafile->{$line}->{'VolumeLabel'}; - last; - } - } - - return $volumelabel; -} - -######################################################################### -# Setting the Source for the new cabinet file -######################################################################### - -sub get_source -{ - my ($mediafile) = @_; - - my $source = ""; - my $line; - foreach $line ( keys %{$mediafile} ) - { - if ( exists($mediafile->{$line}->{'Source'}) ) - { - $diskprompt = $mediafile->{$line}->{'Source'}; - last; - } - } - - return $source; -} - -######################################################################### -# For each Merge Module one new line has to be included into the -# media table. -######################################################################### - -sub create_new_media_line -{ - my ($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids) = @_; - - my $diskid = get_diskid($mediafile, $allupdatediskids, $mergemodulehash->{'cabfilename'}); - my $lastsequence = get_lastsequence($mergemodulehash, $allupdatelastsequences); - my $diskprompt = get_diskprompt($mediafile); - my $cabinet = $mergemodulehash->{'cabfilename'}; - my $volumelabel = get_volumelabel($mediafile); - my $source = get_source($mediafile); - - if ( $installer::globals::include_cab_in_msi ) { $cabinet = "\#" . $cabinet; } - - my $newline = "$diskid\t$lastsequence\t$diskprompt\t$cabinet\t$volumelabel\t$source\n"; - - return $newline; -} - -######################################################################### -# Setting the last diskid in media table. -######################################################################### - -sub get_last_diskid -{ - my ($mediafile) = @_; - - my $lastdiskid = 0; - my $line; - foreach $line ( keys %{$mediafile} ) - { - if ( $mediafile->{$line}->{'DiskId'} > $lastdiskid ) { $lastdiskid = $mediafile->{$line}->{'DiskId'}; } - } - - return $lastdiskid; -} - -######################################################################### -# Setting global variable for last cab file name. -######################################################################### - -sub set_last_cabfile_name -{ - my ($mediafile, $lastdiskid) = @_; - - my $line; - foreach $line ( keys %{$mediafile} ) - { - if ( $mediafile->{$line}->{'DiskId'} == $lastdiskid ) { $installer::globals::lastcabfilename = $mediafile->{$line}->{'Cabinet'}; } - } - my $infoline = "Setting last cabinet file: $installer::globals::lastcabfilename\n"; - push( @installer::globals::logfileinfo, $infoline); -} - -######################################################################### -# In the media table the new cabinet file has to be added or the -# number of the last cabinet file has to be increased. -######################################################################### - -sub change_media_table -{ - my ( $mergemodulehash, $workdir, $mergemodulegid, $allupdatelastsequences, $allupdatediskids ) = @_; - - my $infoline = "Changing content of table \"Media\"\n"; - push( @installer::globals::logfileinfo, $infoline); - - my $filename = "Media.idt"; - if ( ! -f $filename ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$workdir\" !", "change_media_table"); } - - my $filecontent = installer::files::read_file($filename); - my $mediafile = analyze_media_file($filecontent, $workdir); - set_current_last_sequence($mediafile); - - if ( $installer::globals::fix_number_of_cab_files ) - { - # Determining the line with the highest sequencenumber. That file needs to be updated. - my $lastdiskid = get_last_diskid($mediafile); - if ( $installer::globals::lastcabfilename eq "" ) { set_last_cabfile_name($mediafile, $lastdiskid); } - my $newmaxsequencenumber = $installer::globals::lastsequence_before_merge + $mergemodulehash->{'filenumber'}; - - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(\Q$lastdiskid\E\t)\Q$installer::globals::lastsequence_before_merge\E(\t.*)$/ ) - { - my $start = $1; - my $final = $2; - $infoline = "Merge: Old line in media table: ${$filecontent}[$i]\n"; - push( @installer::globals::logfileinfo, $infoline); - my $newline = $start . $newmaxsequencenumber . $final . "\n"; - ${$filecontent}[$i] = $newline; - $infoline = "Merge: Changed line in media table: ${$filecontent}[$i]\n"; - push( @installer::globals::logfileinfo, $infoline); - } - } - } - else - { - # the new line is identical for all localized databases, but has to be created for each MergeModule ($mergemodulegid) - if ( ! exists($installer::globals::merge_media_line{$mergemodulegid}) ) - { - $installer::globals::merge_media_line{$mergemodulegid} = create_new_media_line($mergemodulehash, $mediafile, $allupdatelastsequences, $allupdatediskids); - } - - $infoline = "Adding line: $installer::globals::merge_media_line{$mergemodulegid}\n"; - push( @installer::globals::logfileinfo, $infoline); - - # adding new line - push(@{$filecontent}, $installer::globals::merge_media_line{$mergemodulegid}); - } - - # saving file - installer::files::save_file($filename, $filecontent); -} - -######################################################################### -# Putting the directory table content into a hash. -######################################################################### - -sub analyze_directorytable_file -{ - my ($filecontent, $idtfilename) = @_; - - my %dirhash = (); - # Iterating over the file content - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) - { - my %line = (); - # Format: Directory Directory_Parent DefaultDir - $line{'Directory'} = $1; - $line{'Directory_Parent'} = $2; - $line{'DefaultDir'} = $3; - $line{'linenumber'} = $i; # saving also the line number for direct access - - my $uniquekey = $line{'Directory'}; - $dirhash{$uniquekey} = \%line; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_directorytable_file"); - } - } - - return \%dirhash; -} - -######################################################################### -# Putting the msi assembly table content into a hash. -######################################################################### - -sub analyze_msiassemblytable_file -{ - my ($filecontent, $idtfilename) = @_; - - my %assemblyhash = (); - # Iterating over the file content - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\s*$/ ) - { - my %line = (); - # Format: Component_ Feature_ File_Manifest File_Application Attributes - $line{'Component'} = $1; - $line{'Feature'} = $2; - $line{'File_Manifest'} = $3; - $line{'File_Application'} = $4; - $line{'Attributes'} = $5; - $line{'linenumber'} = $i; # saving also the line number for direct access - - my $uniquekey = $line{'Component'}; - $assemblyhash{$uniquekey} = \%line; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_msiassemblytable_file"); - } - } - - return \%assemblyhash; -} - -######################################################################### -# Putting the file table content into a hash. -######################################################################### - -sub analyze_filetable_file -{ - my ( $filecontent, $idtfilename ) = @_; - - my %filehash = (); - # Iterating over the file content - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.+?)\s*$/ ) - { - my %line = (); - # Format: File Component_ FileName FileSize Version Language Attributes Sequence - $line{'File'} = $1; - $line{'Component'} = $2; - $line{'FileName'} = $3; - $line{'FileSize'} = $4; - $line{'Version'} = $5; - $line{'Language'} = $6; - $line{'Attributes'} = $7; - $line{'Sequence'} = $8; - $line{'linenumber'} = $i; # saving also the line number for direct access - - my $uniquekey = $line{'File'}; - $filehash{$uniquekey} = \%line; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "analyze_filetable_file"); - } - } - - return \%filehash; -} - -######################################################################### -# Creating a new line for the directory table. -######################################################################### - -sub get_new_line_for_directory_table -{ - my ($dir) = @_; - - my $newline = "$dir->{'Directory'}\t$dir->{'Directory_Parent'}\t$dir->{'DefaultDir'}\n"; - - return $newline; -} - -######################################################################### -# Creating a new line for the file table. -######################################################################### - -sub get_new_line_for_file_table -{ - my ($file) = @_; - - 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"; - - return $newline; -} - -######################################################################### -# Creating a new line for the msiassembly table. -######################################################################### - -sub get_new_line_for_msiassembly_table -{ - my ($assembly) = @_; - - my $newline = "$assembly->{'Component'}\t$assembly->{'Feature'}\t$assembly->{'File_Manifest'}\t$assembly->{'File_Application'}\t$assembly->{'Attributes'}\n"; - - return $newline; -} - -######################################################################### -# Sorting the files collector, if there are files, following -# the merge module files. -######################################################################### - -sub sort_files_collector_for_sequence -{ - my ($filesref) = @_; - - my @sortarray = (); - my %helphash = (); - - for ( my $i = 0; $i <= $#{$filesref}; $i++ ) - { - my $onefile = ${$filesref}[$i]; - if ( ! exists($onefile->{'sequencenumber'}) ) { installer::exiter::exit_program("ERROR: Could not find sequencenumber for file: $onefile->{'uniquename'} !", "sort_files_collector_for_sequence"); } - my $sequence = $onefile->{'sequencenumber'}; - $helphash{$sequence} = $onefile; - } - - foreach my $seq ( sort { $a <=> $b } keys %helphash ) { push(@sortarray, $helphash{$seq}); } - - return \@sortarray; -} - -######################################################################### -# In the file table "Sequence" and "Attributes" have to be changed. -######################################################################### - -sub change_file_table -{ - my ($mergemodulehash, $workdir, $allupdatesequenceshashref, $includepatharrayref, $filesref, $mergemodulegid) = @_; - - my $infoline = "Changing content of table \"File\"\n"; - push( @installer::globals::logfileinfo, $infoline); - - my $idtfilename = "File.idt"; - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_file_table"); } - - my $filecontent = installer::files::read_file($idtfilename); - - # If File.idt needed to be removed before the msm database was merged into the msi database, - # now it is time to add the content into File.idt - if ( $mergemodulehash->{'removefiletable'} ) - { - for ( my $i = 0; $i <= $#{$mergemodulehash->{'fileidtcontent'}}; $i++ ) - { - push(@{$filecontent}, ${$mergemodulehash->{'fileidtcontent'}}[$i]); - } - } - - # Unpacking the MergeModule.CABinet (only once) - # Unpacking into temp directory. Warning: expand.exe has problems with very long unpack directories. - - my $unpackdir = installer::systemactions::create_directories("cab", ""); - push(@installer::globals::removedirs, $unpackdir); - $unpackdir = $unpackdir . $installer::globals::separator . $mergemodulegid; - - my %newfileshash = (); - if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector )) - { - if ( ! -d $unpackdir ) { installer::systemactions::create_directory($unpackdir); } - - # changing directory - my $from = cwd(); - my $to = $mergemodulehash->{'workdir'}; - if ( $^O =~ /cygwin/i ) { - $to = qx(cygpath -u "$to"); - chomp $to; - } - - chdir($to) || die "Could not chdir to \"$to\"\n"; - - # Unpack the cab file, so that in can be included into the last office cabinet file. - # Not using cabarc.exe from cabsdk for unpacking cabinet files, but "expand.exe" that - # should be available on every Windows system. - - $infoline = "Unpacking cabinet file: $mergemodulehash->{'cabinetfile'}\n"; - push( @installer::globals::logfileinfo, $infoline); - - # Avoid the Cygwin expand command - my $expandfile = "expand.exe"; # Has to be in the path - if ( $^O =~ /cygwin/i ) { - $expandfile = qx(cygpath -u "$ENV{WINDIR}"/System32/expand.exe); - chomp $expandfile; - } - - my $cabfilename = "MergeModule.CABinet"; - - my $systemcall = ""; - if ( $^O =~ /cygwin/i ) { - my $localunpackdir = qx(cygpath -m "$unpackdir"); - chomp $localunpackdir; - $systemcall = $expandfile . " " . $cabfilename . " -F:\\\* " . $localunpackdir; - } - else - { - $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " 2\>\&1"; - } - - my $returnvalue = system($systemcall); - - $infoline = "Systemcall: $systemcall\n"; - push( @installer::globals::logfileinfo, $infoline); - - if ($returnvalue) - { - $infoline = "ERROR: Could not execute $systemcall !\n"; - push( @installer::globals::logfileinfo, $infoline); - installer::exiter::exit_program("ERROR: Could not extract cabinet file: $mergemodulehash->{'cabinetfile'} !", "change_file_table"); - } - else - { - $infoline = "Success: Executed $systemcall successfully!\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - chdir($from); - } - - # For performance reasons creating a hash with file names and rows - # The content of File.idt is changed after every merge -> content cannot be saved in global hash - $merge_filetablehashref = analyze_filetable_file($filecontent, $idtfilename); - - my $attributes = "16384"; # Always - - my $filename; - foreach $filename (keys %{$mergemodulehash->{'mergefilesequence'}} ) - { - my $mergefilesequence = $mergemodulehash->{'mergefilesequence'}->{$filename}; - - if ( ! exists($merge_filetablehashref->{$filename}) ) { installer::exiter::exit_program("ERROR: Could not find file \"$filename\" in \"$idtfilename\" !", "change_file_table"); } - my $filehash = $merge_filetablehashref->{$filename}; - my $linenumber = $filehash->{'linenumber'}; - - # <- this line has to be changed concerning "Sequence" and "Attributes" - $filehash->{'Attributes'} = $attributes; - - # If this is an update process, the sequence numbers have to be reused. - if ( $installer::globals::updatedatabase ) - { - if ( ! exists($allupdatesequenceshashref->{$filehash->{'File'}}) ) { installer::exiter::exit_program("ERROR: Sequence not defined for file \"$filehash->{'File'}\" !", "change_file_table"); } - $filehash->{'Sequence'} = $allupdatesequenceshashref->{$filehash->{'File'}}; - # Saving all mergemodule sequence numbers. This is important for creating ddf files - $installer::globals::allmergemodulefilesequences{$filehash->{'Sequence'}} = 1; - } - else - { - # Important saved data: $installer::globals::lastsequence_before_merge. - # This mechanism keeps the correct order inside the new cabinet file. - $filehash->{'Sequence'} = $filehash->{'Sequence'} + $installer::globals::lastsequence_before_merge; - } - - my $oldline = ${$filecontent}[$linenumber]; - my $newline = get_new_line_for_file_table($filehash); - ${$filecontent}[$linenumber] = $newline; - - $infoline = "Merge, replacing line:\n"; - push( @installer::globals::logfileinfo, $infoline); - $infoline = "Old: $oldline\n"; - push( @installer::globals::logfileinfo, $infoline); - $infoline = "New: $newline\n"; - push( @installer::globals::logfileinfo, $infoline); - - # Adding files to the files collector (but only once) - if (( $installer::globals::fix_number_of_cab_files ) && ( ! $installer::globals::mergefiles_added_into_collector )) - { - # If the number of cabinet files is kept constant, - # all files from the mergemodule cabinet files will - # be integrated into the last office cabinet file - # (installer::globals::lastcabfilename). - # Therefore the files must now be added to the filescollector, - # so that they will be integrated into the ddf files. - - # Problem with very long filenames -> copying to shorter filenames - my $newfilename = "f" . $filehash->{'Sequence'}; - my $completesource = $unpackdir . $installer::globals::separator . $filehash->{'File'}; - my $completedest = $unpackdir . $installer::globals::separator . $newfilename; - installer::systemactions::copy_one_file($completesource, $completedest); - - my $locallastcabfilename = $installer::globals::lastcabfilename; - if ( $locallastcabfilename =~ /^\s*\#/ ) { $locallastcabfilename =~ s/^\s*\#//; } # removing beginning hashes - - # Create new file hash for file collector - my %newfile = (); - $newfile{'sequencenumber'} = $filehash->{'Sequence'}; - $newfile{'assignedsequencenumber'} = $filehash->{'Sequence'}; - $newfile{'cabinet'} = $locallastcabfilename; - $newfile{'sourcepath'} = $completedest; - $newfile{'componentname'} = $filehash->{'Component'}; - $newfile{'uniquename'} = $filehash->{'File'}; - $newfile{'Name'} = $filehash->{'File'}; - - # Saving in globals sequence hash - $installer::globals::uniquefilenamesequence{$filehash->{'File'}} = $filehash->{'Sequence'}; - - if ( ! -f $newfile{'sourcepath'} ) { installer::exiter::exit_program("ERROR: File \"$newfile{'sourcepath'}\" must exist!", "change_file_table"); } - - # Collecting all new files. Attention: This files must be included into files collector in correct order! - $newfileshash{$filehash->{'Sequence'}} = \%newfile; - # push(@{$filesref}, \%newfile); -> this is not the correct order - } - } - - # Now the files can be added to the files collector - # In the case of an update process, there can be new files, that have to be added after the merge module files. - # Warning: In multilingual installation sets, the files only have to be added once to the files collector! - - if ( ! $installer::globals::mergefiles_added_into_collector ) - { - foreach my $localsequence ( sort { $a <=> $b } keys %newfileshash ) { push(@{$filesref}, $newfileshash{$localsequence}); } - if ( $installer::globals::newfilesexist ) { $filesref = sort_files_collector_for_sequence($filesref); } - # $installer::globals::mergefiles_added_into_collector = 1; -> Not yet. Only if all mergemodules are merged for one language. - } - - # Saving the idt file (for every language) - installer::files::save_file($idtfilename, $filecontent); - - return $filesref; -} - -######################################################################### -# Reading the file "Director.idt". The Directory, that is defined in scp -# has to be defined in this table. -######################################################################### - -sub collect_directories -{ - my $idtfilename = "Director.idt"; - my $filecontent = installer::files::read_file($idtfilename); - - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - # Format: Directory Directory_Parent DefaultDir - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) - { - $installer::globals::merge_alldirectory_hash{$1} = 1; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_directories"); - } - } -} - -######################################################################### -# Reading the file "Feature.idt". The Feature, that is defined in scp -# has to be defined in this table. -######################################################################### - -sub collect_feature -{ - my $idtfilename = "Feature.idt"; - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "collect_feature"); } - my $filecontent = installer::files::read_file($idtfilename); - - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - # Format: Feature Feature_Parent Title Description Display Level Directory_ Attributes - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) - { - $installer::globals::merge_allfeature_hash{$1} = 1; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "collect_feature"); - } - } -} - -######################################################################### -# In the featurecomponent table, the new connections have to be added. -######################################################################### - -sub change_featurecomponent_table -{ - my ($mergemodulehash, $workdir) = @_; - - my $infoline = "Changing content of table \"FeatureComponents\"\n"; - push( @installer::globals::logfileinfo, $infoline); - - my $idtfilename = "FeatureC.idt"; - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_featurecomponent_table"); } - - my $filecontent = installer::files::read_file($idtfilename); - - # Simply adding for each new component one line. The Feature has to be defined in scp project. - my $feature = $mergemodulehash->{'feature'}; - - if ( ! $installer::globals::mergefeaturecollected ) - { - collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash - $installer::globals::mergefeaturecollected = 1; - } - - if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) ) - { - installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_featurecomponent_table"); - } - - my $component; - foreach $component ( keys %{$mergemodulehash->{'componentnames'}} ) - { - my $line = "$feature\t$component\n"; - push(@{$filecontent}, $line); - $infoline = "Adding line: $line\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - # saving file - installer::files::save_file($idtfilename, $filecontent); -} - -######################################################################### -# In the directory table, the directory parent has to be changed, -# if it is not TARGETDIR. -######################################################################### - -sub change_directory_table -{ - my ($mergemodulehash, $workdir) = @_; - - # directory for MergeModule has to be defined in scp project - my $scpdirectory = $mergemodulehash->{'rootdir'}; - - if ( $scpdirectory ne "TARGETDIR" ) # TARGETDIR works fine, when using msidb.exe - { - my $infoline = "Changing content of table \"Directory\"\n"; - push( @installer::globals::logfileinfo, $infoline); - - my $idtfilename = "Director.idt"; - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_directory_table"); } - - my $filecontent = installer::files::read_file($idtfilename); - - if ( ! $installer::globals::mergedirectoriescollected ) - { - collect_directories(); # putting content into %installer::globals::merge_alldirectory_hash, only first column! - $installer::globals::mergedirectoriescollected = 1; - } - - if ( ! exists($installer::globals::merge_alldirectory_hash{$scpdirectory}) ) - { - installer::exiter::exit_program("ERROR: Unknown directory defined in scp: \"$scpdirectory\" . Not defined in table \"Directory\" !", "change_directory_table"); - } - - # If the definition in scp is okay, now the complete content of "Director.idt" can be analyzed - my $merge_directorytablehashref = analyze_directorytable_file($filecontent, $idtfilename); - - my $directory; - foreach $directory (keys %{$mergemodulehash->{'mergedirectories'}} ) - { - if ( ! exists($merge_directorytablehashref->{$directory}) ) { installer::exiter::exit_program("ERROR: Could not find directory \"$directory\" in \"$idtfilename\" !", "change_directory_table"); } - my $dirhash = $merge_directorytablehashref->{$directory}; - my $linenumber = $dirhash->{'linenumber'}; - - # <- this line has to be changed concerning "Directory_Parent", - # if the current value is "TARGETDIR", which is the default value from msidb.exe - - if ( $dirhash->{'Directory_Parent'} eq "TARGETDIR" ) - { - $dirhash->{'Directory_Parent'} = $scpdirectory; - - my $oldline = ${$filecontent}[$linenumber]; - my $newline = get_new_line_for_directory_table($dirhash); - ${$filecontent}[$linenumber] = $newline; - - $infoline = "Merge, replacing line:\n"; - push( @installer::globals::logfileinfo, $infoline); - $infoline = "Old: $oldline\n"; - push( @installer::globals::logfileinfo, $infoline); - $infoline = "New: $newline\n"; - push( @installer::globals::logfileinfo, $infoline); - } - } - - # saving file - installer::files::save_file($idtfilename, $filecontent); - } -} - -######################################################################### -# In the msiassembly table, the feature has to be changed. -######################################################################### - -sub change_msiassembly_table -{ - my ($mergemodulehash, $workdir) = @_; - - my $infoline = "Changing content of table \"MsiAssembly\"\n"; - push( @installer::globals::logfileinfo, $infoline); - - my $idtfilename = "MsiAssem.idt"; - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_msiassembly_table"); } - - my $filecontent = installer::files::read_file($idtfilename); - - # feature has to be defined in scp project - my $feature = $mergemodulehash->{'feature'}; - - if ( ! $installer::globals::mergefeaturecollected ) - { - collect_feature(); # putting content into hash %installer::globals::merge_allfeature_hash - $installer::globals::mergefeaturecollected = 1; - } - - if ( ! exists($installer::globals::merge_allfeature_hash{$feature}) ) - { - installer::exiter::exit_program("ERROR: Unknown feature defined in scp: \"$feature\" . Not defined in table \"Feature\" !", "change_msiassembly_table"); - } - - my $merge_msiassemblytablehashref = analyze_msiassemblytable_file($filecontent, $idtfilename); - - my $component; - foreach $component (keys %{$mergemodulehash->{'mergeassemblies'}} ) - { - if ( ! exists($merge_msiassemblytablehashref->{$component}) ) { installer::exiter::exit_program("ERROR: Could not find component \"$component\" in \"$idtfilename\" !", "change_msiassembly_table"); } - my $assemblyhash = $merge_msiassemblytablehashref->{$component}; - my $linenumber = $assemblyhash->{'linenumber'}; - - # <- this line has to be changed concerning "Feature" - $assemblyhash->{'Feature'} = $feature; - - my $oldline = ${$filecontent}[$linenumber]; - my $newline = get_new_line_for_msiassembly_table($assemblyhash); - ${$filecontent}[$linenumber] = $newline; - - $infoline = "Merge, replacing line:\n"; - push( @installer::globals::logfileinfo, $infoline); - $infoline = "Old: $oldline\n"; - push( @installer::globals::logfileinfo, $infoline); - $infoline = "New: $newline\n"; - push( @installer::globals::logfileinfo, $infoline); - } - - # saving file - installer::files::save_file($idtfilename, $filecontent); -} - -######################################################################### -# Creating file content hash -######################################################################### - -sub make_executeidtcontent_hash -{ - my ($filecontent, $idtfilename) = @_; - - my %newhash = (); - - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - # Format for all sequence tables: Action Condition Sequence - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\s*$/ ) - { - my %onehash = (); - $onehash{'Action'} = $1; - $onehash{'Condition'} = $2; - $onehash{'Sequence'} = $3; - $newhash{$onehash{'Action'}} = \%onehash; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash"); - } - } - - return \%newhash; -} - -######################################################################### -# Creating file content hash -######################################################################### - -sub make_moduleexecuteidtcontent_hash -{ - my ($filecontent, $idtfilename) = @_; - - my %newhash = (); - - for ( my $i = 0; $i <= $#{$filecontent}; $i++ ) - { - if ( $i <= 2 ) { next; } # ignoring first three lines - if ( ${$filecontent}[$i] =~ /^\s*$/ ) { next; } # ignoring empty lines - # Format for all module sequence tables: Action Sequence BaseAction After Condition - if ( ${$filecontent}[$i] =~ /^\s*(.+?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ ) - { - my %onehash = (); - $onehash{'Action'} = $1; - $onehash{'Sequence'} = $2; - $onehash{'BaseAction'} = $3; - $onehash{'After'} = $4; - $onehash{'Condition'} = $5; - $newhash{$onehash{'Action'}} = \%onehash; - } - else - { - my $linecount = $i + 1; - installer::exiter::exit_program("ERROR: Unknown line format in table \"$idtfilename\" (line $linecount) !", "make_executeidtcontent_hash"); - } - } - - return \%newhash; -} - -######################################################################### -# ExecuteSequence tables need to be merged with -# ModuleExecuteSequence tables created by msidb.exe. -######################################################################### - -sub change_executesequence_table -{ - my ($mergemodulehash, $workdir, $idtfilename, $moduleidtfilename) = @_; - - my $infoline = "Changing content of table \"$idtfilename\"\n"; - push( @installer::globals::logfileinfo, $infoline); - - if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_executesequence_table"); } - if ( ! -f $moduleidtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$moduleidtfilename\" in \"$workdir\" !", "change_executesequence_table"); } - - # Reading file content - my $idtfilecontent = installer::files::read_file($idtfilename); - my $moduleidtfilecontent = installer::files::read_file($moduleidtfilename); - - # Converting to hash - my $idtcontenthash = make_executeidtcontent_hash($idtfilecontent, $idtfilename); - my $moduleidtcontenthash = make_moduleexecuteidtcontent_hash($moduleidtfilecontent, $moduleidtfilename); - - # Merging - foreach my $action ( keys %{$moduleidtcontenthash} ) - { - if ( exists($idtcontenthash->{$action}) ) { next; } # Action already exists, can be ignored - - if (( $idtfilename eq "InstallU.idt" ) && ( ! ( $action =~ /^\s*WindowsFolder\./ ))) { next; } # Only "WindowsFolder.*" CustomActions for UI Sequence table - - my $actionhashref = $moduleidtcontenthash->{$action}; - if ( $actionhashref->{'Sequence'} ne "" ) - { - # Format for all sequence tables: Action Condition Sequence - my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $actionhashref->{'Sequence'} . "\n"; - # Adding to table - push(@{$idtfilecontent}, $newline); - # Also adding to hash - my %idttablehash = (); - $idttablehash{'Action'} = $actionhashref->{'Action'}; - $idttablehash{'Condition'} = $actionhashref->{'Condition'}; - $idttablehash{'Sequence'} = $actionhashref->{'Sequence'}; - $idtcontenthash->{$action} = \%idttablehash; - - } - else # no sequence defined, using syntax "BaseAction" and "After" - { - my $baseactionname = $actionhashref->{'BaseAction'}; - # If this baseactionname is not defined in execute idt file, it is not possible to merge - if ( ! exists($idtcontenthash->{$baseactionname}) ) { installer::exiter::exit_program("ERROR: Merge problem: Could not find action \"$baseactionname\" in file \"$idtfilename\" !", "change_executesequence_table"); } - - my $baseaction = $idtcontenthash->{$baseactionname}; - my $sequencenumber = $baseaction->{'Sequence'}; - if ( $actionhashref->{'After'} == 1 ) { $sequencenumber = $sequencenumber + 1; } - else { $sequencenumber = $sequencenumber - 1; } - - # Format for all sequence tables: Action Condition Sequence - my $newline = $actionhashref->{'Action'} . "\t" . $actionhashref->{'Condition'} . "\t" . $sequencenumber . "\n"; - # Adding to table - push(@{$idtfilecontent}, $newline); - # Also adding to hash - my %idttablehash = (); - $idttablehash{'Action'} = $actionhashref->{'Action'}; - $idttablehash{'Condition'} = $actionhashref->{'Condition'}; - $idttablehash{'Sequence'} = $sequencenumber; - $idtcontenthash->{$action} = \%idttablehash; - } - } - - # saving file - installer::files::save_file($idtfilename, $idtfilecontent); -} - - -1; |