Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / solenv / bin / modules / installer / windows / file.pm
blobce345927d999608a26e7af4deb42e3f9b90e5ecc
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;
21 use Digest::MD5;
22 use installer::exiter;
23 use installer::files;
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 ) = @_;
43 my $infoline = "";
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;
60 else
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);
70 my $cabfile;
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
79 my $offset = 1;
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 = ();
120 my %allassigns = ();
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}) )
139 my %onecabfile = ();
140 $onecabfile{$onefile->{'destination'}} = 1;
141 $allassigns{$cabfilename} = \%onecabfile;
143 else
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!
151 my $cabfile;
152 foreach $cabfile ( sort keys %allassigns )
154 my $counter = $installer::globals::cabfilecounter{$cabfile};
155 my $dest;
156 foreach $dest ( sort keys %{$allassigns{$cabfile}} ) # <- sorting the destination!
158 my $directaccessnumber = $directaccess{$dest};
159 ${$filesref}[$directaccessnumber]->{'assignedsequencenumber'} = $counter;
160 $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
171 # -> no resetting of
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
204 my $styles = "";
205 if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; }
206 if ( $styles =~ /\bASSIGNCOMPOMENT\b/ )
208 $componentname = get_component_from_assigned_file($fileref->{'AssignComponent'}, $filesref);
210 else
212 # In this function exists the rule to create components from files
213 # Rule:
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};
268 else
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 = "";
372 my $counter = 0;
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
408 my $newname = 0;
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;
415 $newname = 1;
418 if ( ! $newname )
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;
427 $counter++;
429 if ( $uniquefilenamebase =~ /\./ )
431 $uniquefilename = $uniquefilenamebase;
432 $uniquefilename =~ s/\./$counter\./;
434 else
436 $uniquefilename = $uniquefilenamebase . $counter;
439 $newname = 0;
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;
447 $newname = 1;
450 until ( $newname )
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
472 my $shortstring;
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/.*;(.*)/;
484 if ($1 ne '')
486 $shortstring = $1;
488 else
490 $shortstring = installer::windows::idtglobal::make_eight_three_conform_with_hash($filename, "file", $shortnamesref);
493 else
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 #########################################
508 sub get_filesize
510 my ($fileref) = @_;
512 my $file = $fileref->{'sourcepath'};
514 my $filesize;
516 if ( -f $file ) # test of existence. For instance services.rdb does not always exist
518 $filesize = ( -s $file ); # file size can be "0"
520 else
522 $filesize = -1;
525 return $filesize;
528 #############################################
529 # Returning the file version, if required
530 # Sample: "8.0.1.8976";
531 #############################################
533 sub get_fileversion
535 my ($onefile, $allvariables, $styles) = @_;
537 my $fileversion = "";
539 if ( $onefile->{'Name'} =~ /\.bin$|\.com$|\.dll$|\.exe$|\.pyd$/ )
541 open (EXE, "<$onefile->{'sourcepath'}");
542 binmode EXE;
543 {local $/ = undef; $exedata = <EXE>;}
544 close 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;
555 return $fileversion;
558 #############################################
559 # Returning the sequence for a file
560 #############################################
562 sub get_sequence_for_file
564 my ($number, $onefile, $fileentry, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, $allfilecomponents) = @_;
566 my $sequence = "";
567 my $infoline = "";
568 my $pffcomponentname = $onefile->{'componentname'} . "_pff";
570 if ( $installer::globals::updatedatabase )
572 if (( exists($allupdatesequenceshashref->{$onefile->{'uniquename'}}) ) &&
573 (( $onefile->{'componentname'} eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} ) ||
574 ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} )))
576 # The second condition is necessary to find shifted files, that have same "uniquename", but are now
577 # located in another directory. This can be seen at the component name.
578 $sequence = $allupdatesequenceshashref->{$onefile->{'uniquename'}};
579 $onefile->{'assignedsequencenumber'} = $sequence;
580 # Collecting all used sequences, to guarantee, that no number is unused
581 $installer::globals::allusedupdatesequences{$sequence} = 1;
582 # Special help for files, that already have a "pff" component name (for example after ServicePack 1)
583 if ( $pffcomponentname eq $allupdatecomponentshashref->{$onefile->{'uniquename'}} )
585 $infoline = "Warning: Special handling for component \"$pffcomponentname\". This file was added after the final, but before this ServicePack.\n";
586 push(@installer::globals::logfileinfo, $infoline);
587 $onefile->{'componentname'} = $pffcomponentname; # pff for "post final file"
588 $fileentry->{'Component_'} = $onefile->{'componentname'};
589 if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; }
592 else
594 $installer::globals::updatesequencecounter++;
595 $sequence = $installer::globals::updatesequencecounter;
596 $onefile->{'assignedsequencenumber'} = $sequence;
597 # $onefile->{'assignedcabinetfile'} = $installer::globals::pffcabfilename; # assigning to cabinet file for "post final files"
598 # Collecting all new files
599 $installer::globals::newupdatefiles{$sequence} = $onefile;
600 # Saving in sequence hash
601 $allupdatefileorderhashref->{$sequence} = $onefile->{'uniquename'};
603 # If the new file is part of an existing component, this must be changed now. All files
604 # of one component have to be included in one cabinet file. But because the order must
605 # not change, all new files have to be added to new components.
606 # $onefile->{'componentname'} = $file{'Component_'};
608 $onefile->{'componentname'} = $onefile->{'componentname'} . "_pff"; # pff for "post final file"
609 $fileentry->{'Component_'} = $onefile->{'componentname'};
610 if ( ! exists($allfilecomponents->{$fileentry->{'Component_'}}) ) { $allfilecomponents->{$fileentry->{'Component_'}} = 1; }
611 $onefile->{'PostFinalFile'} = 1;
612 # The sequence for this file has changed. It has to be inserted at the end of the files collector.
613 $installer::globals::insert_file_at_end = 1;
614 $installer::globals::newfilescollector{$sequence} = $onefile; # Adding new files to the end of the filescollector
615 $installer::globals::newfilesexist = 1;
618 else
620 $sequence = $number;
621 # my $sequence = $number + 1;
623 # Idea: Each component is packed into a cab file.
624 # This requires that all files in one cab file have sequences directly follwing each other,
625 # for instance from 1456 to 1466. Then in the media table the LastSequence for this cab file
626 # is 1466.
627 # Because all files belonging to one component are directly behind each other in the file
628 # collector, it is possible to use simply an increasing number as sequence value.
629 # If files belonging to one component are not directly behind each other in the files collector
630 # this mechanism will no longer work.
633 return $sequence;
636 #############################################
637 # Returning the Windows language of a file
638 #############################################
640 sub get_language_for_file
642 my ($fileref) = @_;
644 my $language = "";
646 if ( $fileref->{'specificlanguage'} ) { $language = $fileref->{'specificlanguage'}; }
648 if ( $language eq "" )
650 $language = 0; # language independent
651 # If this is not a font, the return value should be "0" (Check ICE 60)
652 my $styles = "";
653 if ( $fileref->{'Styles'} ) { $styles = $fileref->{'Styles'}; }
654 if ( $styles =~ /\bFONT\b/ ) { $language = ""; }
656 else
658 $language = installer::windows::language::get_windows_language($language);
661 return $language;
664 ####################################################################
665 # Creating a new KeyPath for components in TemplatesFolder.
666 ####################################################################
668 sub generate_registry_keypath
670 my ($onefile) = @_;
672 my $keypath = $onefile->{'Name'};
673 $keypath =~ s/\.//g;
674 $keypath = lc($keypath);
675 $keypath = "userreg_" . $keypath;
677 return $keypath;
680 ####################################################################
681 # Check, if in an update process files are missing. No removal
682 # of files allowed for Windows Patch creation.
683 # Also logging all new files, that have to be included in extra
684 # components and cab files.
685 ####################################################################
687 sub check_file_sequences
689 my ($allupdatefileorderhashref, $allupdatecomponentorderhashref) = @_;
691 # All used sequences stored in %installer::globals::allusedupdatesequences
692 # Maximum sequence number of old database stored in $installer::globals::updatelastsequence
693 # All new files stored in %installer::globals::newupdatefiles
695 my $infoline = "";
697 my @missing_sequences = ();
698 my @really_missing_sequences = ();
700 for ( my $i = 1; $i <= $installer::globals::updatelastsequence; $i++ )
702 if ( ! exists($installer::globals::allusedupdatesequences{$i}) ) { push(@missing_sequences, $i); }
705 if ( $#missing_sequences > -1 )
707 # Missing sequences can also be caused by files included in merge modules. This files are added later into the file table.
708 # Therefore now it is time to check the content of the merge modules.
710 for ( my $j = 0; $j <= $#missing_sequences; $j++ )
712 my $filename = $allupdatefileorderhashref->{$missing_sequences[$j]};
714 # Is this a file from a merge module? Then this is no error.
715 if ( ! exists($installer::globals::mergemodulefiles{$filename}) )
717 push(@really_missing_sequences, $missing_sequences[$j]);
722 if ( $#really_missing_sequences > -1 )
724 my $errorstring = "";
725 for ( my $j = 0; $j <= $#really_missing_sequences; $j++ )
727 my $filename = $allupdatefileorderhashref->{$really_missing_sequences[$j]};
728 my $comp = $allupdatecomponentorderhashref->{$really_missing_sequences[$j]};
729 $errorstring = "$errorstring$filename (Sequence: $really_missing_sequences[$j], Component: \"$comp\")\n";
732 $infoline = "ERROR: Files are removed compared with update database.\nThe following files are missing:\n$errorstring";
733 push(@installer::globals::logfileinfo, $infoline);
734 installer::exiter::exit_program($infoline, "check_file_sequences");
737 # Searching for new files
739 my $counter = 0;
741 foreach my $key ( keys %installer::globals::newupdatefiles )
743 my $onefile = $installer::globals::newupdatefiles{$key};
744 $counter++;
745 if ( $counter == 1 )
747 $infoline = "\nNew files compared to the update database:\n";
748 push(@installer::globals::logfileinfo, $infoline);
751 $infoline = "$onefile->{'Name'} ($onefile->{'gid'}) Sequence: $onefile->{'assignedsequencenumber'}\n";
752 push(@installer::globals::logfileinfo, $infoline);
755 if ( $counter == 0 )
757 $infoline = "Info: No new file compared with update database!\n";
758 push(@installer::globals::logfileinfo, $infoline);
763 ###################################################################
764 # Collecting further conditions for the component table.
765 # This is used by multilayer products, to enable installation
766 # of separate layers.
767 ###################################################################
769 sub get_tree_condition_for_component
771 my ($onefile, $componentname) = @_;
773 if ( $onefile->{'destination'} )
775 my $dest = $onefile->{'destination'};
777 # Comparing the destination path with
778 # $installer::globals::hostnametreestyles{$hostname} = $treestyle;
779 # (-> hostname is the key, the style the value!)
781 foreach my $hostname ( keys %installer::globals::hostnametreestyles )
783 if (( $dest eq $hostname ) || ( $dest =~ /^\s*\Q$hostname\E\\/ ))
785 # the value is the style
786 my $style = $installer::globals::hostnametreestyles{$hostname};
787 # the condition is saved in %installer::globals::treestyles
788 my $condition = $installer::globals::treestyles{$style};
789 # Saving condition to be added in table Property
790 $installer::globals::usedtreeconditions{$condition} = 1;
791 $condition = $condition . "=1";
792 # saving this condition
793 $installer::globals::treeconditions{$componentname} = $condition;
795 # saving also at the file, for usage in fileinfo
796 $onefile->{'layer'} = $installer::globals::treelayername{$style};
802 ############################################
803 # Collecting all short names, that are
804 # already used by the old database
805 ############################################
807 sub collect_shortnames_from_old_database
809 my ($uniquefilenamehashref, $shortnameshashref) = @_;
811 foreach my $key ( keys %{$uniquefilenamehashref} )
813 my $value = $uniquefilenamehashref->{$key}; # syntax of $value: ($uniquename;$shortname)
815 if ( $value =~ /^\s*(.*?)\;\s*(.*?)\s*$/ )
817 my $shortstring = $2;
818 $shortnameshashref->{$shortstring} = 1; # adding the shortname to the array of all shortnames
823 ############################################
824 # Creating the file File.idt dynamically
825 ############################################
827 sub create_files_table
829 my ($filesref, $dirref, $allfilecomponentsref, $basedir, $allvariables, $uniquefilenamehashref, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref) = @_;
831 installer::logger::include_timestamp_into_logfile("Performance Info: File Table start");
833 # Structure of the files table:
834 # File Component_ FileName FileSize Version Language Attributes Sequence
835 # In this function, all components are created.
837 # $allfilecomponentsref is empty at the beginning
839 my $infoline;
841 my @allfiles = ();
842 my @filetable = ();
843 my @filehashtable = ();
844 my %allfilecomponents = ();
845 my $counter = 0;
847 if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_paths($filesref); }
849 # The filenames must be collected because of uniqueness
850 # 01-44-~1.DAT, 01-44-~2.DAT, ...
851 my %shortnames = ();
853 if ( $installer::globals::updatedatabase ) { collect_shortnames_from_old_database($uniquefilenamehashref, \%shortnames); }
855 installer::windows::idtglobal::write_idt_header(\@filetable, "file");
856 installer::windows::idtglobal::write_idt_header(\@filehashtable, "filehash");
857 installer::windows::idtglobal::write_idt_header(\@installer::globals::removefiletable, "removefile");
859 for ( my $i = 0; $i <= $#{$filesref}; $i++ )
861 my %file = ();
863 my $onefile = ${$filesref}[$i];
865 my $styles = "";
866 if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; }
868 $file{'Component_'} = get_file_component_name($onefile, $filesref);
869 $file{'File'} = generate_unique_filename_for_filetable($onefile, $file{'Component_'}, $uniquefilenamehashref);
871 $onefile->{'uniquename'} = $file{'File'};
872 $onefile->{'componentname'} = $file{'Component_'};
874 # Collecting all components
876 if ( ! exists($allfilecomponents{$file{'Component_'}}) ) { $allfilecomponents{$file{'Component_'}} = 1; }
878 $file{'FileName'} = generate_filename_for_filetable($onefile, \%shortnames, $uniquefilenamehashref);
880 $file{'FileSize'} = get_filesize($onefile);
882 $file{'Version'} = get_fileversion($onefile, $allvariables, $styles);
884 $file{'Language'} = get_language_for_file($onefile);
886 if ( $styles =~ /\bDONT_PACK\b/ ) { $file{'Attributes'} = "8192"; }
887 else { $file{'Attributes'} = "16384"; }
889 # $file{'Attributes'} = "16384"; # Sourcefile is packed
890 # $file{'Attributes'} = "8192"; # Sourcefile is unpacked
892 $installer::globals::insert_file_at_end = 0;
893 $counter++;
894 $file{'Sequence'} = get_sequence_for_file($counter, $onefile, \%file, $allupdatesequenceshashref, $allupdatecomponentshashref, $allupdatefileorderhashref, \%allfilecomponents);
896 $onefile->{'sequencenumber'} = $file{'Sequence'};
898 my $oneline = $file{'File'} . "\t" . $file{'Component_'} . "\t" . $file{'FileName'} . "\t"
899 . $file{'FileSize'} . "\t" . $file{'Version'} . "\t" . $file{'Language'} . "\t"
900 . $file{'Attributes'} . "\t" . $file{'Sequence'} . "\n";
902 push(@filetable, $oneline);
904 if ( $file{'File'} =~ /\.py$/ )
906 my %removefile = ();
908 $removefile{'FileKey'} = "remove_" . $file{'File'} . "c";
909 $removefile{'Component_'} = $file{'Component_'};
910 $removefile{'FileName'} = $file{'FileName'};
911 $removefile{'FileName'} =~ s/\.py$/.pyc/;
912 $removefile{'FileName'} =~ s/\.PY\|/.PYC|/;
913 $removefile{'DirProperty'} = installer::windows::component::get_file_component_directory($file{'Component_'}, $filesref, $dirref);
914 $removefile{'InstallMode'} = 2; # msiInstallStateAbsent
915 $oneline = $removefile{'FileKey'} . "\t" . $removefile{'Component_'} . "\t" . $removefile{'FileName'} . "\t"
916 . $removefile{'DirProperty'} . "\t" . $removefile{'InstallMode'} . "\n";
918 push(@installer::globals::removefiletable, $oneline);
921 if ( ! $installer::globals::insert_file_at_end ) { push(@allfiles, $onefile); }
923 # Collecting all component conditions
924 if ( $onefile->{'ComponentCondition'} )
926 if ( ! exists($installer::globals::componentcondition{$file{'Component_'}}))
928 $installer::globals::componentcondition{$file{'Component_'}} = $onefile->{'ComponentCondition'};
932 # Collecting also all tree conditions for multilayer products
933 get_tree_condition_for_component($onefile, $file{'Component_'});
935 unless ( $file{'Version'} )
937 my $path = $onefile->{'sourcepath'};
938 if ( $^O =~ /cygwin/i ) { $path = $onefile->{'cyg_sourcepath'}; }
940 open(FILE, $path) or die "ERROR: Can't open $path for creating file hash";
941 binmode(FILE);
942 my $hashinfo = pack("l", 20);
943 $hashinfo .= Digest::MD5->new->addfile(*FILE)->digest;
945 my @i = unpack ('x[l]l4', $hashinfo);
946 $oneline = $file{'File'} . "\t" .
947 "0" . "\t" .
948 $i[0] . "\t" .
949 $i[1] . "\t" .
950 $i[2] . "\t" .
951 $i[3] . "\n";
952 push (@filehashtable, $oneline);
955 # Saving the sequence number in a hash with uniquefilename as key.
956 # This is used for better performance in "save_packorder"
957 $installer::globals::uniquefilenamesequence{$onefile->{'uniquename'}} = $onefile->{'sequencenumber'};
959 my $destdir = "";
960 if ( $onefile->{'Dir'} ) { $destdir = $onefile->{'Dir'}; }
962 if ( $onefile->{'needs_user_registry_key'} )
964 my $keypath = generate_registry_keypath($onefile);
965 $onefile->{'userregkeypath'} = $keypath;
966 push(@installer::globals::userregistrycollector, $onefile);
967 $installer::globals::addeduserregitrykeys = 1;
971 # putting content from %allfilecomponents to $allfilecomponentsref for later usage
972 foreach $localkey (keys %allfilecomponents ) { push( @{$allfilecomponentsref}, $localkey); }
974 my $filetablename = $basedir . $installer::globals::separator . "File.idt";
975 installer::files::save_file($filetablename ,\@filetable);
976 $infoline = "\nCreated idt file: $filetablename\n";
977 push(@installer::globals::logfileinfo, $infoline);
979 installer::logger::include_timestamp_into_logfile("Performance Info: File Table end");
981 my $filehashtablename = $basedir . $installer::globals::separator . "MsiFileHash.idt";
982 installer::files::save_file($filehashtablename ,\@filehashtable);
983 $infoline = "\nCreated idt file: $filehashtablename\n";
984 push(@installer::globals::logfileinfo, $infoline);
986 # Now the new files can be added to the files collector (only in update packaging processes)
987 if ( $installer::globals::newfilesexist )
989 foreach my $seq (sort keys %installer::globals::newfilescollector) { push(@allfiles, $installer::globals::newfilescollector{$seq}) }
992 return \@allfiles;