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
::file
;
22 use installer
::exiter
;
24 use installer
::globals
;
25 use installer
::logger
;
26 use installer
::pathanalyzer
;
27 use installer
::worker
;
28 use installer
::windows
::font
;
29 use installer
::windows
::idtglobal
;
30 use installer
::windows
::msiglobal
;
31 use installer
::windows
::language
;
32 use installer
::windows
::component
;
34 ##########################################################################
35 # Assigning one cabinet file to each file. This is requrired,
36 # if cabinet files shall be equivalent to packages.
37 ##########################################################################
39 sub assign_cab_to_files
41 my ( $filesref ) = @_;
45 for ( my $i = 0; $i <= $#{$filesref}; $i++ )
47 if ( ! exists(${$filesref}[$i]->{'modules'}) ) { installer
::exiter
::exit_program
("ERROR: No module assignment found for ${$filesref}[$i]->{'gid'} !", "assign_cab_to_files"); }
48 my $module = ${$filesref}[$i]->{'modules'};
49 # If modules contains a list of modules, only taking the first one.
50 if ( $module =~ /^\s*(.*?)\,/ ) { $module = $1; }
52 if ( ! exists($installer::globals
::allcabinetassigns
{$module}) ) { installer
::exiter
::exit_program
("ERROR: No cabinet file assigned to module \"$module\" (${$filesref}[$i]->{'gid'}) !", "assign_cab_to_files"); }
53 ${$filesref}[$i]->{'assignedcabinetfile'} = $installer::globals
::allcabinetassigns
{$module};
55 # Counting the files in each cabinet file
56 if ( ! exists($installer::globals
::cabfilecounter
{${$filesref}[$i]->{'assignedcabinetfile'}}) )
58 $installer::globals
::cabfilecounter
{${$filesref}[$i]->{'assignedcabinetfile'}} = 1;
62 $installer::globals
::cabfilecounter
{${$filesref}[$i]->{'assignedcabinetfile'}}++;
66 # logging the number of files in each cabinet file
68 $infoline = "\nCabinet file content:\n";
69 push(@installer::globals
::logfileinfo
, $infoline);
71 foreach $cabfile ( sort keys %installer::globals
::cabfilecounter
)
73 $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile} files\n";
74 push(@installer::globals
::logfileinfo
, $infoline);
77 # assigning startsequencenumbers for each cab file
80 foreach $cabfile ( sort keys %installer::globals
::cabfilecounter
)
82 my $filecount = $installer::globals
::cabfilecounter
{$cabfile};
83 $installer::globals
::cabfilecounter
{$cabfile} = $offset;
84 $offset = $offset + $filecount;
86 $installer::globals
::lastsequence
{$cabfile} = $offset - 1;
89 # logging the start sequence numbers
91 $infoline = "\nCabinet file start sequences:\n";
92 push(@installer::globals
::logfileinfo
, $infoline);
93 foreach $cabfile ( sort keys %installer::globals
::cabfilecounter
)
95 $infoline = "$cabfile : $installer::globals::cabfilecounter{$cabfile}\n";
96 push(@installer::globals
::logfileinfo
, $infoline);
99 # logging the last sequence numbers
101 $infoline = "\nCabinet file last sequences:\n";
102 push(@installer::globals
::logfileinfo
, $infoline);
103 foreach $cabfile ( sort keys %installer::globals
::lastsequence
)
105 $infoline = "$cabfile : $installer::globals::lastsequence{$cabfile}\n";
106 push(@installer::globals
::logfileinfo
, $infoline);
110 ##########################################################################
111 # Assigning sequencenumbers to files. This is requrired,
112 # if cabinet files shall be equivalent to packages.
113 ##########################################################################
115 sub assign_sequencenumbers_to_files
117 my ( $filesref ) = @_;
119 my %directaccess = ();
122 for ( my $i = 0; $i <= $#{$filesref}; $i++ )
124 my $onefile = ${$filesref}[$i];
126 # Keeping order in cabinet files
127 # -> collecting all files in one cabinet file
128 # -> sorting files and assigning numbers
130 # Saving counter $i for direct access into files array
131 # "destination" of the file is a unique identifier ('Name' is not unique!)
132 if ( exists($directaccess{$onefile->{'destination'}}) ) { installer
::exiter
::exit_program
("ERROR: 'destination' at file not unique: $onefile->{'destination'}", "assign_sequencenumbers_to_files"); }
133 $directaccess{$onefile->{'destination'}} = $i;
135 my $cabfilename = $onefile->{'assignedcabinetfile'};
136 # collecting files in cabinet files
137 if ( ! exists($allassigns{$cabfilename}) )
140 $onecabfile{$onefile->{'destination'}} = 1;
141 $allassigns{$cabfilename} = \
%onecabfile;
145 $allassigns{$cabfilename}->{$onefile->{'destination'}} = 1;
149 # Sorting each hash and assigning numbers
150 # The destination of the file determines the sort order, not the filename!
152 foreach $cabfile ( sort keys %allassigns )
154 my $counter = $installer::globals
::cabfilecounter
{$cabfile};
156 foreach $dest ( sort keys %{$allassigns{$cabfile}} ) # <- sorting the destination!
158 my $directaccessnumber = $directaccess{$dest};
159 ${$filesref}[$directaccessnumber]->{'assignedsequencenumber'} = $counter;
165 #########################################################
166 # Create a shorter version of a long component name,
167 # because maximum length in msi database is 72.
168 # Attention: In multi msi installation sets, the short
169 # names have to be unique over all packages, because
170 # this string is used to create the globally unique id
172 # %installer::globals::allshortcomponents
173 # after a package was created.
174 # Using no counter because of reproducibility.
175 #########################################################
177 sub generate_new_short_componentname
179 my ($componentname) = @_;
181 my $startversion = substr($componentname, 0, 60); # taking only the first 60 characters
182 my $subid = installer
::windows
::msiglobal
::calculate_id
($componentname, 9); # taking only the first 9 digits
183 my $shortcomponentname = $startversion . "_" . $subid;
185 if ( exists($installer::globals
::allshortcomponents
{$shortcomponentname}) ) { installer
::exiter
::exit_program
("Failed to create unique component name: \"$shortcomponentname\"", "generate_new_short_componentname"); }
187 $installer::globals
::allshortcomponents
{$shortcomponentname} = 1;
189 return $shortcomponentname;
192 ###############################################
193 # Generating the component name from a file
194 ###############################################
196 sub get_file_component_name
198 my ($fileref, $filesref) = @_;
200 my $componentname = "";
202 # Special handling for files with ASSIGNCOMPOMENT
205 if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; }
206 if ( $styles =~ /\bASSIGNCOMPOMENT\b/ )
208 $componentname = get_component_from_assigned_file
($fileref->{'AssignComponent'}, $filesref);
212 # In this function exists the rule to create components from files
214 # Two files get the same componentid, if:
215 # both have the same destination directory.
216 # both have the same "gid" -> both were packed in the same zip file
217 # All other files are included into different components!
219 # my $componentname = $fileref->{'gid'} . "_" . $fileref->{'Dir'};
221 # $fileref->{'Dir'} is not sufficient! All files in a zip file have the same $fileref->{'Dir'},
222 # but can be in different subdirectories.
223 # Solution: destination=share\Scripts\beanshell\Capitalise\capitalise.bsh
224 # in which the filename (capitalise.bsh) has to be removed and all backslashes (slashes) are
225 # converted into underline.
227 my $destination = $fileref->{'destination'};
228 installer
::pathanalyzer
::get_path_from_fullqualifiedname
(\
$destination);
229 $destination =~ s/\s//g;
230 $destination =~ s/\\/\_/g;
231 $destination =~ s/\//\_
/g
;
232 $destination =~ s/\_\s*$//g; # removing ending underline
234 $componentname = $fileref->{'gid'} . "__" . $destination;
236 # Files with different languages, need to be packed into different components.
237 # Then the installation of the language specific component is determined by a language condition.
239 if ( $fileref->{'ismultilingual'} )
241 my $officelanguage = $fileref->{'specificlanguage'};
242 $componentname = $componentname . "_" . $officelanguage;
245 $componentname = lc($componentname); # componentnames always lowercase
247 $componentname =~ s/\-/\_/g; # converting "-" to "_"
248 $componentname =~ s/\./\_/g; # converting "-" to "_"
250 # Attention: Maximum length for the componentname is 72
251 # %installer::globals::allcomponents_in_this_database : resetted for each database
252 # %installer::globals::allcomponents : not resetted for each database
253 # Component strings must be unique for the complete product, because they are used for
254 # the creation of the globally unique identifier.
256 my $fullname = $componentname; # This can be longer than 72
258 if (( exists($installer::globals
::allcomponents
{$fullname}) ) && ( ! exists($installer::globals
::allcomponents_in_this_database
{$fullname}) ))
260 # This is not allowed: One component cannot be installed with different packages.
261 installer
::exiter
::exit_program
("ERROR: Component \"$fullname\" is already included into another package. This is not allowed.", "get_file_component_name");
264 if ( exists($installer::globals
::allcomponents
{$fullname}) )
266 $componentname = $installer::globals
::allcomponents
{$fullname};
270 if ( length($componentname) > 70 )
272 $componentname = generate_new_short_componentname
($componentname); # This has to be unique for the complete product, not only one package
275 $installer::globals
::allcomponents
{$fullname} = $componentname;
276 $installer::globals
::allcomponents_in_this_database
{$fullname} = 1;
279 # $componentname =~ s/gid_file_/g_f_/g;
280 # $componentname =~ s/_extra_/_e_/g;
281 # $componentname =~ s/_config_/_c_/g;
282 # $componentname =~ s/_org_openoffice_/_o_o_/g;
283 # $componentname =~ s/_program_/_p_/g;
284 # $componentname =~ s/_typedetection_/_td_/g;
285 # $componentname =~ s/_linguistic_/_l_/g;
286 # $componentname =~ s/_module_/_m_/g;
287 # $componentname =~ s/_optional_/_opt_/g;
288 # $componentname =~ s/_packages/_pack/g;
289 # $componentname =~ s/_menubar/_mb/g;
290 # $componentname =~ s/_common_/_cm_/g;
291 # $componentname =~ s/_export_/_exp_/g;
292 # $componentname =~ s/_table_/_tb_/g;
293 # $componentname =~ s/_sofficecfg_/_sc_/g;
294 # $componentname =~ s/_soffice_cfg_/_sc_/g;
295 # $componentname =~ s/_startmodulecommands_/_smc_/g;
296 # $componentname =~ s/_drawimpresscommands_/_dic_/g;
297 # $componentname =~ s/_basiccommands_/_bac_/g;
298 # $componentname =~ s/_basicidecommands_/_baic_/g;
299 # $componentname =~ s/_genericcommands_/_genc_/g;
300 # $componentname =~ s/_bibliographycommands_/_bibc_/g;
301 # $componentname =~ s/_gentiumbookbasicbolditalic_/_gbbbi_/g;
302 # $componentname =~ s/_share_/_s_/g;
303 # $componentname =~ s/_extension_/_ext_/g;
304 # $componentname =~ s/_extensions_/_exs_/g;
305 # $componentname =~ s/_modules_/_ms_/g;
306 # $componentname =~ s/_uiconfig_zip_/_ucz_/g;
307 # $componentname =~ s/_productivity_/_pr_/g;
308 # $componentname =~ s/_wizard_/_wz_/g;
309 # $componentname =~ s/_import_/_im_/g;
310 # $componentname =~ s/_javascript_/_js_/g;
311 # $componentname =~ s/_template_/_tpl_/g;
312 # $componentname =~ s/_tplwizletter_/_twl_/g;
313 # $componentname =~ s/_beanshell_/_bs_/g;
314 # $componentname =~ s/_presentation_/_bs_/g;
315 # $componentname =~ s/_columns_/_cls_/g;
316 # $componentname =~ s/_python_/_py_/g;
318 # $componentname =~ s/_tools/_ts/g;
319 # $componentname =~ s/_transitions/_trs/g;
320 # $componentname =~ s/_scriptbinding/_scrb/g;
321 # $componentname =~ s/_spreadsheet/_ssh/g;
322 # $componentname =~ s/_publisher/_pub/g;
323 # $componentname =~ s/_presenter/_pre/g;
324 # $componentname =~ s/_registry/_reg/g;
326 # $componentname =~ s/screen/sc/g;
327 # $componentname =~ s/wordml/wm/g;
328 # $componentname =~ s/openoffice/oo/g;
331 return $componentname;
334 ####################################################################
335 # Returning the component name for a defined file gid.
336 # This is necessary for files with flag ASSIGNCOMPOMENT
337 ####################################################################
339 sub get_component_from_assigned_file
341 my ($gid, $filesref) = @_;
343 my ($onefile) = grep {$_->{gid
} eq $gid} @
{$filesref};
344 if (! defined $onefile) {
345 installer
::exiter
::exit_program
("ERROR: Could not find file $gid in list of files!", "get_component_from_assigned_file");
348 my $componentname = "";
349 if ( $onefile->{'componentname'} ) { $componentname = $onefile->{'componentname'}; }
350 else { installer
::exiter
::exit_program
("ERROR: No component defined for file: $gid", "get_component_from_assigned_file"); }
352 return $componentname;
355 ####################################################################
356 # Generating the special filename for the database file File.idt
357 # Sample: CONTEXTS, CONTEXTS1
358 # This name has to be unique.
359 # In most cases this is simply the filename.
360 ####################################################################
362 sub generate_unique_filename_for_filetable
364 my ($fileref, $component, $uniquefilenamehashref) = @_;
366 # This new filename has to be saved into $fileref, because this is needed to find the source.
367 # The filename sbasic.idx/OFFSETS is changed to OFFSETS, but OFFSETS is not unique.
368 # In this procedure names like OFFSETS5 are produced. And exactly this string has to be added to
369 # the array of all files.
371 my $uniquefilename = "";
374 if ( $fileref->{'Name'} ) { $uniquefilename = $fileref->{'Name'}; }
376 installer
::pathanalyzer
::make_absolute_filename_to_relative_filename
(\
$uniquefilename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs
378 # Reading unique filename with help of "Component_" in File table from old database
379 if (( $installer::globals
::updatedatabase
) && ( exists($uniquefilenamehashref->{"$component/$uniquefilename"}) ))
381 $uniquefilename = $uniquefilenamehashref->{"$component/$uniquefilename"}; # syntax of $value: ($uniquename;$shortname)
382 if ( $uniquefilename =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $uniquefilename = $1; }
383 $lcuniquefilename = lc($uniquefilename);
384 $installer::globals
::alluniquefilenames
{$uniquefilename} = 1;
385 $installer::globals
::alllcuniquefilenames
{$lcuniquefilename} = 1;
386 return $uniquefilename;
388 elsif (( $installer::globals
::prepare_winpatch
) && ( exists($installer::globals
::savedmapping
{"$component/$uniquefilename"}) ))
390 # If we have a FTK mapping for this component/file, use it.
391 $installer::globals
::savedmapping
{"$component/$uniquefilename"} =~ m
/^(.*);/;
392 $uniquefilename = $1;
393 $lcuniquefilename = lc($uniquefilename);
394 $installer::globals
::alluniquefilenames
{$uniquefilename} = 1;
395 $installer::globals
::alllcuniquefilenames
{$lcuniquefilename} = 1;
396 return $uniquefilename;
399 $uniquefilename =~ s/\-/\_/g; # no "-" allowed
400 $uniquefilename =~ s/\@/\_/g; # no "@" allowed
401 $uniquefilename =~ s/\$/\_/g; # no "$" allowed
402 $uniquefilename =~ s/^\s*\./\_/g; # no "." at the beginning allowed allowed
403 $uniquefilename =~ s/^\s*\d/\_d/g; # no number at the beginning allowed allowed (even file "0.gif", replacing to "_d.gif")
404 $uniquefilename =~ s/org_openoffice_/ooo_/g; # shorten the unique file name
406 my $lcuniquefilename = lc($uniquefilename); # only lowercase names
410 if ( ! exists($installer::globals
::alllcuniquefilenames
{$lcuniquefilename}) &&
411 ! exists($installer::globals
::savedrevmapping
{$lcuniquefilename}) )
413 $installer::globals
::alluniquefilenames
{$uniquefilename} = 1;
414 $installer::globals
::alllcuniquefilenames
{$lcuniquefilename} = 1;
420 # adding a number until the name is really unique: OFFSETS, OFFSETS1, OFFSETS2, ...
421 # But attention: Making "abc.xcu" to "abc1.xcu"
423 my $uniquefilenamebase = $uniquefilename;
429 if ( $uniquefilenamebase =~ /\./ )
431 $uniquefilename = $uniquefilenamebase;
432 $uniquefilename =~ s/\./$counter\./;
436 $uniquefilename = $uniquefilenamebase . $counter;
440 $lcuniquefilename = lc($uniquefilename); # only lowercase names
442 if ( ! exists($installer::globals
::alllcuniquefilenames
{$lcuniquefilename}) &&
443 ! exists($installer::globals
::savedrevmapping
{$lcuniquefilename}) )
445 $installer::globals
::alluniquefilenames
{$uniquefilename} = 1;
446 $installer::globals
::alllcuniquefilenames
{$lcuniquefilename} = 1;
453 return $uniquefilename;
456 ####################################################################
457 # Generating the special file column for the database file File.idt
458 # Sample: NAMETR~1.TAB|.nametranslation.table
459 # The first part has to be 8.3 conform.
460 ####################################################################
462 sub generate_filename_for_filetable
464 my ($fileref, $shortnamesref, $uniquefilenamehashref) = @_;
466 my $returnstring = "";
468 my $filename = $fileref->{'Name'};
470 installer
::pathanalyzer
::make_absolute_filename_to_relative_filename
(\
$filename); # making /registry/schema/org/openoffice/VCL.xcs to VCL.xcs
474 # Reading short string with help of "FileName" in File table from old database
475 if (( $installer::globals
::updatedatabase
) && ( exists($uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}) ))
477 my $value = $uniquefilenamehashref->{"$fileref->{'componentname'}/$filename"}; # syntax of $value: ($uniquename;$shortname)
478 if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ ) { $shortstring = $2; } # already collected in function "collect_shortnames_from_old_database"
479 else { $shortstring = $filename; }
481 elsif (( $installer::globals
::prepare_winpatch
) && ( exists($installer::globals
::savedmapping
{"$fileref->{'componentname'}/$filename"}) ))
483 $installer::globals
::savedmapping
{"$fileref->{'componentname'}/$filename"} =~ m
/.*;(.*)/;
490 $shortstring = installer
::windows
::idtglobal
::make_eight_three_conform_with_hash
($filename, "file", $shortnamesref);
495 $shortstring = installer
::windows
::idtglobal
::make_eight_three_conform_with_hash
($filename, "file", $shortnamesref);
498 if ( $shortstring eq $filename ) { $returnstring = $filename; } # nothing changed
499 else {$returnstring = $shortstring . "\|" . $filename; }
501 return $returnstring;
504 #########################################
505 # Returning the filesize of a file
506 #########################################
512 my $file = $fileref->{'sourcepath'};
516 if ( -f
$file ) # test of existence. For instance services.rdb does not always exist
518 $filesize = ( -s
$file ); # file size can be "0"
528 #############################################
529 # Returning the file version, if required
530 # Sample: "8.0.1.8976";
531 #############################################
535 my ($onefile, $allvariables, $styles) = @_;
537 my $fileversion = "";
539 if ( $onefile->{'Name'} =~ /\.bin$|\.com$|\.dll$|\.exe$|\.pyd$/ )
541 open (EXE
, "<$onefile->{'sourcepath'}");
543 {local $/ = undef; $exedata = <EXE
>;}
546 my $binaryfileversion = "(V\x00S\x00_\x00V\x00E\x00R\x00S\x00I\x00O\x00N\x00_\x00I\x00N\x00F\x00O\x00\x00\x00\x00\x00\xbd\x04\xef\xfe\x00\x00\x01\x00)(........)";
548 if ($exedata =~ /$binaryfileversion/ms)
550 my ($header, $subversion, $version, $vervariant, $microversion) = ($1,unpack( "vvvv", $2));
551 $fileversion = $version . "." . $subversion . "." . $microversion . "." . $vervariant;
554 # file version for font files (tdf#76239)
555 if ( $onefile->{'Name'} =~ /\.ttf$|\.TTF$/ )
557 open (TTF
, "<$onefile->{'sourcepath'}");
559 {local $/ = undef; $ttfdata = <TTF
>;}
562 my $ttfversion = "(Version )([0-9]+[.]*([0-9][.])*[0-9]+)";
564 if ($ttfdata =~ /$ttfversion/ms)
566 my ($version, $subversion, $microversion, $vervariant) = split(/\./,$2);
567 $fileversion = int($version) . "." . int($subversion) . "." . int($microversion) . "." . int($vervariant);
571 $fileversion = "1.0.0.0";
578 #############################################
579 # Returning the sequence for a file
580 #############################################
582 sub get_sequence_for_file
584 my ($number, $onefile, $fileentry, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, $allfilecomponents) = @_;
588 my $pffcomponentname = $onefile->{'componentname'} . "_pff";
590 if ( $installer::globals
::updatedatabase
)
592 if (( exists($allupdatesequenceshashref->{$onefile->{'uniquename'}}) ) &&
593 (( $onefile->{'componentname'} eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) ||
594 ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} )))
596 # The second condition is necessary to find shifted files, that have same "uniquename", but are now
597 # located in another directory. This can be seen at the component name.
598 $sequence = $allupdatesequenceshashref->{$onefile->{'uniquename'}};
599 $onefile->{'assignedsequencenumber'} = $sequence;
600 # Collecting all used sequences, to guarantee, that no number is unused
601 $installer::globals
::allusedupdatesequences
{$sequence} = 1;
602 # Special help for files, that already have a "pff" component name (for example after ServicePack 1)
603 if ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} )
605 $infoline = "Warning: Special handling for component \"$pffcomponentname\". This file was added after the final, but before this ServicePack.\n";
606 push(@installer::globals
::logfileinfo
, $infoline);
607 $onefile->{'componentname'} = $pffcomponentname; # pff for "post final file"
608 $fileentry->{'Component_'} = $onefile->{'componentname'};
609 if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; }
614 $installer::globals
::updatesequencecounter
++;
615 $sequence = $installer::globals
::updatesequencecounter
;
616 $onefile->{'assignedsequencenumber'} = $sequence;
617 # $onefile->{'assignedcabinetfile'} = $installer::globals::pffcabfilename; # assigning to cabinet file for "post final files"
618 # Collecting all new files
619 $installer::globals
::newupdatefiles
{$sequence} = $onefile;
620 # Saving in sequence hash
621 $allupdatefileorderhashref->{$sequence} = $onefile->{'uniquename'};
623 # If the new file is part of an existing component, this must be changed now. All files
624 # of one component have to be included in one cabinet file. But because the order must
625 # not change, all new files have to be added to new components.
626 # $onefile->{'componentname'} = $file{'Component_'};
628 $onefile->{'componentname'} = $onefile->{'componentname'} . "_pff"; # pff for "post final file"
629 $fileentry->{'Component_'} = $onefile->{'componentname'};
630 if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; }
631 $onefile->{'PostFinalFile'} = 1;
632 # The sequence for this file has changed. It has to be inserted at the end of the files collector.
633 $installer::globals
::insert_file_at_end
= 1;
634 $installer::globals
::newfilescollector
{$sequence} = $onefile; # Adding new files to the end of the filescollector
635 $installer::globals
::newfilesexist
= 1;
641 # my $sequence = $number + 1;
643 # Idea: Each component is packed into a cab file.
644 # This requires that all files in one cab file have sequences directly following each other,
645 # for instance from 1456 to 1466. Then in the media table the LastSequence for this cab file
647 # Because all files belonging to one component are directly behind each other in the file
648 # collector, it is possible to use simply an increasing number as sequence value.
649 # If files belonging to one component are not directly behind each other in the files collector
650 # this mechanism will no longer work.
656 #############################################
657 # Returning the Windows language of a file
658 #############################################
660 sub get_language_for_file
666 if ( $fileref->{'specificlanguage'} ) { $language = $fileref->{'specificlanguage'}; }
668 if ( $language eq "" )
670 $language = 0; # language independent
671 # If this is not a font, the return value should be "0" (Check ICE 60)
673 if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; }
674 if ( $styles =~ /\bFONT\b/ ) { $language = ""; }
678 $language = installer
::windows
::language
::get_windows_language
($language);
684 ####################################################################
685 # Creating a new KeyPath for components in TemplatesFolder.
686 ####################################################################
688 sub generate_registry_keypath
692 my $keypath = $onefile->{'Name'};
694 $keypath = lc($keypath);
695 $keypath = "userreg_" . $keypath;
700 ####################################################################
701 # Check, if in an update process files are missing. No removal
702 # of files allowed for Windows Patch creation.
703 # Also logging all new files, that have to be included in extra
704 # components and cab files.
705 ####################################################################
707 sub check_file_sequences
709 my ($allupdatefileorderhashref, $allupdatecomponentorderhashref) = @_;
711 # All used sequences stored in %installer::globals::allusedupdatesequences
712 # Maximum sequence number of old database stored in $installer::globals::updatelastsequence
713 # All new files stored in %installer::globals::newupdatefiles
717 my @missing_sequences = ();
718 my @really_missing_sequences = ();
720 for ( my $i = 1; $i <= $installer::globals
::updatelastsequence
; $i++ )
722 if ( ! exists($installer::globals
::allusedupdatesequences
{$i}) ) { push(@missing_sequences, $i); }
725 if ( $#missing_sequences > -1 )
727 # Missing sequences can also be caused by files included in merge modules. This files are added later into the file table.
728 # Therefore now it is time to check the content of the merge modules.
730 for ( my $j = 0; $j <= $#missing_sequences; $j++ )
732 my $filename = $allupdatefileorderhashref->{$missing_sequences[$j]};
734 # Is this a file from a merge module? Then this is no error.
735 if ( ! exists($installer::globals
::mergemodulefiles
{$filename}) )
737 push(@really_missing_sequences, $missing_sequences[$j]);
742 if ( $#really_missing_sequences > -1 )
744 my $errorstring = "";
745 for ( my $j = 0; $j <= $#really_missing_sequences; $j++ )
747 my $filename = $allupdatefileorderhashref->{$really_missing_sequences[$j]};
748 my $comp = $allupdatecomponentorderhashref->{$really_missing_sequences[$j]};
749 $errorstring = "$errorstring$filename (Sequence: $really_missing_sequences[$j], Component: \"$comp\")\n";
752 $infoline = "ERROR: Files are removed compared with update database.\nThe following files are missing:\n$errorstring";
753 push(@installer::globals
::logfileinfo
, $infoline);
754 installer
::exiter
::exit_program
($infoline, "check_file_sequences");
757 # Searching for new files
761 foreach my $key ( keys %installer::globals
::newupdatefiles
)
763 my $onefile = $installer::globals
::newupdatefiles
{$key};
767 $infoline = "\nNew files compared to the update database:\n";
768 push(@installer::globals
::logfileinfo
, $infoline);
771 $infoline = "$onefile->{'Name'} ($onefile->{'gid'}) Sequence: $onefile->{'assignedsequencenumber'}\n";
772 push(@installer::globals
::logfileinfo
, $infoline);
777 $infoline = "Info: No new file compared with update database!\n";
778 push(@installer::globals
::logfileinfo
, $infoline);
783 ###################################################################
784 # Collecting further conditions for the component table.
785 # This is used by multilayer products, to enable installation
786 # of separate layers.
787 ###################################################################
789 sub get_tree_condition_for_component
791 my ($onefile, $componentname) = @_;
793 if ( $onefile->{'destination'} )
795 my $dest = $onefile->{'destination'};
797 # Comparing the destination path with
798 # $installer::globals::hostnametreestyles{$hostname} = $treestyle;
799 # (-> hostname is the key, the style the value!)
801 foreach my $hostname ( keys %installer::globals
::hostnametreestyles
)
803 if (( $dest eq $hostname ) || ( $dest =~ /^\s*\Q$hostname\E\\/ ))
805 # the value is the style
806 my $style = $installer::globals
::hostnametreestyles
{$hostname};
807 # the condition is saved in %installer::globals::treestyles
808 my $condition = $installer::globals
::treestyles
{$style};
809 # Saving condition to be added in table Property
810 $installer::globals
::usedtreeconditions
{$condition} = 1;
811 $condition = $condition . "=1";
812 # saving this condition
813 $installer::globals
::treeconditions
{$componentname} = $condition;
815 # saving also at the file, for usage in fileinfo
816 $onefile->{'layer'} = $installer::globals
::treelayername
{$style};
822 ############################################
823 # Collecting all short names, that are
824 # already used by the old database
825 ############################################
827 sub collect_shortnames_from_old_database
829 my ($uniquefilenamehashref, $shortnameshashref) = @_;
831 foreach my $key ( keys %{$uniquefilenamehashref} )
833 my $value = $uniquefilenamehashref->{$key}; # syntax of $value: ($uniquename;$shortname)
835 if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ )
837 my $shortstring = $2;
838 $shortnameshashref->{$shortstring} = 1; # adding the shortname to the array of all shortnames
843 ############################################
844 # Creating the file File.idt dynamically
845 ############################################
847 sub create_files_table
849 my ($filesref, $dirref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref) = @_;
851 installer
::logger
::include_timestamp_into_logfile
("Performance Info: File Table start");
853 # Structure of the files table:
854 # File Component_ FileName FileSize Version Language Attributes Sequence
855 # In this function, all components are created.
857 # $allfilecomponentsref is empty at the beginning
863 my @filehashtable = ();
864 my %allfilecomponents = ();
867 if ( $^O
=~ /cygwin/i ) { installer
::worker
::generate_cygwin_paths
($filesref); }
869 # The filenames must be collected because of uniqueness
870 # 01-44-~1.DAT, 01-44-~2.DAT, ...
873 if ( $installer::globals
::updatedatabase
) { collect_shortnames_from_old_database
($uniquefilenamehashref, \
%shortnames); }
875 installer
::windows
::idtglobal
::write_idt_header
(\
@filetable, "file");
876 installer
::windows
::idtglobal
::write_idt_header
(\
@filehashtable, "filehash");
877 installer
::windows
::idtglobal
::write_idt_header
(\
@installer::globals
::removefiletable
, "removefile");
879 for ( my $i = 0; $i <= $#{$filesref}; $i++ )
883 my $onefile = ${$filesref}[$i];
886 if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }
888 $file{'Component_'} = get_file_component_name
($onefile, $filesref);
889 $file{'File'} = generate_unique_filename_for_filetable
($onefile, $file{'Component_'}, $uniquefilenamehashref);
891 $onefile->{'uniquename'} = $file{'File'};
892 $onefile->{'componentname'} = $file{'Component_'};
894 # Collecting all components
896 if ( ! exists($allfilecomponents{$file{'Component_'}}) ) { $allfilecomponents{$file{'Component_'}} = 1; }
898 $file{'FileName'} = generate_filename_for_filetable
($onefile, \
%shortnames, $uniquefilenamehashref);
900 $file{'FileSize'} = get_filesize
($onefile);
902 $file{'Version'} = get_fileversion
($onefile, $allvariables, $styles);
904 $file{'Language'} = get_language_for_file
($onefile);
906 if ( $styles =~ /\bDONT_PACK\b/ ) { $file{'Attributes'} = "8192"; }
907 else { $file{'Attributes'} = "16384"; }
909 # $file{'Attributes'} = "16384"; # Sourcefile is packed
910 # $file{'Attributes'} = "8192"; # Sourcefile is unpacked
912 $installer::globals
::insert_file_at_end
= 0;
914 $file{'Sequence'} = get_sequence_for_file
($counter, $onefile, \
%file, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, \
%allfilecomponents);
916 $onefile->{'sequencenumber'} = $file{'Sequence'};
918 my $oneline = $file{'File'} . "\t" . $file{'Component_'} . "\t" . $file{'FileName'} . "\t"
919 . $file{'FileSize'} . "\t" . $file{'Version'} . "\t" . $file{'Language'} . "\t"
920 . $file{'Attributes'} . "\t" . $file{'Sequence'} . "\n";
922 push(@filetable, $oneline);
924 if ( $file{'File'} =~ /\.py$/ )
928 $removefile{'FileKey'} = "remove_" . $file{'File'} . "c";
929 $removefile{'Component_'} = $file{'Component_'};
930 $removefile{'FileName'} = $file{'FileName'};
931 $removefile{'FileName'} =~ s/\.py$/.pyc/;
932 $removefile{'FileName'} =~ s/\.PY\|/.PYC|/;
933 $removefile{'DirProperty'} = installer
::windows
::component
::get_file_component_directory
($file{'Component_'}, $filesref, $dirref);
934 $removefile{'InstallMode'} = 2; # msiInstallStateAbsent
935 $oneline = $removefile{'FileKey'} . "\t" . $removefile{'Component_'} . "\t" . $removefile{'FileName'} . "\t"
936 . $removefile{'DirProperty'} . "\t" . $removefile{'InstallMode'} . "\n";
938 push(@installer::globals
::removefiletable
, $oneline);
941 if ( ! $installer::globals
::insert_file_at_end
) { push(@allfiles, $onefile); }
943 # Collecting all component conditions
944 if ( $onefile->{'ComponentCondition'} )
946 if ( ! exists($installer::globals
::componentcondition
{$file{'Component_'}}))
948 $installer::globals
::componentcondition
{$file{'Component_'}} = $onefile->{'ComponentCondition'};
952 # Collecting also all tree conditions for multilayer products
953 get_tree_condition_for_component
($onefile, $file{'Component_'});
955 unless ( $file{'Version'} )
957 my $path = $onefile->{'sourcepath'};
958 if ( $^O
=~ /cygwin/i ) { $path = $onefile->{'cyg_sourcepath'}; }
960 open(FILE
, $path) or die "ERROR: Can't open $path for creating file hash";
962 my $hashinfo = pack("l", 20);
963 $hashinfo .= Digest
::MD5
->new->addfile(*FILE
)->digest;
965 my @i = unpack ('x[l]l4', $hashinfo);
966 $oneline = $file{'File'} . "\t" .
972 push (@filehashtable, $oneline);
975 # Saving the sequence number in a hash with uniquefilename as key.
976 # This is used for better performance in "save_packorder"
977 $installer::globals
::uniquefilenamesequence
{$onefile->{'uniquename'}} = $onefile->{'sequencenumber'};
980 if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; }
982 if ( $onefile->{'needs_user_registry_key'} )
984 my $keypath = generate_registry_keypath
($onefile);
985 $onefile->{'userregkeypath'} = $keypath;
986 push(@installer::globals
::userregistrycollector
, $onefile);
987 $installer::globals
::addeduserregitrykeys
= 1;
991 # putting content from %allfilecomponents to $allfilecomponentsref for later usage
992 foreach $localkey (keys %allfilecomponents ) { push( @
{$allfilecomponentsref}, $localkey); }
994 my $filetablename = $basedir . $installer::globals
::separator
. "File.idt";
995 installer
::files
::save_file
($filetablename ,\
@filetable);
996 $infoline = "\nCreated idt file: $filetablename\n";
997 push(@installer::globals
::logfileinfo
, $infoline);
999 installer
::logger
::include_timestamp_into_logfile
("Performance Info: File Table end");
1001 my $filehashtablename = $basedir . $installer::globals
::separator
. "MsiFileHash.idt";
1002 installer
::files
::save_file
($filehashtablename ,\
@filehashtable);
1003 $infoline = "\nCreated idt file: $filehashtablename\n";
1004 push(@installer::globals
::logfileinfo
, $infoline);
1006 # Now the new files can be added to the files collector (only in update packaging processes)
1007 if ( $installer::globals
::newfilesexist
)
1009 foreach my $seq (sort keys %installer::globals
::newfilescollector
) { push(@allfiles, $installer::globals
::newfilescollector
{$seq}) }