cid#1607171 Data race condition
[LibreOffice.git] / solenv / bin / modules / installer / windows / msiglobal.pm
blob51e91af12be327050bc45718bf32a72d5131f02c
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 .
19 package installer::windows::msiglobal;
21 use strict;
22 use warnings;
24 use Cwd;
25 use Digest::MD5;
26 use installer::converter;
27 use installer::exiter;
28 use installer::files;
29 use installer::globals;
30 use installer::logger;
31 use installer::pathanalyzer;
32 use installer::remover;
33 use installer::scriptitems;
34 use installer::systemactions;
35 use installer::worker;
36 use installer::windows::idtglobal;
37 use installer::windows::language;
39 ###########################################################################
40 # Generating the header of the ddf file.
41 # The usage of ddf files is needed, because makecab.exe can only include
42 # one sourcefile into a cab file
43 ###########################################################################
45 sub write_ddf_file_header
47 my ($ddffileref, $cabinetfile, $installdir) = @_;
49 my $oneline;
51 $oneline = ".Set CabinetName1=" . $cabinetfile . "\n";
52 push(@{$ddffileref} ,$oneline);
53 $oneline = ".Set ReservePerCabinetSize=128\n"; # This reserves space for a digital signature.
54 push(@{$ddffileref} ,$oneline);
55 $oneline = ".Set MaxDiskSize=2147483648\n"; # This allows the .cab file to get a size of 2 GB.
56 push(@{$ddffileref} ,$oneline);
57 $oneline = ".Set CompressionType=LZX\n";
58 push(@{$ddffileref} ,$oneline);
59 $oneline = ".Set Compress=ON\n";
60 push(@{$ddffileref} ,$oneline);
61 # The window size for LZX compression
62 # CompressionMemory=15 | 16 | ... | 21
63 # Reference: http://msdn.microsoft.com/en-us/library/bb417343.aspx
64 $oneline = ".Set CompressionMemory=$installer::globals::cabfilecompressionlevel\n";
65 push(@{$ddffileref} ,$oneline);
66 $oneline = ".Set Cabinet=ON\n";
67 push(@{$ddffileref} ,$oneline);
68 $oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n";
69 push(@{$ddffileref} ,$oneline);
72 ##########################################################################
73 # Lines in ddf files must not contain more than 256 characters
74 ##########################################################################
76 sub check_ddf_file
78 my ( $ddffile, $ddffilename ) = @_;
80 my $maxlength = 0;
81 my $maxline = 0;
82 my $linelength = 0;
83 my $linenumber = 0;
85 for ( my $i = 0; $i <= $#{$ddffile}; $i++ )
87 my $oneline = ${$ddffile}[$i];
89 $linelength = length($oneline);
90 $linenumber = $i + 1;
92 if ( $linelength > 256 )
94 installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file");
97 if ( $linelength > $maxlength )
99 $maxlength = $linelength;
100 $maxline = $linenumber;
104 my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n";
105 push(@installer::globals::logfileinfo, $infoline);
108 ##########################################################################
109 # Lines in ddf files must not be longer than 256 characters.
110 # Therefore it can be useful to use relative paths. Then it is
111 # necessary to change into temp directory before calling
112 # makecab.exe.
113 ##########################################################################
115 sub make_relative_ddf_path
117 my ( $sourcepath ) = @_;
119 my $windowstemppath = $installer::globals::temppath;
121 if ( $^O =~ /cygwin/i )
123 $windowstemppath = $installer::globals::cyg_temppath;
126 $sourcepath =~ s/\Q$windowstemppath\E//;
127 $sourcepath =~ s/^[\\\/]//;
129 return $sourcepath;
132 ##########################################################################
133 # Returning the order of the sequences in the files array.
134 ##########################################################################
136 sub get_sequenceorder
138 my ($filesref) = @_;
140 my %order = ();
142 for ( my $i = 0; $i <= $#{$filesref}; $i++ )
144 my $onefile = ${$filesref}[$i];
145 if ( ! $onefile->{'assignedsequencenumber'} ) { installer::exiter::exit_program("ERROR: No sequence number assigned to $onefile->{'gid'} ($onefile->{'uniquename'})!", "get_sequenceorder"); }
146 $order{$onefile->{'assignedsequencenumber'}} = $i;
149 return \%order;
152 ##########################################################################
153 # Generation the list, in which the source of the files is connected
154 # with the cabinet destination file. Because more than one file needs
155 # to be included into a cab file, this has to be done via ddf files.
156 ##########################################################################
158 sub generate_cab_file_list
160 my ($filesref, $installdir, $ddfdir, $allvariables) = @_;
162 my @cabfilelist = ();
164 installer::logger::include_header_into_logfile("Generating ddf files");
166 installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation start");
168 if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_paths($filesref); }
170 if (( $installer::globals::fix_number_of_cab_files ) && ( $installer::globals::updatedatabase ))
172 my $sequenceorder = get_sequenceorder($filesref);
174 my $counter = 1;
176 while ( ( exists($sequenceorder->{$counter}) ) || ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) ) # Taking care of files from merge modules
178 # if ( exists($installer::globals::allmergemodulefilesequences{$counter}) )
180 # # Skipping this sequence, it is not included in $filesref, because it is assigned to a file from a merge module.\n";
181 # $counter++;
182 # next;
185 my $onefile = ${$filesref}[$sequenceorder->{$counter}];
186 $counter++;
188 my $cabinetfile = $onefile->{'cabinet'};
189 my $sourcepath = $onefile->{'sourcepath'};
190 if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
191 my $uniquename = $onefile->{'uniquename'};
193 my $styles = "";
194 my $doinclude = 1;
195 if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
196 if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
198 # to avoid lines with more than 256 characters, it can be useful to use relative paths
199 $sourcepath = make_relative_ddf_path($sourcepath);
201 my @ddffile = ();
203 write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
205 my $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n";
206 if ( $doinclude ) { push(@ddffile, $ddfline); }
208 my $nextfile = "";
209 if ( ${$filesref}[$sequenceorder->{$counter}] ) { $nextfile = ${$filesref}[$sequenceorder->{$counter}]; }
211 my $nextcabinetfile = "";
213 if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
215 while ( $nextcabinetfile eq $cabinetfile )
217 $sourcepath = $nextfile->{'sourcepath'};
218 if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
219 # to avoid lines with more than 256 characters, it can be useful to use relative paths
220 $sourcepath = make_relative_ddf_path($sourcepath);
221 $uniquename = $nextfile->{'uniquename'};
222 my $localdoinclude = 1;
223 my $nextfilestyles = "";
224 if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
225 if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
226 $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n";
227 if ( $localdoinclude ) { push(@ddffile, $ddfline); }
228 $counter++;
229 $nextfile = "";
230 $nextcabinetfile = "_lastfile_";
231 if (( exists($sequenceorder->{$counter}) ) && ( ${$filesref}[$sequenceorder->{$counter}] ))
233 $nextfile = ${$filesref}[$sequenceorder->{$counter}];
234 $nextcabinetfile = $nextfile->{'cabinet'};
238 # creating the DDF file
240 my $ddffilename = $cabinetfile;
241 $ddffilename =~ s/.cab/.ddf/;
242 $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
243 $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
245 installer::files::save_file($ddffilename ,\@ddffile);
246 my $infoline = "Created ddf file: $ddffilename\n";
247 push(@installer::globals::logfileinfo, $infoline);
249 # lines in ddf files must not be longer than 256 characters
250 check_ddf_file(\@ddffile, $ddffilename);
252 # Writing the makecab system call
254 my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
256 push(@cabfilelist, $oneline);
258 # collecting all ddf files
259 push(@installer::globals::allddffiles, $ddffilename);
262 elsif ( $installer::globals::fix_number_of_cab_files )
264 for ( my $i = 0; $i <= $#{$filesref}; $i++ )
266 my $onefile = ${$filesref}[$i];
267 my $cabinetfile = $onefile->{'cabinet'};
268 my $sourcepath = $onefile->{'sourcepath'};
269 if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
270 my $uniquename = $onefile->{'uniquename'};
272 my $styles = "";
273 my $doinclude = 1;
274 if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
275 if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
278 # to avoid lines with more than 256 characters, it can be useful to use relative paths
279 $sourcepath = make_relative_ddf_path($sourcepath);
281 # all files with the same cabinetfile are directly behind each other in the files collector
283 my @ddffile = ();
285 write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
287 my $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n";
288 if ( $doinclude ) { push(@ddffile, $ddfline); }
290 my $nextfile = ${$filesref}[$i+1];
291 my $nextcabinetfile = "";
293 if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
295 while ( $nextcabinetfile eq $cabinetfile )
297 $sourcepath = $nextfile->{'sourcepath'};
298 if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
299 # to avoid lines with more than 256 characters, it can be useful to use relative paths
300 $sourcepath = make_relative_ddf_path($sourcepath);
301 $uniquename = $nextfile->{'uniquename'};
302 my $localdoinclude = 1;
303 my $nextfilestyles = "";
304 if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
305 if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
306 $ddfline = "\"" . $sourcepath . "\" \"" . $uniquename . "\"\n";
307 if ( $localdoinclude ) { push(@ddffile, $ddfline); }
308 $i++; # increasing the counter!
309 $nextfile = ${$filesref}[$i+1];
310 if ( $nextfile ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
311 else { $nextcabinetfile = "_lastfile_"; }
314 # creating the DDF file
316 my $ddffilename = $cabinetfile;
317 $ddffilename =~ s/.cab/.ddf/;
318 $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
319 $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
321 installer::files::save_file($ddffilename ,\@ddffile);
322 my $infoline = "Created ddf file: $ddffilename\n";
323 push(@installer::globals::logfileinfo, $infoline);
325 # lines in ddf files must not be longer than 256 characters
326 check_ddf_file(\@ddffile, $ddffilename);
328 # Writing the makecab system call
330 my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
332 push(@cabfilelist, $oneline);
334 # collecting all ddf files
335 push(@installer::globals::allddffiles, $ddffilename);
338 else
340 installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "generate_cab_file_list");
343 installer::logger::include_timestamp_into_logfile("Performance Info: ddf file generation end");
345 return \@cabfilelist; # contains all system calls for packaging process
348 ########################################################################
349 # For update and patch reasons the pack order needs to be saved.
350 # The pack order is saved in the ddf files; the names and locations
351 # of the ddf files are saved in @installer::globals::allddffiles.
352 # The outputfile "packorder.txt" can be saved in
353 # $installer::globals::infodirectory .
354 ########################################################################
356 sub save_packorder
358 installer::logger::include_header_into_logfile("Saving pack order");
360 installer::logger::include_timestamp_into_logfile("Performance Info: saving pack order start");
362 my $packorderfilename = "packorder.txt";
363 $packorderfilename = $installer::globals::infodirectory . $installer::globals::separator . $packorderfilename;
365 my @packorder = ();
367 my $headerline = "\# Syntax\: Filetable_Sequence Cabinetfilename Physical_FileName Unique_FileName\n\n";
368 push(@packorder, $headerline);
370 for ( my $i = 0; $i <= $#installer::globals::allddffiles; $i++ )
372 my $ddffilename = $installer::globals::allddffiles[$i];
373 my $ddffile = installer::files::read_file($ddffilename);
374 my $cabinetfile = "";
376 for ( my $j = 0; $j <= $#{$ddffile}; $j++ )
378 my $oneline = ${$ddffile}[$j];
380 # Getting the Cabinet file name
382 if ( $oneline =~ /^\s*\.Set\s+CabinetName.*\=(.*?)\s*$/ ) { $cabinetfile = $1; }
383 if ( $oneline =~ /^\s*\.Set\s+/ ) { next; }
385 if ( $oneline =~ /^\s*\"(.*?)\"\s+\"(.*?)\"\s*$/ )
387 my $sourcefile = $1;
388 my $uniquefilename = $2;
390 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$sourcefile);
392 # Using the hash created in create_files_table for performance reasons to get the sequence number
393 my $filesequence = "";
394 if ( exists($installer::globals::uniquefilenamesequence{$uniquefilename}) ) { $filesequence = $installer::globals::uniquefilenamesequence{$uniquefilename}; }
395 else { installer::exiter::exit_program("ERROR: No sequence number value for $uniquefilename !", "save_packorder"); }
397 my $line = $filesequence . "\t" . $cabinetfile . "\t" . $sourcefile . "\t" . $uniquefilename . "\n";
398 push(@packorder, $line);
403 installer::files::save_file($packorderfilename ,\@packorder);
405 installer::logger::include_timestamp_into_logfile("Performance Info: saving pack order end");
408 #################################################################
409 # Returning the name of the msi database
410 #################################################################
412 sub get_msidatabasename
414 my ($allvariableshashref, $language) = @_;
416 my $databasename = $allvariableshashref->{'PRODUCTNAME'} . $allvariableshashref->{'PRODUCTVERSION'};
417 $databasename = lc($databasename);
418 $databasename =~ s/\.//g;
419 $databasename =~ s/\-//g;
420 $databasename =~ s/\s//g;
422 # possibility to overwrite the name with variable DATABASENAME
423 if ( $allvariableshashref->{'DATABASENAME'} )
425 $databasename = $allvariableshashref->{'DATABASENAME'};
428 if ( $language )
430 if (!($language eq ""))
432 $databasename .= "_$language";
436 $databasename .= ".msi";
438 return $databasename;
441 #################################################################
442 # Creating the msi database
443 # This works only on Windows
444 #################################################################
446 sub create_msi_database
448 my ($idtdirbase ,$msifilename) = @_;
450 # -f : path containing the idt files
451 # -d : msi database, including path
452 # -c : create database
453 # -i : include the following tables ("*" includes all available tables)
455 my $msidb = "msidb.exe"; # Has to be in the path
456 my $extraslash = ""; # Has to be set for non-ActiveState perl
458 installer::logger::include_header_into_logfile("Creating msi database");
460 $idtdirbase = installer::converter::make_path_conform($idtdirbase);
462 $msifilename = installer::converter::make_path_conform($msifilename);
464 if ( $^O =~ /cygwin/i || $^O =~ /MSWin/i ) {
465 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
466 $idtdirbase =~ s/\//\\\\/g;
467 $msifilename =~ s/\//\\\\/g;
468 $extraslash = "\\";
470 if ( $^O =~ /linux/i ) {
471 $extraslash = "\\";
473 my $systemcall = $msidb . " -f " . $idtdirbase . " -d " . $msifilename . " -c " . "-i " . $extraslash . "*";
475 my $systemcall_output = `$systemcall`;
476 my $returnvalue = $? >> 8;
478 my $infoline = "Systemcall: $systemcall\n";
479 push( @installer::globals::logfileinfo, $infoline);
481 if ($returnvalue)
483 $infoline = "ERROR: Could not execute $msidb! - returncode: $returnvalue - output:\n$systemcall_output\n";
484 push( @installer::globals::logfileinfo, $infoline);
486 else
488 $infoline = "Success: Executed $msidb successfully!\n";
489 push( @installer::globals::logfileinfo, $infoline);
493 #################################################################
494 # Returning the msi version for the Summary Information Stream
495 #################################################################
497 sub get_msiversion_for_sis
499 my $msiversion = "200";
500 return $msiversion;
503 #################################################################
504 # Returning the word count for the Summary Information Stream
505 #################################################################
507 sub get_wordcount_for_sis
509 my $wordcount = "0";
510 return $wordcount;
513 #################################################################
514 # Returning the template for the Summary Information Stream
515 #################################################################
517 sub get_template_for_sis
519 my ( $language, $allvariables ) = @_;
521 my $windowslanguage = installer::windows::language::get_windows_language($language);
523 my $architecture = "Intel";
525 if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) { $architecture = "x64"; }
527 my $value = "\"" . $architecture . ";" . $windowslanguage; # adding the Windows language
529 $value = $value . "\""; # adding ending '"'
531 return $value ;
534 #################################################################
535 # Returning the PackageCode for the Summary Information Stream
536 #################################################################
538 sub get_packagecode_for_sis
540 # always generating a new package code for each package
542 my $guidref = get_guid_list(1, 1); # only one GUID shall be generated
544 ${$guidref}[0] =~ s/\s*$//; # removing ending spaces
546 my $guid = "\{" . ${$guidref}[0] . "\}";
548 my $infoline = "PackageCode: $guid\n";
549 push( @installer::globals::logfileinfo, $infoline);
551 return $guid;
554 #################################################################
555 # Returning the author for the Summary Information Stream
556 #################################################################
558 sub get_author_for_sis
560 my $author = $installer::globals::longmanufacturer;
562 $author = "\"" . $author . "\"";
564 return $author;
567 #################################################################
568 # Returning the subject for the Summary Information Stream
569 #################################################################
571 sub get_subject_for_sis
573 my ( $allvariableshashref ) = @_;
575 my $subject = $allvariableshashref->{'PRODUCTNAME'} . " " . $allvariableshashref->{'PRODUCTVERSION'};
577 $subject = "\"" . $subject . "\"";
579 return $subject;
582 ######################################################################
583 # Returning the security for the Summary Information Stream
584 ######################################################################
586 sub get_security_for_sis
588 my $security = "0";
589 return $security;
592 #################################################################
593 # Writing the Summary information stream into the msi database
594 # This works only on Windows
595 #################################################################
597 sub write_summary_into_msi_database
599 my ($msifilename, $language, $allvariableshashref) = @_;
601 # -g : required msi version
602 # -c : codepage
603 # -p : template
605 installer::logger::include_header_into_logfile("Writing summary information stream");
607 my $msiinfo = "msiinfo.exe"; # Has to be in the path
609 my $msiversion = get_msiversion_for_sis();
610 my $codepage = 0; # PID_CODEPAGE summary property in a signed short, therefore it is impossible to set 65001 here.
611 my $template = get_template_for_sis($language, $allvariableshashref);
612 my $guid = get_packagecode_for_sis();
613 my $title = "\"Installation database\"";
614 my $author = get_author_for_sis();
615 my $subject = get_subject_for_sis($allvariableshashref);
616 my $comment = "\"" . $allvariableshashref->{'PRODUCTNAME'} ."\"";
617 my $keywords = "\"Install,MSI\"";
618 my $appname = "\"Windows Installer\"";
619 my $security = get_security_for_sis();
620 my $wordcount = get_wordcount_for_sis();
622 $msifilename = installer::converter::make_path_conform($msifilename);
624 my $systemcall = $msiinfo . " " . $msifilename . " -g " . $msiversion . " -c " . $codepage
625 . " -p " . $template . " -v " . $guid . " -t " . $title . " -a " . $author
626 . " -j " . $subject . " -o " . $comment . " -k " . $keywords . " -n " . $appname
627 . " -u " . $security . " -w " . $wordcount;
629 my $systemcall_output = `$systemcall`;
630 my $returnvalue = $? >> 8;
632 my $infoline = "Systemcall: $systemcall\n";
633 push( @installer::globals::logfileinfo, $infoline);
635 if ($returnvalue)
637 $infoline = "ERROR: Could not execute $systemcall (return $returnvalue) - output:\n$systemcall_output\n";
638 push( @installer::globals::logfileinfo, $infoline);
640 else
642 $infoline = "Success: Executed $msiinfo successfully!\n";
643 push( @installer::globals::logfileinfo, $infoline);
647 #########################################################################
648 # For more than one language in the installation set:
649 # Use one database and create Transformations for all other languages
650 #########################################################################
652 sub create_transforms
654 my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
656 installer::logger::include_header_into_logfile("Creating Transforms");
658 my $cscript = "cscript.exe"; # Has to be in the path
659 my $msitran = "msitran.exe"; # Has to be in the path
660 my $msidb = "msidb.exe"; # Has to be in the path
661 my $wilangid = $ENV{WINDOWS_SDK_WILANGID};
663 my $from = cwd();
665 my $templatevalue = "1033";
667 $installdir = installer::converter::make_path_conform($installdir);
669 # Syntax for creating a transformation
670 # msitran.exe -g <baseDB> <referenceDB> <transformfile> [<errorhandling>}
672 my $basedbname = get_msidatabasename($allvariableshashref, $defaultlanguage);
673 $basedbname = $installdir . $installer::globals::separator . $basedbname;
675 my $errorhandling = "f"; # Suppress "change codepage" error
677 # Iterating over all files
679 foreach ( @{$languagesarray} )
681 my $onelanguage = $_;
683 if ( $onelanguage eq $defaultlanguage ) { next; }
685 my $referencedbname = get_msidatabasename($allvariableshashref, $onelanguage);
686 $referencedbname = $installdir . $installer::globals::separator . $referencedbname;
688 my $windowslanguage = installer::windows::language::get_windows_language($onelanguage);
689 my $transformfile = $installdir . $installer::globals::separator . $windowslanguage;
691 my $systemcall = $msitran . " " . " -g " . $basedbname . " " . $referencedbname . " " . $transformfile . " " . $errorhandling;
693 my $returnvalue = system($systemcall);
695 my $infoline = "Systemcall: $systemcall\n";
696 push( @installer::globals::logfileinfo, $infoline);
698 # Problem: msitran.exe in version 4.0 always returns "1", even if no failure occurred.
699 # Therefore it has to be checked, if this is version 4.0. If yes, if the mst file
700 # exists and if it is larger than 0 bytes. If this is true, then no error occurred.
701 # File Version of msitran.exe: 4.0.6000.16384 has checksum: "b66190a70145a57773ec769e16777b29".
702 # Same for msitran.exe from wntmsci12: "aa25d3445b94ffde8ef0c1efb77a56b8"
704 if ($returnvalue)
706 $infoline = "WARNING: Returnvalue of $msitran is not 0. Checking version of $msitran!\n";
707 push( @installer::globals::logfileinfo, $infoline);
709 open(FILE, "<$installer::globals::msitranpath") or die "ERROR: Can't open $installer::globals::msitranpath for creating file hash";
710 binmode(FILE);
711 my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest;
712 close(FILE);
714 my @problemchecksums = ("b66190a70145a57773ec769e16777b29", "aa25d3445b94ffde8ef0c1efb77a56b8", "748206e54fc93efe6a1aaa9d491f3ad1");
715 my $isproblemchecksum = 0;
717 foreach my $problemchecksum ( @problemchecksums )
719 $infoline = "Checksum of problematic MsiTran.exe: $problemchecksum\n";
720 push( @installer::globals::logfileinfo, $infoline);
721 $infoline = "Checksum of used MsiTran.exe: $digest\n";
722 push( @installer::globals::logfileinfo, $infoline);
723 if ( $digest eq $problemchecksum ) { $isproblemchecksum = 1; }
726 if ( $isproblemchecksum )
728 # Check existence of mst
729 if ( -f $transformfile )
731 $infoline = "File $transformfile exists.\n";
732 push( @installer::globals::logfileinfo, $infoline);
733 my $filesize = ( -s $transformfile );
734 $infoline = "Size of $transformfile: $filesize\n";
735 push( @installer::globals::logfileinfo, $infoline);
737 if ( $filesize > 0 )
739 $infoline = "Info: Returnvalue $returnvalue of $msitran is no problem :-) .\n";
740 push( @installer::globals::logfileinfo, $infoline);
741 $returnvalue = 0; # reset the error
743 else
745 $infoline = "Filesize indicates that an error occurred.\n";
746 push( @installer::globals::logfileinfo, $infoline);
749 else
751 $infoline = "File $transformfile does not exist -> An error occurred.\n";
752 push( @installer::globals::logfileinfo, $infoline);
755 else
757 $infoline = "This is not a problematic version of msitran.exe. Therefore the error is not caused by problematic msitran.exe.\n";
758 push( @installer::globals::logfileinfo, $infoline);
762 if ($returnvalue)
764 $infoline = "ERROR: Could not execute $msitran!\n";
765 push( @installer::globals::logfileinfo, $infoline);
767 else
769 $infoline = "Success: Executed $msitran successfully!\n";
770 push( @installer::globals::logfileinfo, $infoline);
773 # The reference database can be deleted
775 my $result = unlink($referencedbname);
776 # $result contains the number of deleted files
778 if ( $result == 0 )
780 $infoline = "ERROR while processing language $onelanguage: Could not remove file $referencedbname!\n";
781 push( @installer::globals::logfileinfo, $infoline);
782 installer::exiter::exit_program($infoline, "create_transforms");
785 chdir($installdir);
786 $systemcall = $msidb . " " . " -d " . $basedbname . " -r " . $windowslanguage;
787 system($systemcall);
788 # fdo#46181 - zh-HK and zh-MO should have fallen back to zh-TW not to zh-CN
789 # we need to hack zh-HK and zh-MO LCIDs directly into the MSI
790 if($windowslanguage eq '1028')
792 rename 1028,3076;
793 $systemcall = $msidb . " " . " -d " . $basedbname . " -r " . 3076;
794 system($systemcall);
795 rename 3076,5124;
796 $systemcall = $msidb . " " . " -d " . $basedbname . " -r " . 5124;
797 system($systemcall);
798 $templatevalue = $templatevalue . "," . 3076 . "," . 5124;
799 rename 5124,1028;
801 chdir($from);
802 unlink($transformfile);
804 $infoline = "Systemcall: $systemcall\n";
805 push( @installer::globals::logfileinfo, $infoline);
807 if ( $windowslanguage ne '1033')
809 $templatevalue = $templatevalue . "," . $windowslanguage;
813 $ENV{TEMP} = $ENV{TMPDIR};
814 my $systemcall = "$cscript \"$wilangid\" $basedbname Package $templatevalue";
815 my $systemcall_output = `$systemcall`;
816 my $returnvalue = $? >> 8;
818 my $infoline = "Systemcall: $systemcall\n";
819 push( @installer::globals::logfileinfo, $infoline);
821 if ($returnvalue)
823 $infoline = "ERROR: $returnvalue from $systemcall - output:\n$systemcall_output\n";
824 push( @installer::globals::logfileinfo, $infoline);
826 else
828 $infoline = "Success: Executed WiLangId.vbs successfully!\n";
829 push( @installer::globals::logfileinfo, $infoline);
833 #########################################################################
834 # The default language msi database does not need to contain
835 # the language in the database name. Therefore the file
836 # is renamed. Example: "openofficeorg20_01.msi" to "openofficeorg20.msi"
837 #########################################################################
839 sub rename_msi_database_in_installset
841 my ($defaultlanguage, $installdir, $allvariableshashref) = @_;
843 installer::logger::include_header_into_logfile("Renaming msi database");
845 my $olddatabasename = get_msidatabasename($allvariableshashref, $defaultlanguage);
846 $olddatabasename = $installdir . $installer::globals::separator . $olddatabasename;
848 my $newdatabasename = get_msidatabasename($allvariableshashref);
850 $installer::globals::shortmsidatabasename = $newdatabasename;
852 $newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
854 installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
856 $installer::globals::msidatabasename = $newdatabasename;
859 #################################################################
860 # Copying MergeModules for the Windows installer into the
861 # installation set. The list of MergeModules is located
862 # in %installer::globals::copy_msm_files
863 #################################################################
865 sub copy_merge_modules_into_installset
867 my ($installdir) = @_;
869 installer::logger::include_header_into_logfile("Copying Merge files into installation set");
871 my $cabfile;
872 foreach $cabfile ( keys %installer::globals::copy_msm_files )
874 my $sourcefile = $installer::globals::copy_msm_files{$cabfile};
875 my $destfile = $installdir . $installer::globals::separator . $cabfile;
877 installer::systemactions::copy_one_file($sourcefile, $destfile);
881 #################################################################
882 # Getting a list of GUID using uuidgen.exe.
883 # This works only on Windows
884 #################################################################
886 sub get_guid_list
888 my ($number, $log) = @_;
890 if ( $log ) { installer::logger::include_header_into_logfile("Generating $number GUID"); }
892 my $uuidgen = $ENV{'UUIDGEN'}; # Has to be in the path
894 # "-c" for uppercase output
896 my $systemcall = "$uuidgen -n$number |";
897 open (UUIDGEN, "$systemcall" ) or die("uuidgen is missing.");
898 my @uuidlist = <UUIDGEN>;
899 close (UUIDGEN);
901 my $infoline = "Systemcall: $systemcall\n";
902 if ( $log ) { push( @installer::globals::logfileinfo, $infoline); }
904 my $comparenumber = $#uuidlist + 1;
906 if ( $comparenumber == $number )
908 $infoline = "Success: Executed $uuidgen successfully!\n";
909 if ( $log ) { push( @installer::globals::logfileinfo, $infoline); }
911 else
913 $infoline = "ERROR: Could not execute $uuidgen successfully!\n";
914 if ( $log ) { push( @installer::globals::logfileinfo, $infoline); }
917 # uppercase, no longer "-c", because this is only supported in uuidgen.exe v.1.01
918 for ( my $i = 0; $i <= $#uuidlist; $i++ ) { $uuidlist[$i] = uc($uuidlist[$i]); }
920 return \@uuidlist;
923 #################################################################
924 # Calculating a GUID with a string using md5.
925 #################################################################
927 sub calculate_guid
929 my ( $string ) = @_;
931 my $guid = "";
933 my $md5 = Digest::MD5->new;
934 $md5->add($string);
935 my $digest = $md5->hexdigest;
936 $digest = uc($digest);
938 my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest);
939 $guid = "$first-$second-$third-$fourth-$fifth";
941 return $guid;
944 #################################################################
945 # Calculating a ID with a string using md5 (very fast).
946 #################################################################
948 sub calculate_id
950 my ( $string, $length ) = @_;
952 my $id = "";
954 my $md5 = Digest::MD5->new;
955 $md5->add($string);
956 my $digest = lc($md5->hexdigest);
957 $id = substr($digest, 0, $length);
959 return $id;
962 #################################################################
963 # Filling real component GUID into the component table.
964 # This works only on Windows
965 #################################################################
967 sub set_uuid_into_component_table
969 my ($idtdirbase, $allvariables) = @_;
971 my $componenttablename = $idtdirbase . $installer::globals::separator . "Componen.idt";
973 my $componenttable = installer::files::read_file($componenttablename);
975 # For update and patch reasons (small update) the GUID of an existing component must not change!
976 # The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt"
978 my $infoline = "";
979 my $counter = 0;
981 for ( my $i = 3; $i <= $#{$componenttable}; $i++ ) # ignoring the first three lines
983 my $oneline = ${$componenttable}[$i];
984 my $componentname = "";
985 if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; }
987 my $uuid = "";
989 if ( exists($installer::globals::calculated_component_guids{$componentname}))
991 $uuid = $installer::globals::calculated_component_guids{$componentname};
993 else
995 # Calculating new GUID with the help of the component name.
997 if ( ! exists($allvariables->{'PRODUCTVERSION'}) ) { installer::exiter::exit_program("ERROR: Could not find variable \"PRODUCTVERSION\" (required value for GUID creation)!", "set_uuid_into_component_table"); }
998 my $sourcestring = $componentname . "_" . $allvariables->{'PRODUCTVERSION'};
999 $uuid = calculate_guid($sourcestring);
1000 $counter++;
1002 # checking, if there is a conflict with an already created guid
1003 if ( exists($installer::globals::allcalculated_guids{$uuid}) ) { installer::exiter::exit_program("ERROR: \"$uuid\" was already created before!", "set_uuid_into_component_table"); }
1004 $installer::globals::allcalculated_guids{$uuid} = 1;
1005 $installer::globals::calculated_component_guids{$componentname} = $uuid;
1008 ${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/;
1011 installer::files::save_file($componenttablename, $componenttable);
1014 #########################################################################
1015 # Adding final 64 properties into msi database, if required.
1016 # RegLocator : +16 in type column to search in 64 bit registry.
1017 # All conditions: "VersionNT" -> "VersionNT64" (several tables).
1018 # DrLocator: "SystemFolder" -> "System64Folder"
1019 # Already done: "+256" in Attributes column of table "Component".
1020 # Still following: Setting "x64" instead of "Intel" in Summary
1021 # Information Stream of msi database in "get_template_for_sis".
1022 #########################################################################
1024 sub prepare_64bit_database
1026 my ($basedir, $allvariables) = @_;
1028 my $infoline = "";
1030 if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 ))
1032 # 1. Beginning with table "RegLocat.idt". Adding "16" to the type.
1034 my $reglocatfile = "";
1035 my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1037 if ( -f $reglocatfilename )
1039 my $saving_required = 0;
1040 $reglocatfile = installer::files::read_file($reglocatfilename);
1042 for ( my $i = 3; $i <= $#{$reglocatfile}; $i++ ) # ignoring the first three lines
1044 my $oneline = ${$reglocatfile}[$i];
1046 if ( $oneline =~ /^\s*\#/ ) { next; } # this is a comment line
1047 if ( $oneline =~ /^\s*$/ ) { next; }
1049 if ( $oneline =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(\d+)\s*$/ )
1051 # Syntax: Signature_ Root Key Name Type
1052 my $sig = $1;
1053 my $root = $2;
1054 my $key = $3;
1055 my $name = $4;
1056 my $type = $5;
1058 $type = $type + 16;
1060 my $newline = $sig . "\t" . $root . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
1061 ${$reglocatfile}[$i] = $newline;
1063 $saving_required = 1;
1067 if ( $saving_required )
1069 # Saving the files
1070 installer::files::save_file($reglocatfilename ,$reglocatfile);
1071 $infoline = "Making idt file 64 bit conform: $reglocatfilename\n";
1072 push(@installer::globals::logfileinfo, $infoline);
1076 # 2. Replacing all occurrences of "VersionNT" by "VersionNT64"
1078 my @versionnt_files = ("Componen.idt", "InstallE.idt", "InstallU.idt", "LaunchCo.idt");
1080 foreach my $onefile ( @versionnt_files )
1082 my $fullfilename = $basedir . $installer::globals::separator . $onefile;
1084 if ( -f $fullfilename )
1086 my $saving_required = 0;
1087 my $filecontent = installer::files::read_file($fullfilename);
1089 for ( my $i = 3; $i <= $#{$filecontent}; $i++ ) # ignoring the first three lines
1091 my $oneline = ${$filecontent}[$i];
1093 if ( $oneline =~ /\bVersionNT\b/ )
1095 ${$filecontent}[$i] =~ s/\bVersionNT\b/VersionNT64/g;
1096 $saving_required = 1;
1100 if ( $saving_required )
1102 # Saving the files
1103 installer::files::save_file($fullfilename ,$filecontent);
1104 $infoline = "Making idt file 64 bit conform: $fullfilename\n";
1105 push(@installer::globals::logfileinfo, $infoline);
1110 # 3. Replacing all occurrences of "SystemFolder" by "System64Folder" in "DrLocato.idt"
1112 my $drlocatofilename = $basedir . $installer::globals::separator . "DrLocato.idt";
1113 if ( -f $drlocatofilename )
1115 my $saving_required = 0;
1116 my $drlocatofile = installer::files::read_file($drlocatofilename);
1118 for ( my $i = 3; $i <= $#{$drlocatofile}; $i++ ) # ignoring the first three lines
1120 my $oneline = ${$drlocatofile}[$i];
1122 if ( $oneline =~ /\bSystemFolder\b/ )
1124 ${$drlocatofile}[$i] =~ s/\bSystemFolder\b/System64Folder/g;
1125 $saving_required = 1;
1129 if ( $saving_required )
1131 # Saving the files
1132 installer::files::save_file($drlocatofilename ,$drlocatofile);
1133 $infoline = "Making idt file 64 bit conform: $drlocatofilename\n";
1134 push(@installer::globals::logfileinfo, $infoline);
1141 #################################################################
1142 # Include all cab files into the msi database.
1143 # This works only on Windows
1144 #################################################################
1146 sub include_cabs_into_msi
1148 my ($installdir) = @_;
1150 installer::logger::include_header_into_logfile("Including cabs into msi database");
1152 my $from = cwd();
1153 my $to = $installdir;
1155 chdir($to);
1157 my $infoline = "Changing into directory: $to";
1158 push( @installer::globals::logfileinfo, $infoline);
1160 my $msidb = "msidb.exe"; # Has to be in the path
1161 my $extraslash = ""; # Has to be set for non-ActiveState perl
1163 my $msifilename = $installer::globals::msidatabasename;
1165 $msifilename = installer::converter::make_path_conform($msifilename);
1167 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
1168 $msifilename =~ s/\//\\\\/g;
1169 $extraslash = "\\";
1171 my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
1173 for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ )
1175 my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i];
1177 my $returnvalue = system($systemcall);
1179 $infoline = "Systemcall: $systemcall\n";
1180 push( @installer::globals::logfileinfo, $infoline);
1182 if ($returnvalue)
1184 $infoline = "ERROR: Could not execute $systemcall !\n";
1185 push( @installer::globals::logfileinfo, $infoline);
1187 else
1189 $infoline = "Success: Executed $systemcall successfully!\n";
1190 push( @installer::globals::logfileinfo, $infoline);
1193 # deleting the cab file
1195 unlink(${$allcabfiles}[$i]);
1197 $infoline = "Deleted cab file: ${$allcabfiles}[$i]\n";
1198 push( @installer::globals::logfileinfo, $infoline);
1201 $infoline = "Changing back into directory: $from";
1202 push( @installer::globals::logfileinfo, $infoline);
1204 chdir($from);
1207 #################################################################
1208 # Executing the created batch file to pack all files.
1209 # This works only on Windows
1210 #################################################################
1212 sub execute_packaging
1214 my ($localpackjobref, $loggingdir, $allvariables) = @_;
1216 installer::logger::include_header_into_logfile("Packaging process");
1218 installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging start");
1220 my $infoline = "";
1221 my $from = cwd();
1222 my $to = $loggingdir;
1224 chdir($to);
1225 $infoline = "chdir: $to \n";
1226 push( @installer::globals::logfileinfo, $infoline);
1228 # the ddf file contains relative paths, it is necessary to change into the temp directory
1229 $to = $installer::globals::temppath;
1230 chdir($to);
1231 $infoline = "chdir: $to \n";
1232 push( @installer::globals::logfileinfo, $infoline);
1234 my $maxmakecabcalls = 3;
1235 my $allmakecabcalls = $#{$localpackjobref} + 1;
1237 for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ )
1239 my $systemcall = ${$localpackjobref}[$i];
1241 my $callscounter = $i + 1;
1243 installer::logger::print_message( "... makecab.exe ($callscounter/$allmakecabcalls) ... \n" );
1245 for ( my $n = 1; $n <= $maxmakecabcalls; $n++ )
1247 my @ddfoutput = ();
1249 $infoline = "Systemcall: $systemcall";
1250 push( @installer::globals::logfileinfo, $infoline);
1252 open (DDF, "$systemcall");
1253 while (<DDF>) {push(@ddfoutput, $_); }
1254 close (DDF);
1256 my $returnvalue = $?; # $? contains the return value of the systemcall
1258 if ($returnvalue)
1260 if ( $n < $maxmakecabcalls )
1262 installer::logger::print_message( "makecab_error (Try $n): Trying again \n" );
1263 $infoline = "makecab_error (Try $n): $systemcall !";
1265 else
1267 installer::logger::print_message( "ERROR (Try $n): Abort packing \n" );
1268 $infoline = "ERROR (Try $n): $systemcall !";
1271 push( @installer::globals::logfileinfo, $infoline);
1273 for ( my $m = 0; $m <= $#ddfoutput; $m++ )
1275 if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ )
1277 $infoline = $1 . "\n";
1278 if ( $n < $maxmakecabcalls ) { $infoline =~ s/ERROR\:/makecab_error\:/i; }
1279 installer::logger::print_message( $infoline );
1280 push( @installer::globals::logfileinfo, $infoline);
1284 if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); }
1286 else
1288 $infoline = "Success (Try $n): $systemcall";
1289 push( @installer::globals::logfileinfo, $infoline);
1290 last;
1295 installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging end");
1297 chdir($from);
1298 $infoline = "chdir: $from \n";
1299 push( @installer::globals::logfileinfo, $infoline);
1302 ###############################################################
1303 # Setting the global variables ProductCode and the UpgradeCode
1304 ###############################################################
1306 sub set_global_code_variables
1308 my ( $languagesref, $languagestringref, $allvariableshashref, $alloldproperties ) = @_;
1310 # In the msi template directory a files "codes.txt" has to exist, in which the ProductCode
1311 # and the UpgradeCode for the product are defined.
1312 # The name "codes.txt" can be overwritten in Product definition with CODEFILENAME .
1313 # Default $installer::globals::codefilename is defined in parameter.pm.
1315 if ( $allvariableshashref->{'CODEFILENAME'} )
1317 $installer::globals::codefilename = $installer::globals::idttemplatepath . $installer::globals::separator . $allvariableshashref->{'CODEFILENAME'};
1318 installer::files::check_file($installer::globals::codefilename);
1321 my $infoline = "Using Codes file: $installer::globals::codefilename \n";
1322 push( @installer::globals::logfileinfo, $infoline);
1324 my $codefile = installer::files::read_file($installer::globals::codefilename);
1326 my $onelanguage = "";
1328 if ( $#{$languagesref} > 0 ) # more than one language
1330 if (( $installer::globals::added_english ) && ( $#{$languagesref} == 1 )) # only multilingual because of added English
1332 $onelanguage = ${$languagesref}[1]; # setting the first language, that is not english
1334 else
1336 if (( ${$languagesref}[1] =~ /jp/ ) ||
1337 ( ${$languagesref}[1] =~ /ko/ ) ||
1338 ( ${$languagesref}[1] =~ /zh/ ))
1340 $onelanguage = "multiasia";
1342 else
1344 $onelanguage = "multiwestern";
1348 else # only one language
1350 $onelanguage = ${$languagesref}[0];
1353 # ProductCode must not change, if Windows patches shall be applied
1354 if ( $installer::globals::updatedatabase )
1356 $installer::globals::productcode = $alloldproperties->{'ProductCode'};
1358 elsif ( $installer::globals::prepare_winpatch )
1360 # ProductCode has to be specified in each language
1361 my $searchstring = "PRODUCTCODE";
1362 my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
1363 $installer::globals::productcode = installer::windows::idtglobal::get_code_from_code_block($codeblock, $onelanguage);
1364 } else {
1365 my $guidref = get_guid_list(1, 1); # only one GUID shall be generated
1366 ${$guidref}[0] =~ s/\s*$//; # removing ending spaces
1367 $installer::globals::productcode = "\{" . ${$guidref}[0] . "\}";
1370 # UpgradeCode can take english as default, if not defined in specified language
1372 my $searchstring = "UPGRADECODE"; # searching in the codes.txt file
1373 my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
1374 $installer::globals::upgradecode = installer::windows::idtglobal::get_language_string_from_language_block($codeblock, $onelanguage, "");
1376 if ( $installer::globals::upgradecode eq "" ) { installer::exiter::exit_program("ERROR: UpgradeCode not defined in $installer::globals::codefilename !", "set_global_code_variables"); }
1378 $infoline = "Setting ProductCode to: $installer::globals::productcode \n";
1379 push( @installer::globals::logfileinfo, $infoline);
1380 $infoline = "Setting UpgradeCode to: $installer::globals::upgradecode \n";
1381 push( @installer::globals::logfileinfo, $infoline);
1383 # Adding both variables into the variables array
1385 $allvariableshashref->{'PRODUCTCODE'} = $installer::globals::productcode;
1386 $allvariableshashref->{'UPGRADECODE'} = $installer::globals::upgradecode;
1388 $infoline = "Defined variable PRODUCTCODE: $installer::globals::productcode \n";
1389 push( @installer::globals::logfileinfo, $infoline);
1391 $infoline = "Defined variable UPGRADECODE: $installer::globals::upgradecode \n";
1392 push( @installer::globals::logfileinfo, $infoline);
1396 ###############################################################
1397 # Setting the product version used in property table and
1398 # upgrade table. Saving in global variable $msiproductversion
1399 ###############################################################
1401 sub set_msiproductversion
1403 my ( $allvariables ) = @_;
1405 my $productversion = $allvariables->{'PACKAGEVERSION'};
1407 if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
1409 $productversion = $1 . "\." . $2 . "\." . $3 . "\." . $installer::globals::buildid;
1412 $installer::globals::msiproductversion = $productversion;
1414 # Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table
1416 if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ )
1418 my $major = $1;
1419 $installer::globals::msimajorproductversion = $major . "\.0\.0";
1423 ####################################################################################
1424 # Updating the file Property.idt dynamically
1425 # Content:
1426 # Property Value
1427 ####################################################################################
1429 sub update_reglocat_table
1431 my ($basedir, $allvariables) = @_;
1433 my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1435 # Only do something, if this file exists
1437 if ( -f $reglocatfilename )
1439 my $reglocatfile = installer::files::read_file($reglocatfilename);
1441 my $layername = "";
1442 if ( $allvariables->{'REGISTRYLAYERNAME'} )
1444 $layername = $allvariables->{'REGISTRYLAYERNAME'};
1446 else
1448 for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
1450 if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ )
1452 installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table");
1457 if ( $layername ne "" )
1459 # Updating the layername in
1461 for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
1463 ${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/;
1466 # Saving the file
1467 installer::files::save_file($reglocatfilename ,$reglocatfile);
1468 my $infoline = "Updated idt file: $reglocatfilename\n";
1469 push(@installer::globals::logfileinfo, $infoline);
1476 ####################################################################################
1477 # Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt)
1478 # The name of the component has to be replaced.
1479 ####################################################################################
1481 sub update_removere_table
1483 my ($basedir) = @_;
1485 my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt";
1487 # Only do something, if this file exists
1489 if ( -f $removeregistryfilename )
1491 my $removeregistryfile = installer::files::read_file($removeregistryfilename);
1493 for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
1495 for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
1497 ${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/;
1501 # Saving the file
1502 installer::files::save_file($removeregistryfilename ,$removeregistryfile);
1503 my $infoline = "Updated idt file: $removeregistryfilename \n";
1504 push(@installer::globals::logfileinfo, $infoline);
1508 ##########################################################################
1509 # Reading saved mappings in Files.idt and Director.idt.
1510 # This is required, if installation sets shall be created,
1511 # that can be used for creation of msp files.
1512 ##########################################################################
1514 sub read_saved_mappings
1516 installer::logger::include_header_into_logfile("Reading saved mappings from older installation sets:");
1518 installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings start");
1520 if ( $installer::globals::previous_idt_dir )
1522 my @errorlines = ();
1523 my $errorstring = "";
1524 my $error_occurred = 0;
1525 my $file_error_occurred = 0;
1526 my $dir_error_occurred = 0;
1528 my $idtdir = $installer::globals::previous_idt_dir;
1529 $idtdir =~ s/\Q$installer::globals::separator\E\s*$//;
1531 # Reading File.idt
1533 my $idtfile = $idtdir . $installer::globals::separator . "File.idt";
1534 push( @installer::globals::globallogfileinfo, "\nAnalyzing file: $idtfile\n" );
1535 if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); }
1537 my $n = 0;
1538 open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
1539 <F>; <F>; <F>;
1540 while (<F>)
1542 m/^([^\t]+)\t([^\t]+)\t((.*)\|)?([^\t]*)/;
1543 print "OUT1: \$1: $1, \$2: $2, \$3: $3, \$4: $4, \$5: $5\n";
1544 next if ("$1" eq "$5") && (!defined($3));
1545 my $lc1 = lc($1);
1547 if ( exists($installer::globals::savedmapping{"$2/$5"}))
1549 if ( ! $file_error_occurred )
1551 $errorstring = "\nErrors in $idtfile: \n";
1552 push(@errorlines, $errorstring);
1554 $errorstring = "Duplicate savedmapping{" . "$2/$5}\n";
1555 push(@errorlines, $errorstring);
1556 $error_occurred = 1;
1557 $file_error_occurred = 1;
1560 if ( exists($installer::globals::savedrevmapping{$lc1}))
1562 if ( ! $file_error_occurred )
1564 $errorstring = "\nErrors in $idtfile: \n";
1565 push(@errorlines, $errorstring);
1567 $errorstring = "Duplicate savedrevmapping{" . "$lc1}\n";
1568 push(@errorlines, $errorstring);
1569 $error_occurred = 1;
1570 $file_error_occurred = 1;
1573 my $shortname = $4 || '';
1575 # Don't reuse illegal 8.3 mappings that we used to generate in 2.0.4
1576 if (index($shortname, '.') > 8 ||
1577 (index($shortname, '.') == -1 && length($shortname) > 8))
1579 $shortname = '';
1582 if (( $shortname ne '' ) && ( index($shortname, '~') > 0 ) && ( exists($installer::globals::savedrev83mapping{$shortname}) ))
1584 if ( ! $file_error_occurred )
1586 $errorstring = "\nErrors in $idtfile: \n";
1587 push(@errorlines, $errorstring);
1589 $errorstring = "Duplicate savedrev83mapping{" . "$shortname}\n";
1590 push(@errorlines, $errorstring);
1591 $error_occurred = 1;
1592 $file_error_occurred = 1;
1595 $installer::globals::savedmapping{"$2/$5"} = "$1;$shortname";
1596 $installer::globals::savedrevmapping{lc($1)} = "$2/$5";
1597 $installer::globals::savedrev83mapping{$shortname} = "$2/$5" if $shortname ne '';
1598 $n++;
1601 close (F);
1603 push( @installer::globals::globallogfileinfo, "Read $n old file table key or 8.3 name mappings from $idtfile\n" );
1605 # Reading Director.idt
1607 $idtfile = $idtdir . $installer::globals::separator . "Director.idt";
1608 push( @installer::globals::globallogfileinfo, "\nAnalyzing file $idtfile\n" );
1609 if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); }
1611 $n = 0;
1612 open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
1613 <F>; <F>; <F>;
1614 while (<F>)
1616 m/^([^\t]+)\t([^\t]+)\t(([^~]+~\d.*)\|)?([^\t]*)/;
1617 next if (!defined($3));
1618 my $lc1 = lc($1);
1620 print "OUT2: \$1: $1, \$2: $2, \$3: $3\n";
1622 if ( exists($installer::globals::saved83dirmapping{$1}) )
1624 if ( ! $dir_error_occurred )
1626 $errorstring = "\nErrors in $idtfile: \n";
1627 push(@errorlines, $errorstring);
1629 $errorstring = "Duplicate saved83dirmapping{" . "$1}\n";
1630 push(@errorlines, $errorstring);
1631 $error_occurred = 1;
1632 $dir_error_occurred = 1;
1635 $installer::globals::saved83dirmapping{$1} = $4;
1636 $n++;
1638 close (F);
1640 push( @installer::globals::globallogfileinfo, "Read $n old directory 8.3 name mappings from $idtfile\n" );
1642 # Analyzing errors
1644 if ( $error_occurred )
1646 for ( my $i = 0; $i <= $#errorlines; $i++ )
1648 print "$errorlines[$i]";
1649 push( @installer::globals::globallogfileinfo, "$errorlines[$i]");
1651 installer::exiter::exit_program("ERROR: Duplicate entries in saved mappings!", "read_saved_mappings");
1653 } else {
1654 installer::exiter::exit_program("ERROR: Windows patch shall be prepared, but environment variable PREVIOUS_IDT_DIR is not set!", "read_saved_mappings");
1657 installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings end");
1662 # vim:set shiftwidth=4 softtabstop=4 expandtab: