2 # This file is part of the LibreOffice project.
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 # This file incorporates work covered by the following license notice:
10 # Licensed to the Apache Software Foundation (ASF) under one or more
11 # contributor license agreements. See the NOTICE file distributed
12 # with this work for additional information regarding copyright
13 # ownership. The ASF licenses this file to you under the Apache
14 # License, Version 2.0 (the "License"); you may not use this file
15 # except in compliance with the License. You may obtain a copy of
16 # the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #################################################################################
24 #################################################################################
28 $prog = "msi installer";
32 $globaltempdirname = "ooopackaging";
34 $msiinfo_available = 0;
40 if ( $plat =~ /cygwin/i )
43 $pathseparator = "\:";
48 $pathseparator = "\;";
52 #################################################################################
54 #################################################################################
59 ----------------------------------------------------------------------
60 This program installs a Windows Installer installation set
61 without using msiexec.exe. The installation is comparable
62 with an administrative installation using the Windows Installer
65 -d Path to installation set or msi database
67 ---------------------------------------------------------------------
72 #################################################################################
73 # Collecting parameter
74 #################################################################################
78 if (( $#ARGV < 3 ) || ( $#ARGV > 3 )) { usage
(); }
82 my $param = shift(@ARGV);
84 if ($param eq "-t") { $targetdir = shift(@ARGV); }
85 elsif ($param eq "-d") { $databasepath = shift(@ARGV); }
88 print "\n**********************************************\n";
89 print "Error: Unknows parameter: $param";
90 print "\n**********************************************\n";
97 #################################################################################
98 # Checking content of parameter
99 #################################################################################
103 if ( $targetdir eq "" )
105 print "\n******************************************************\n";
106 print "Error: Target directory not defined (parameter -t)!";
107 print "\n******************************************************\n";
112 if ( $databasepath eq "" )
114 print "\n******************************************************\n";
115 print "Error: Path to msi database not defined (parameter -d)!";
116 print "\n******************************************************\n";
121 if ( -d
$databasepath )
123 $databasepath =~ s/\\\s*$//;
124 $databasepath =~ s/\/\s*$//;
126 my $msifiles = find_file_with_file_extension
("msi", $databasepath);
128 if ( $#{$msifiles} < 0 ) { exit_program("ERROR: Did not find msi database in directory $installationdir"); }
129 if ( $#{$msifiles} > 0 ) { exit_program("ERROR: Did find more than one msi database in directory $installationdir"); }
131 $databasepath = $databasepath . $separator . ${$msifiles}[0];
134 if ( ! -f
$databasepath ) { exit_program
("ERROR: Did not find msi database in directory $databasepath."); }
136 if ( ! -d
$targetdir ) { create_directories
($targetdir); }
139 #############################################################################
140 # The program msidb.exe can be located next to the Perl program. Then it is
141 # not neccessary to find it in the PATH variable.
142 #############################################################################
144 sub check_local_msidb
146 my $msidbname = "msidb.exe";
147 my $perlprogramm = $0;
148 my $path = $perlprogramm;
150 get_path_from_fullqualifiedname
(\
$path);
156 if ( $path =~ /^\s*$/ ) { $msidbpath = $msidbname; }
157 else { $msidbpath = $path . $separator . $msidbname; }
161 $localmsidbpath = $msidbpath;
162 print "Using $msidbpath (next to \"admin.pl\")\n";
166 #############################################################################
167 # Converting a string list with separator $listseparator
169 #############################################################################
171 sub convert_stringlist_into_array
173 my ( $includestringref, $listseparator ) = @_;
177 my $last = ${$includestringref};
179 while ( $last =~ /^\s*(.+?)\Q$listseparator\E(.+)\s*$/) # "$" for minimal matching
183 # Problem with two directly following listseparators. For example a path with two ";;" directly behind each other
184 $first =~ s/^$listseparator//;
185 push(@newarray, "$first\n");
188 push(@newarray, "$last\n");
193 #########################################################
194 # Checking the local system
195 # Checking existence of needed files in include path
196 #########################################################
198 sub check_system_path
202 my $pathvariable = $ENV{'PATH'};
203 my $local_pathseparator = $pathseparator;
205 if( $^O
=~ /cygwin/i )
206 { # When using cygwin's perl the PATH variable is POSIX style and ...
207 $pathvariable = qx{cygpath
-mp
"$pathvariable"} ;
208 # has to be converted to DOS style for further use.
209 $local_pathseparator = ';';
211 my $patharrayref = convert_stringlist_into_array
(\
$pathvariable, $local_pathseparator);
213 my @needed_files_in_path = ("expand.exe");
214 if ( $localmsidbpath eq "" ) { push(@needed_files_in_path, "msidb.exe"); } # not found locally -> search in path
215 my @optional_files_in_path = ("msiinfo.exe");
217 print("\nChecking required files:\n");
219 foreach $onefile ( @needed_files_in_path )
221 print("...... searching $onefile ...");
223 my $fileref = get_sourcepath_from_filename_and_includepath
(\
$onefile, $patharrayref);
225 if ( $$fileref eq "" )
228 print( "$onefile not found\n" );
232 print( "\tFound: $$fileref\n" );
236 if ( $error ) { exit_program
("ERROR: Could not find all needed files in path (using setsolar should help)!"); }
238 print("\nChecking optional files:\n");
240 foreach $onefile ( @optional_files_in_path )
242 print("...... searching $onefile ...");
244 my $fileref = get_sourcepath_from_filename_and_includepath
(\
$onefile, $patharrayref);
246 if ( $$fileref eq "" )
248 print( "$onefile not found\n" );
249 if ( $onefile eq "msiinfo.exe" ) { $msiinfo_available = 0; }
253 print( "\tFound: $$fileref\n" );
254 if ( $onefile eq "msiinfo.exe" ) { $msiinfo_available = 1; }
260 ##########################################################################
261 # Searching a file in a list of paths
262 ##########################################################################
264 sub get_sourcepath_from_filename_and_includepath
266 my ($searchfilenameref, $includepatharrayref) = @_;
269 my $foundsourcefile = 0;
271 for ( my $j = 0; $j <= $#{$includepatharrayref}; $j++ )
273 my $includepath = ${$includepatharrayref}[$j];
274 $includepath =~ s/^\s*//;
275 $includepath =~ s/\s*$//;
277 $onefile = $includepath . $separator . $$searchfilenameref;
281 $foundsourcefile = 1;
286 if (!($foundsourcefile)) { $onefile = ""; }
291 ########################################################
292 # Finding all files with a specified file extension
293 # in a specified directory.
294 ########################################################
296 sub find_file_with_file_extension
298 my ($extension, $dir) = @_;
301 my @sourcefiles = ();
303 $dir =~ s/\Q$separator\E\s*$//;
306 @sourcefiles = readdir(DIR
);
311 foreach $onefile (@sourcefiles)
313 if ((!($onefile eq ".")) && (!($onefile eq "..")))
315 if ( $onefile =~ /^\s*(\S.*?)\.$extension\s*$/ )
317 push(@allfiles, $onefile)
325 ##############################################################
326 # Creating a directory with all parent directories
327 ##############################################################
329 sub create_directories
331 my ($directory) = @_;
333 if ( ! try_to_create_directory
($directory) )
335 my $parentdir = $directory;
336 get_path_from_fullqualifiedname
(\
$parentdir);
337 create_directories
($parentdir); # recursive
340 create_directory
($directory); # now it has to succeed
343 ##############################################################
344 # Creating one directory
345 ##############################################################
349 my ($directory) = @_;
351 if ( ! -d
$directory ) { mkdir($directory, 0775); }
354 ##############################################################
355 # Trying to create a directory, no error if this fails
356 ##############################################################
358 sub try_to_create_directory
360 my ($directory) = @_;
363 my $created_directory = 0;
365 if (!(-d
$directory))
367 $returnvalue = mkdir($directory, 0775);
371 $created_directory = 1;
373 my $localcall = "chmod 775 $directory \>\/dev\/null 2\>\&1";
378 $created_directory = 0;
383 $created_directory = 1;
386 return $created_directory;
389 ###########################################
390 # Getting path from full file name
391 ###########################################
393 sub get_path_from_fullqualifiedname
395 my ($longfilenameref) = @_;
397 if ( $$longfilenameref =~ /\Q$separator\E/ ) # Is there a separator in the path? Otherwise the path is empty.
399 if ( $$longfilenameref =~ /^\s*(\S.*\Q$separator\E)(\S.+\S?)/ )
401 $$longfilenameref = $1;
406 $$longfilenameref = ""; # there is no path
410 ##############################################################
411 # Getting file name from full file name
412 ##############################################################
414 sub make_absolute_filename_to_relative_filename
416 my ($longfilenameref) = @_;
419 if ( $$longfilenameref =~ /^.*[\/\\](\S
.+\S?
)/ )
421 $$longfilenameref = $1;
425 ############################################
426 # Exiting the program with an error
427 # This function is used instead of "die"
428 ############################################
434 print "\n***************************************************************\n";
436 print "***************************************************************\n";
437 remove_complete_directory
($savetemppath, 1);
438 print "\n" . get_time_string
();
442 #################################################################################
443 # Unpacking cabinet files with expand
444 #################################################################################
446 sub unpack_cabinet_file
448 my ($cabfilename, $unpackdir) = @_;
450 my $expandfile = "expand.exe"; # has to be in the PATH
452 # expand.exe has to be located in the system directory.
453 # Cygwin has another tool expand.exe, that converts tabs to spaces. This cannot be used of course.
454 # But this wrong expand.exe is typically in the PATH before this expand.exe, to unpack
457 if ( $^O
=~ /cygwin/i )
459 $expandfile = $ENV{'SYSTEMROOT'} . "/system32/expand.exe"; # Has to be located in the systemdirectory
460 $expandfile =~ s/\\/\//;
461 if ( ! -f
$expandfile ) { exit_program
("ERROR: Did not find file $expandfile in the Windows system folder!"); }
464 my $expandlogfile = $unpackdir . $separator . "expand.log";
466 # exclude cabinet file
467 # my $systemcall = $cabarc . " -o X " . $mergemodulehash->{'cabinetfile'};
470 if ( $^O
=~ /cygwin/i ) {
471 my $localunpackdir = qx{cygpath
-w
"$unpackdir"};
472 $localunpackdir =~ s/\\/\\\\/g;
474 my $localcabfilename = qx{cygpath
-w
"$cabfilename"};
475 $localcabfilename =~ s/\\/\\\\/g;
476 $localcabfilename =~ s/\s*$//g;
478 $systemcall = $expandfile . " " . $localcabfilename . " -F:\* " . $localunpackdir . " \>\/dev\/null 2\>\&1";
482 $systemcall = $expandfile . " " . $cabfilename . " -F:\* " . $unpackdir . " \> " . $expandlogfile;
485 my $returnvalue = system($systemcall);
487 if ($returnvalue) { exit_program
("ERROR: Could not execute $systemcall !"); }
490 #################################################################################
491 # Extracting tables from msi database
492 #################################################################################
494 sub extract_tables_from_database
496 my ($fullmsidatabasepath, $workdir, $tablelist) = @_;
498 my $msidb = "msidb.exe"; # Has to be in the path
499 if ( $localmsidbpath ) { $msidb = $localmsidbpath; }
502 my $returnvalue = "";
504 if ( $^O
=~ /cygwin/i ) {
505 chomp( $fullmsidatabasepath = qx{cygpath
-w
"$fullmsidatabasepath"} );
506 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
507 $fullmsidatabasepath =~ s/\\/\\\\/g;
508 $workdir =~ s/\\/\\\\/g;
509 # and if there are still slashes, they also need to be double backslash
510 $fullmsidatabasepath =~ s/\//\\\\/g
;
511 $workdir =~ s/\//\\\\/g
;
514 # Export of all tables by using "*"
516 $systemcall = $msidb . " -d " . $fullmsidatabasepath . " -f " . $workdir . " -e $tablelist";
517 print "\nAnalyzing msi database\n";
518 $returnvalue = system($systemcall);
522 $infoline = "ERROR: Could not execute $systemcall !\n";
523 exit_program
($infoline);
527 ########################################################
528 # Check, if this installation set contains
529 # internal cabinet files included into the msi
531 ########################################################
533 sub check_for_internal_cabfiles
535 my ($cabfilehash) = @_;
537 my $contains_internal_cabfiles = 0;
538 my %allcabfileshash = ();
540 foreach my $filename ( keys %{$cabfilehash} )
542 if ( $filename =~ /^\s*\#/ ) # starting with a hash
544 $contains_internal_cabfiles = 1;
545 # setting real filename without hash as key and name with hash as value
546 my $realfilename = $filename;
547 $realfilename =~ s/^\s*\#//;
548 $allcabfileshash{$realfilename} = $filename;
552 return ( $contains_internal_cabfiles, \
%allcabfileshash );
555 #################################################################
556 # Exclude all cab files from the msi database.
557 #################################################################
559 sub extract_cabs_from_database
561 my ($msidatabase, $allcabfiles) = @_;
565 my $msidb = "msidb.exe"; # Has to be in the path
566 if ( $localmsidbpath ) { $msidb = $localmsidbpath; }
568 my @all_excluded_cabfiles = ();
570 if( $^O
=~ /cygwin/i )
572 $msidatabase = qx{cygpath
-w
"$msidatabase"};
573 $msidatabase =~ s/\\/\\\\/g;
574 $msidatabase =~ s/\s*$//g;
578 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
579 $msidatabase =~ s/\//\\\\/g
;
582 foreach my $onefile ( keys %{$allcabfiles} )
584 my $systemcall = $msidb . " -d " . $msidatabase . " -x " . $onefile;
586 push(@all_excluded_cabfiles, $onefile);
589 \
@all_excluded_cabfiles;
592 ################################################################################
593 # Collect all DiskIds to the corresponding cabinet files from Media.idt.
594 ################################################################################
596 sub analyze_media_file
598 my ($filecontent) = @_;
602 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
604 if ( $i < 3 ) { next; }
606 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
611 $diskidhash{$cabfile} = $diskid;
618 ################################################################################
619 # Analyzing the content of Directory.idt
620 #################################################################################
622 sub analyze_directory_file
624 my ($filecontent) = @_;
628 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
630 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
632 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\s*$/ )
638 if ( $name =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/ ) { $name = $2; }
639 if ( $name =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $name = $2; }
642 $helphash{'Directory_Parent'} = $parent;
643 $helphash{'DefaultDir'} = $name;
644 $table{$dir} = \
%helphash;
651 #################################################################################
652 # Analyzing the content of Component.idt
653 #################################################################################
655 sub analyze_component_file
657 my ($filecontent) = @_;
661 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
663 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
665 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
670 $table{$component} = $dir;
677 #################################################################################
678 # Analyzing the content of File.idt
679 #################################################################################
681 sub analyze_file_file
683 my ($filecontent) = @_;
689 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
691 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
693 if ( ${$filecontent}[$i] =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\s*$/ )
700 if ( $filename =~ /^\s*(.*?)\s*\|\s*(.*?)\s*$/ ) { $filename = $2; }
703 $helphash{'Component'} = $comp;
704 $helphash{'FileName'} = $filename;
705 $helphash{'Sequence'} = $sequence;
707 $table{$file} = \
%helphash;
709 $fileorder{$sequence} = $file;
711 if ( $sequence > $maxsequence ) { $maxsequence = $sequence; }
715 return (\
%table, \
%fileorder, $maxsequence);
718 ####################################################################################
719 # Recursively creating the directory tree
720 ####################################################################################
722 sub create_directory_tree
724 my ($parent, $pathcollector, $fulldir, $dirhash) = @_;
726 foreach my $dir ( keys %{$dirhash} )
728 if (( $dirhash->{$dir}->{'Directory_Parent'} eq $parent ) && ( $dirhash->{$dir}->{'DefaultDir'} ne "." ))
730 my $dirname = $dirhash->{$dir}->{'DefaultDir'};
731 # Create the directory
732 my $newdir = $fulldir . $separator . $dirname;
733 if ( ! -f
$newdir ) { mkdir $newdir; }
734 # Saving in collector
735 $pathcollector->{$dir} = $newdir;
737 create_directory_tree
($dir, $pathcollector, $newdir, $dirhash);
742 ####################################################################################
743 # Creating the directory tree
744 ####################################################################################
746 sub create_directory_structure
748 my ($dirhash, $targetdir) = @_;
750 print "Creating directories\n";
752 my %fullpathhash = ();
754 my @startparents = ("TARGETDIR", "INSTALLLOCATION");
756 foreach $dir (@startparents) { create_directory_tree
($dir, \
%fullpathhash, $targetdir, $dirhash); }
758 # Also adding the paths of the startparents
759 foreach $dir (@startparents)
761 if ( ! exists($fullpathhash{$dir}) ) { $fullpathhash{$dir} = $targetdir; }
764 return \
%fullpathhash;
767 ####################################################################################
768 # Cygwin: Setting privileges for files
769 ####################################################################################
771 sub change_privileges
773 my ($destfile, $privileges) = @_;
775 my $localcall = "chmod $privileges " . "\"" . $destfile . "\"";
779 ####################################################################################
780 # Cygwin: Setting privileges for files recursively
781 ####################################################################################
783 sub change_privileges_full
787 print "Changing privileges\n";
789 my $localcall = "chmod -R 755 " . "\"" . $target . "\"";
793 ######################################################
794 # Creating a new directory with defined privileges
795 ######################################################
797 sub create_directory_with_privileges
799 my ($directory, $privileges) = @_;
804 if (!(-d
$directory))
806 my $localprivileges = oct("0".$privileges); # changes "777" to 0777
807 $returnvalue = mkdir($directory, $localprivileges);
811 my $localcall = "chmod $privileges $directory \>\/dev\/null 2\>\&1";
817 my $localcall = "chmod $privileges $directory \>\/dev\/null 2\>\&1";
822 ######################################################
823 # Creating a unique directory with pid extension
824 ######################################################
826 sub create_pid_directory
828 my ($directory) = @_;
830 $directory =~ s/\Q$separator\E\s*$//;
831 my $pid = $$; # process id
832 my $time = time(); # time
834 $directory = $directory . "_" . $pid . $time;
836 if ( ! -d
$directory ) { create_directory
($directory); }
837 else { exit_program
("ERROR: Directory $directory already exists!"); }
842 ####################################################################################
843 # Copying files into installation set
844 ####################################################################################
846 sub copy_files_into_directory_structure
848 my ($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash) = @_;
850 print "Copying files\n";
852 for ( my $i = 1; $i <= $maxsequence; $i++ )
854 if ( exists($fileorder->{$i}) )
856 my $file = $fileorder->{$i};
857 if ( ! exists($filehash->{$file}->{'Component'}) ) { exit_program
("ERROR: Did not find component for file: \"$file\"."); }
858 my $component = $filehash->{$file}->{'Component'};
859 if ( ! exists($componenthash->{$component}) ) { exit_program
("ERROR: Did not find directory for component: \"$component\"."); }
860 my $dirname = $componenthash->{$component};
861 if ( ! exists($fullpathhash->{$dirname}) ) { exit_program
("ERROR: Did not find full directory path for dir: \"$dirname\"."); }
862 my $destdir = $fullpathhash->{$dirname};
863 if ( ! exists($filehash->{$file}->{'FileName'}) ) { exit_program
("ERROR: Did not find \"FileName\" for file: \"$file\"."); }
864 my $destfile = $filehash->{$file}->{'FileName'};
866 $destfile = $destdir . $separator . $destfile;
867 my $sourcefile = $unpackdir . $separator . $file;
869 if ( ! -f
$sourcefile )
871 # It is possible, that this was an unpacked file
872 # Looking in the dirhash, to find the subdirectory in the installation set (the id is $dirname)
873 # subdir is not recursively analyzed, only one directory.
875 my $oldsourcefile = $sourcefile;
877 if ( exists($dirhash->{$dirname}->{'DefaultDir'}) ) { $subdir = $dirhash->{$dirname}->{'DefaultDir'} . $separator; }
878 my $realfilename = $filehash->{$file}->{'FileName'};
879 my $localinstalldir = $installdir;
881 $localinstalldir =~ s/\\\s*$//;
882 $localinstalldir =~ s/\/\s*$//;
884 $sourcefile = $localinstalldir . $separator . $subdir . $realfilename;
886 if ( ! -f
$sourcefile ) { exit_program
("ERROR: File not found: \"$oldsourcefile\" (or \"$sourcefile\")."); }
889 my $copyreturn = copy
($sourcefile, $destfile);
891 if ( ! $copyreturn) { exit_program
("ERROR: Could not copy $source to $dest\n"); }
893 # if (( $^O =~ /cygwin/i ) && ( $destfile =~ /\.exe\s*$/ )) { change_privileges($destfile, "775"); }
895 # else # allowing missing sequence numbers ?
897 # exit_program("ERROR: No file assigned to sequence $i");
902 ######################################################
903 # Removing a complete directory with subdirectories
904 ######################################################
906 sub remove_complete_directory
908 my ($directory, $start) = @_;
913 $directory =~ s/\Q$separator\E\s*$//;
917 if ( $start ) { print "Removing directory $directory\n"; }
919 opendir(DIR
, $directory);
920 @content = readdir(DIR
);
925 foreach $oneitem (@content)
927 if ((!($oneitem eq ".")) && (!($oneitem eq "..")))
929 my $item = $directory . $separator . $oneitem;
931 if ( -f
$item || -l
$item ) # deleting files or links
936 if ( -d
$item ) # recursive
938 remove_complete_directory
($item, 0);
943 # try to remove empty directory
944 my $returnvalue = rmdir $directory;
945 if ( ! $returnvalue ) { print "Warning: Problem with removing empty dir $directory\n"; }
949 ####################################################################################
950 # Defining a temporary path
951 ####################################################################################
957 if (( $ENV{'TMP'} ) || ( $ENV{'TEMP'} ))
959 if ( $ENV{'TMP'} ) { $temppath = $ENV{'TMP'}; }
960 elsif ( $ENV{'TEMP'} ) { $temppath = $ENV{'TEMP'}; }
962 $temppath =~ s/\Q$separator\E\s*$//; # removing ending slashes and backslashes
963 $temppath = $temppath . $separator . $globaltempdirname;
964 create_directory_with_privileges
($temppath, "777");
966 my $dirsave = $temppath;
968 $temppath = $temppath . $separator . "a";
969 $temppath = create_pid_directory
($temppath);
971 if ( ! -d
$temppath ) { exit_program
("ERROR: Failed to create directory $temppath ! Possible reason: Wrong privileges in directory $dirsave."); }
973 if ( $^O
=~ /cygwin/i )
975 $temppath =~ s/\\/\\\\/g;
976 chomp( $temppath = qx{cygpath
-w
"$temppath"} );
979 $savetemppath = $temppath;
983 exit_program
("ERROR: Could not set temporary directory (TMP and TEMP not set!).");
989 ####################################################################################
991 ####################################################################################
995 my ($localfile) = @_;
999 open( IN
, "<$localfile" ) || exit_program
("ERROR: Cannot open file $localfile for reading");
1001 # Don't use "my @localfile = <IN>" here, because
1002 # perl has a problem with the internal "large_and_huge_malloc" function
1003 # when calling perl using MacOS 10.5 with a perl built with MacOS 10.4
1004 while ( $line = <IN
> ) {
1005 push @localfile, $line;
1013 ###############################################################
1014 # Setting the time string for the
1015 # Summary Information stream in the
1016 # msi database of the admin installations.
1017 ###############################################################
1019 sub get_sis_time_string
1021 # Syntax: <yyyy/mm/dd hh:mm:ss>
1022 my $second = (localtime())[0];
1023 my $minute = (localtime())[1];
1024 my $hour = (localtime())[2];
1025 my $day = (localtime())[3];
1026 my $month = (localtime())[4];
1027 my $year = 1900 + (localtime())[5];
1030 if ( $second < 10 ) { $second = "0" . $second; }
1031 if ( $minute < 10 ) { $minute = "0" . $minute; }
1032 if ( $hour < 10 ) { $hour = "0" . $hour; }
1033 if ( $day < 10 ) { $day = "0" . $day; }
1034 if ( $month < 10 ) { $month = "0" . $month; }
1036 my $timestring = $year . "/" . $month . "/" . $day . " " . $hour . ":" . $minute . ":" . $second;
1041 ###############################################################
1042 # Writing content of administrative installations into
1043 # Summary Information Stream of msi database.
1044 # This is required for example for following
1045 # patch processes using Windows Installer service.
1046 ###############################################################
1050 my ($msidatabase) = @_;
1052 print "Setting SIS in msi database\n";
1054 if ( ! -f
$msidatabase ) { exit_program
("ERROR: Cannot find file $msidatabase"); }
1056 my $msiinfo = "msiinfo.exe"; # Has to be in the path
1058 my $systemcall = "";
1059 my $returnvalue = "";
1061 # Required setting for administrative installations:
1062 # -w 4 (source files are unpacked), wordcount
1063 # -s <date of admin installation>, LastPrinted, Syntax: <yyyy/mm/dd hh:mm:ss>
1064 # -l <person_making_admin_installation>, LastSavedBy
1066 my $wordcount = 4; # Unpacked files
1067 my $lastprinted = get_sis_time_string
();
1068 my $lastsavedby = "Installer";
1070 my $localmsidatabase = $msidatabase;
1072 if( $^O
=~ /cygwin/i )
1074 $localmsidatabase = qx{cygpath
-w
"$localmsidatabase"};
1075 $localmsidatabase =~ s/\\/\\\\/g;
1076 $localmsidatabase =~ s/\s*$//g;
1079 $systemcall = $msiinfo . " " . "\"" . $localmsidatabase . "\"" . " -w " . $wordcount . " -s " . "\"" . $lastprinted . "\"" . " -l $lastsavedby";
1081 $returnvalue = system($systemcall);
1085 $infoline = "ERROR: Could not execute $systemcall !\n";
1086 exit_program
($infoline);
1090 ###############################################################
1091 # Convert time string
1092 ###############################################################
1094 sub convert_timestring
1096 my ($secondstring) = @_;
1098 my $timestring = "";
1100 if ( $secondstring < 60 ) # less than a minute
1102 if ( $secondstring < 10 ) { $secondstring = "0" . $secondstring; }
1103 $timestring = "00\:$secondstring min\.";
1105 elsif ( $secondstring < 3600 )
1107 my $minutes = $secondstring / 60;
1108 my $seconds = $secondstring % 60;
1109 if ( $minutes =~ /(\d*)\.\d*/ ) { $minutes = $1; }
1110 if ( $minutes < 10 ) { $minutes = "0" . $minutes; }
1111 if ( $seconds < 10 ) { $seconds = "0" . $seconds; }
1112 $timestring = "$minutes\:$seconds min\.";
1114 else # more than one hour
1116 my $hours = $secondstring / 3600;
1117 my $secondstring = $secondstring % 3600;
1118 my $minutes = $secondstring / 60;
1119 my $seconds = $secondstring % 60;
1120 if ( $hours =~ /(\d*)\.\d*/ ) { $hours = $1; }
1121 if ( $minutes =~ /(\d*)\.\d*/ ) { $minutes = $1; }
1122 if ( $hours < 10 ) { $hours = "0" . $hours; }
1123 if ( $minutes < 10 ) { $minutes = "0" . $minutes; }
1124 if ( $seconds < 10 ) { $seconds = "0" . $seconds; }
1125 $timestring = "$hours\:$minutes\:$seconds hours";
1131 ###############################################################
1132 # Returning time string for logging
1133 ###############################################################
1137 my $currenttime = time();
1138 $currenttime = $currenttime - $starttime;
1139 $currenttime = convert_timestring
($currenttime);
1140 $currenttime = localtime() . " \(" . $currenttime . "\)\n";
1141 return $currenttime;
1144 ####################################################################################
1145 # Simulating an administrative installation
1146 ####################################################################################
1148 $starttime = time();
1152 check_local_msidb
();
1153 check_system_path
();
1154 my $temppath = get_temppath
();
1156 print("\nmsi database: $databasepath\n");
1157 print("Destination directory: $targetdir\n" );
1159 my $helperdir = $temppath . $separator . "installhelper";
1160 create_directory
($helperdir);
1162 # Get File.idt, Component.idt and Directory.idt from database
1164 my $tablelist = "File Directory Component Media CustomAction";
1165 extract_tables_from_database
($databasepath, $helperdir, $tablelist);
1168 my $unpackdir = $helperdir . $separator . "unpack";
1169 create_directory
($unpackdir);
1171 # Reading media table to check for internal cabinet files
1172 my $filename = $helperdir . $separator . "Media.idt";
1173 if ( ! -f
$filename ) { exit_program
("ERROR: Could not find required file: $filename !"); }
1174 my $filecontent = read_file
($filename);
1175 my $cabfilehash = analyze_media_file
($filecontent);
1177 # Check, if there are internal cab files
1178 my ( $contains_internal_cabfiles, $all_internal_cab_files) = check_for_internal_cabfiles
($cabfilehash);
1180 if ( $contains_internal_cabfiles )
1183 my $cabdir = $helperdir . $separator . "internal_cabs";
1184 create_directory
($cabdir);
1187 # Exclude all cabinet files from database
1188 my $all_excluded_cabs = extract_cabs_from_database
($databasepath, $all_internal_cab_files);
1189 print "Unpacking files from internal cabinet file(s)\n";
1190 foreach my $cabfile ( @
{$all_excluded_cabs} ) { unpack_cabinet_file
($cabfile, $unpackdir); }
1194 # Unpack all cab files into $helperdir, cab files must be located next to msi database
1195 my $installdir = $databasepath;
1197 get_path_from_fullqualifiedname
(\
$installdir);
1199 my $databasefilename = $databasepath;
1200 make_absolute_filename_to_relative_filename
(\
$databasefilename);
1202 my $cabfiles = find_file_with_file_extension
("cab", $installdir);
1204 if (( $#{$cabfiles} < 0 ) && ( ! $contains_internal_cabfiles )) { exit_program("ERROR: Did not find any cab file in directory $installdir"); }
1206 print "Unpacking files from cabinet file(s)\n";
1207 for ( my $i = 0; $i <= $#{$cabfiles}; $i++ )
1209 my $cabfile = $installdir . $separator . ${$cabfiles}[$i];
1210 unpack_cabinet_file
($cabfile, $unpackdir);
1214 $filename = $helperdir . $separator . "Directory.idt";
1215 $filecontent = read_file
($filename);
1216 my $dirhash = analyze_directory_file
($filecontent);
1218 $filename = $helperdir . $separator . "Component.idt";
1219 $filecontent = read_file
($filename);
1220 my $componenthash = analyze_component_file
($filecontent);
1222 $filename = $helperdir . $separator . "File.idt";
1223 $filecontent = read_file
($filename);
1224 my ( $filehash, $fileorder, $maxsequence ) = analyze_file_file
($filecontent);
1226 # Creating the directory structure
1227 my $fullpathhash = create_directory_structure
($dirhash, $targetdir);
1230 copy_files_into_directory_structure
($fileorder, $filehash, $componenthash, $fullpathhash, $maxsequence, $unpackdir, $installdir, $dirhash);
1231 if ( $^O
=~ /cygwin/i ) { change_privileges_full
($targetdir); }
1233 my $msidatabase = $targetdir . $separator . $databasefilename;
1234 my $copyreturn = copy
($databasepath, $msidatabase);
1235 if ( ! $copyreturn) { exit_program
("ERROR: Could not copy $source to $dest\n"); }
1237 # Saving info in Summary Information Stream of msi database (required for following patches)
1238 if ( $msiinfo_available ) { write_sis_info
($msidatabase); }
1240 # Removing the helper directory
1241 remove_complete_directory
($temppath, 1);
1243 print "\nSuccessful installation: " . get_time_string
();