1666 lines
75 KiB
Perl
1666 lines
75 KiB
Perl
#
|
|
# This file is part of the LibreOffice project.
|
|
#
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
#
|
|
# This file incorporates work covered by the following license notice:
|
|
#
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed
|
|
# with this work for additional information regarding copyright
|
|
# ownership. The ASF licenses this file to you under the Apache
|
|
# License, Version 2.0 (the "License"); you may not use this file
|
|
# except in compliance with the License. You may obtain a copy of
|
|
# the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
#
|
|
|
|
package installer::windows::mergemodule;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
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, $allvariables, $includepatharrayref, $allupdatesequences, $allupdatelastsequences, $allupdatediskids) = @_;
|
|
|
|
my $domerge = 0;
|
|
if (( $#{$mergemodules} > -1 ) && ( ! $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 $systemcall_output = "";
|
|
my $returnvalue = "";
|
|
# in cygwin the * glob needs to be escaped when passing it to msidb
|
|
my $globescape = "";
|
|
$globescape = "\\" if ( $^O =~ /cygwin/i );
|
|
|
|
# 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 = $ENV{'MSM_PATH'} . $filename;
|
|
|
|
if ( ! -f $mergefile ) { installer::exiter::exit_program("ERROR: msm file not found: $filename ($mergefile)!", "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); }
|
|
|
|
# export cabinet file
|
|
$systemcall = $msidb . " -d " . $filename . " -x " . $cabinetfile;
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue)
|
|
{
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\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);
|
|
}
|
|
|
|
# export 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).
|
|
|
|
$systemcall = $msidb . " -d " . $filename . " -f " . $workdir . " -e $globescape*";
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue)
|
|
{
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
installer::exiter::exit_program("ERROR: Could not export 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'};
|
|
if ( $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
|
|
$systemcall = $msidb . " -d " . $completeremovedest . " -f " . $removeworkdir . " -e $globescape*";
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue) {
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
installer::exiter::exit_program("ERROR: $systemcall failed!", "merge_mergemodules_into_msi_database");
|
|
} else {
|
|
$infoline = "Success: Executed $systemcall successfully!\n";
|
|
push(@installer::globals::logfileinfo, $infoline);
|
|
}
|
|
|
|
my $idtfilename = $removeworkdir . $installer::globals::separator . "File.idt";
|
|
if ( -f $idtfilename ) { unlink $idtfilename; }
|
|
unlink $completeremovedest;
|
|
|
|
# Packing msm file without "File.idt"
|
|
$systemcall = $msidb . " -c -d " . $completeremovedest . " -f " . $removeworkdir . " -i $globescape*";
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue) {
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
installer::exiter::exit_program("ERROR: $systemcall failed!", "merge_mergemodules_into_msi_database");
|
|
} else {
|
|
$infoline = "Success: Executed $systemcall successfully!\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
}
|
|
|
|
# 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{'componentcondition'} = $mergemodule->{'ComponentCondition'};
|
|
$onemergemodulehash{'attributes_add'} = $mergemodule->{'Attributes_Add'};
|
|
$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
|
|
if ( $cabfilename ) { $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");
|
|
|
|
$systemcall = $msidb . " -d " . $msifilename . " -m " . $mergemodulehash->{'mergefilepath'};
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue)
|
|
{
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
installer::exiter::exit_program("Could not merge msm file into database: $mergemodulehash->{'mergefilepath'}\n$infoline", "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"); }
|
|
if ( -f "Componen.idt" ) { installer::systemactions::rename_one_file("Componen.idt", "old.Componen.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"; }
|
|
if ( ( $mergemodulehash->{'componentcondition'} ) || ( $mergemodulehash->{'attributes_add'} ) ) { $workingtables = $workingtables . " Component"; }
|
|
|
|
# Table "Feature" has to be exported, but it is not necessary to import it.
|
|
$systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $workingtables;
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue)
|
|
{
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\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"); }
|
|
if ( -f "Component.idt" ) { installer::systemactions::rename_one_file("Component.idt", "Componen.idt"); }
|
|
|
|
# Changing content of tables: File, Media, Directory, FeatureComponent, MsiAssembly, Component
|
|
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);
|
|
}
|
|
|
|
if ( ( $mergemodulehash->{'componentcondition'} ) || ( $mergemodulehash->{'attributes_add'} ) )
|
|
{
|
|
installer::logger::include_timestamp_into_logfile("\nPerformance Info: Changing Component table");
|
|
change_component_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
|
|
|
|
$systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $moduleexecutetables;
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue) {
|
|
# the exit status of this command had not been checked in the past, it fails because
|
|
# there is no ModuleAdminExecuteSequence table
|
|
$infoline = "IGNORING: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
#installer::exiter::exit_program("ERROR: $infoline", "merge_mergemodules_into_msi_database");
|
|
} else {
|
|
$infoline = "Success: Executed $systemcall successfully\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
}
|
|
|
|
$systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -e " . "Feature " . $executetables;
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue) {
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
installer::exiter::exit_program("$infoline", "merge_mergemodules_into_msi_database");
|
|
} else {
|
|
$infoline = "Success: Executed $systemcall successfully\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
}
|
|
|
|
# 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");
|
|
|
|
# trying to import all tables at once seems to work fine, but creates a broken installer tdf#165149
|
|
foreach my $table (split / /, $workingtables . ' ' . $executetables) {
|
|
$systemcall = $msidb . " -d " . $msifilename . " -f " . $workdir . " -i " . $table;
|
|
# msidb.exe really wants backslashes
|
|
$systemcall =~ s/\//\\\\/g;
|
|
|
|
$systemcall_output = `$systemcall`;
|
|
$returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue)
|
|
{
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\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'}) )
|
|
{
|
|
$source = $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 $globescape = "";
|
|
$globescape = "\\" if ( $^O =~ /cygwin/i );
|
|
|
|
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 $empty = "";
|
|
my $unpackdir = installer::systemactions::create_directories("cab", \$empty);
|
|
$unpackdir = qx(cygpath -m "$unpackdir");
|
|
chomp $unpackdir;
|
|
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 = qx(cygpath -m "$ENV{WINDIR}"/System32/expand.exe);
|
|
chomp $expandfile;
|
|
|
|
my $cabfilename = "MergeModule.CABinet";
|
|
|
|
my $systemcall = $expandfile . " " . $cabfilename . " -F:$globescape* " . $unpackdir . " 2\>\&1";
|
|
|
|
my $systemcall_output = `$systemcall`;
|
|
my $returnvalue = $? >> 8;
|
|
|
|
if ($returnvalue)
|
|
{
|
|
$infoline = "ERROR: Could not execute $systemcall - returncode: $returnvalue - output: $systemcall_output\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
installer::exiter::exit_program("ERROR: extracting $cabfilename failed using $systemcall", "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
|
|
my $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 ($workdir) = @_;
|
|
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($workdir); # 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 components table, the conditions or attributes of merge modules should be updated
|
|
###############################################################################
|
|
|
|
sub change_component_table
|
|
{
|
|
my ($mergemodulehash, $workdir) = @_;
|
|
|
|
my $infoline = "Changing content of table \"Component\"\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
|
|
my $idtfilename = "Componen.idt";
|
|
if ( ! -f $idtfilename ) { installer::exiter::exit_program("ERROR: Could not find file \"$idtfilename\" in \"$workdir\" !", "change_component_table"); }
|
|
|
|
my $filecontent = installer::files::read_file($idtfilename);
|
|
|
|
for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
|
|
{
|
|
my $component;
|
|
foreach $component ( keys %{$mergemodulehash->{'componentnames'}} )
|
|
{
|
|
if ( my ( $comp_, $compid_, $dir_, $attr_, $cond_, $keyp_ ) = ${$filecontent}[$i] =~ /^\s*($component)\t(.*?)\t(.+?)\t(.+?)\t(.*?)\t(.*?)\s*$/)
|
|
{
|
|
my $newattr_ = ( $attr_ =~ /^\s*0x/ ) ? hex($attr_) : $attr_;
|
|
if ( $mergemodulehash->{'attributes_add'} )
|
|
{
|
|
$infoline = "Adding attribute(s) ($mergemodulehash->{'attributes_add'}) from scp2 to component $comp_\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
if ( $mergemodulehash->{'attributes_add'} =~ /^\s*0x/ )
|
|
{
|
|
$newattr_ = $newattr_ | hex($mergemodulehash->{'attributes_add'});
|
|
}
|
|
else
|
|
{
|
|
$newattr_ = $newattr_ | $mergemodulehash->{'attributes_add'};
|
|
}
|
|
$infoline = "Old attribute(s): $attr_\nNew attribute(s): $newattr_\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
}
|
|
my $newcond_ = $cond_;
|
|
if ( $mergemodulehash->{'componentcondition'} )
|
|
{
|
|
$infoline = "Adding condition ($mergemodulehash->{'componentcondition'}) from scp2 to component $comp_\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
if ($cond_)
|
|
{
|
|
$newcond_ = "($cond_) AND ($mergemodulehash->{'componentcondition'})";
|
|
}
|
|
else
|
|
{
|
|
$newcond_ = "$mergemodulehash->{'componentcondition'}";
|
|
}
|
|
$infoline = "Old condition: $cond_\nNew condition: $newcond_\n";
|
|
push( @installer::globals::logfileinfo, $infoline);
|
|
}
|
|
${$filecontent}[$i] = "$comp_\t$compid_\t$dir_\t$newattr_\t$newcond_\t$keyp_\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
# 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($workdir); # 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;
|