cid#1607171 Data race condition
[LibreOffice.git] / solenv / bin / modules / installer / windows / directory.pm
blobb0231e34063e6260fa9a483fb82c23e9c4c50440
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::directory;
21 use strict;
22 use warnings;
24 use installer::exiter;
25 use installer::files;
26 use installer::globals;
27 use installer::pathanalyzer;
28 use installer::windows::idtglobal;
29 use installer::windows::msiglobal;
31 ##############################################################
32 # Collecting all directory trees in global hash
33 ##############################################################
35 my @msistandarddirectorynames = qw(
36 AdminToolsFolder
37 AppDataFolder
38 CommonAppDataFolder
39 CommonFiles64Folder
40 CommonFilesFolder
41 DesktopFolder
42 FavoritesFolder
43 FontsFolder
44 LocalAppDataFolder
45 MyPicturesFolder
46 NetHoodFolder
47 PersonalFolder
48 PrintHoodFolder
49 ProgramFiles64Folder
50 ProgramFilesFolder
51 ProgramMenuFolder
52 RecentFolder
53 SendToFolder
54 StartMenuFolder
55 StartupFolder
56 System16Folder
57 System64Folder
58 SystemFolder
59 TempFolder
60 TemplateFolder
61 WindowsFolder
62 WindowsVolume
65 sub collectdirectorytrees
67 my ( $directoryref ) = @_;
69 for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
71 my $onedir = ${$directoryref}[$i];
72 my $styles = "";
73 if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
75 if ( $styles ne "" )
77 foreach my $treestyle ( keys %installer::globals::treestyles )
79 if ( $styles =~ /\b$treestyle\b/ )
81 my $hostname = $onedir->{'HostName'};
82 # -> hostname is the key, the style the value!
83 $installer::globals::hostnametreestyles{$hostname} = $treestyle;
90 ##############################################################
91 # Overwriting global programfilesfolder, if required
92 ##############################################################
94 sub overwrite_programfilesfolder
96 my ( $allvariables ) = @_;
98 if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 ))
100 $installer::globals::programfilesfolder = "ProgramFiles64Folder";
104 ##############################################################
105 # Maximum length of directory name is 72.
106 # Taking care of underlines, which are the separator.
107 ##############################################################
109 sub make_short_dir_version
111 my ($longstring) = @_;
113 my $shortstring = "";
114 my $cutlength = 60;
115 my $length = 5; # So the directory can still be recognized
116 my $longstring_save = $longstring;
118 # Splitting the string at each "underline" and allowing only
119 # $length characters per directory name.
120 # Checking also uniqueness and length.
122 for my $onestring ( split /_\s*/, $longstring )
124 my $partstring = "";
126 if ( $onestring =~ /\-/ )
128 for my $onelocalstring ( split /-\s*/, $onestring )
130 if ( length($onelocalstring) > $length ) {
131 $onelocalstring = substr($onelocalstring, 0, $length);
133 $partstring .= "-" . $onelocalstring;
135 $partstring =~ s/^\s*\-//;
137 else
139 if ( length($onestring) > $length ) {
140 $partstring = substr($onestring, 0, $length);
142 else {
143 $partstring = $onestring;
147 $shortstring .= "_" . $partstring;
150 $shortstring =~ s/^\s*\_//;
152 # Setting unique ID to each directory
153 # No counter allowed, process must be absolute reproducible due to patch creation process.
155 # chomp(my $id = `echo $longstring_save | md5sum | sed -e "s/ .*//g"`); # Very, very slow
156 # my $subid = substr($id, 0, 9); # taking only the first 9 digits
158 my $subid = installer::windows::msiglobal::calculate_id($longstring_save, 9); # taking only the first 9 digits
160 if ( length($shortstring) > $cutlength ) { $shortstring = substr($shortstring, 0, $cutlength); }
162 $shortstring = $shortstring . "_" . $subid;
164 return $shortstring;
167 ##############################################################
168 # Adding unique directory names to the directory collection
169 ##############################################################
171 my $already_checked_the_frigging_directories_for_uniqueness = 0;
173 sub create_unique_directorynames
175 my ($directoryref, $allvariables) = @_;
177 my %completedirhashstep1 = ();
178 my %shortdirhash = ();
179 my %shortdirhashreverse = ();
180 my $infoline = "";
182 for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
184 my $onedir = ${$directoryref}[$i];
185 my $uniquename = $onedir->{'HostName'};
187 my $styles = "";
188 if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
190 $uniquename =~ s/^\s*//g; # removing beginning white spaces
191 $uniquename =~ s/\s*$//g; # removing ending white spaces
192 $uniquename =~ s/\s//g; # removing white spaces
193 $uniquename =~ s/\_//g; # removing existing underlines
194 $uniquename =~ s/\.//g; # removing dots in directoryname
195 $uniquename =~ s/\Q$installer::globals::separator\E/\_/g; # replacing slash and backslash with underline
196 $uniquename =~ s/OpenOffice/OO/g;
197 $uniquename =~ s/LibreOffice/LO/g;
198 $uniquename =~ s/_registry/_rgy/g;
199 $uniquename =~ s/_registration/_rgn/g;
200 $uniquename =~ s/_extension/_ext/g;
201 $uniquename =~ s/_frame/_frm/g;
202 $uniquename =~ s/_table/_tbl/g;
203 $uniquename =~ s/_chart/_crt/g;
204 $uniquename =~ s/_plat-linux/_plx/g;
206 # The names after this small changes must still be unique!
207 if ( exists($completedirhashstep1{$uniquename}) ) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 1): \"$uniquename\".", "create_unique_directorynames"); }
208 $completedirhashstep1{$uniquename} = 1;
210 # Starting to make unique name for the parent and its directory
211 my $originaluniquename = $uniquename;
213 $uniquename = make_short_dir_version($uniquename);
215 # Checking if the same directory already exists, but has another short version.
216 if (( exists($shortdirhash{$originaluniquename}) ) && ( $shortdirhash{$originaluniquename} ne $uniquename )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 2A): \"$uniquename\".", "create_unique_directorynames"); }
218 # Also checking vice versa
219 # Checking if the same short directory already exists, but has another long version.
220 if (( exists($shortdirhashreverse{$uniquename}) ) && ( $shortdirhashreverse{$uniquename} ne $originaluniquename )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 2B): \"$uniquename\".", "create_unique_directorynames"); }
222 # Creating assignment from long to short directory names
223 $shortdirhash{$originaluniquename} = $uniquename;
224 $shortdirhashreverse{$uniquename} = $originaluniquename;
226 # Important: The unique parent is generated from the string $originaluniquename (with the use of underlines).
228 my $uniqueparentname = $originaluniquename;
229 my $keepparent = 1;
231 if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ ) # the underline is now the separator
233 $uniqueparentname = $1;
234 $keepparent = 0;
236 else
238 $uniqueparentname = $installer::globals::programfilesfolder;
239 $keepparent = 1;
242 if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ )
244 $uniqueparentname = $installer::globals::programfilesfolder;
245 $keepparent = 1;
247 if ( $styles =~ /\bCOMMONFILESFOLDER\b/ )
249 $uniqueparentname = $installer::globals::commonfilesfolder;
250 $keepparent = 1;
252 if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ )
254 $uniqueparentname = $installer::globals::commonappdatafolder;
255 $keepparent = 1;
257 if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ )
259 $uniqueparentname = $installer::globals::localappdatafolder;
260 $keepparent = 1;
263 if ( $styles =~ /\bSHAREPOINTPATH\b/ )
265 $uniqueparentname = "SHAREPOINTPATH";
266 $installer::globals::usesharepointpath = 1;
267 $keepparent = 1;
270 # also setting short directory name for the parent
272 my $originaluniqueparentname = $uniqueparentname;
274 if ( ! $keepparent )
276 $uniqueparentname = make_short_dir_version($uniqueparentname);
279 # Again checking if the same directory already exists, but has another short version.
280 if (( exists($shortdirhash{$originaluniqueparentname}) ) && ( $shortdirhash{$originaluniqueparentname} ne $uniqueparentname )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 3A): \"$uniqueparentname\".", "create_unique_directorynames"); }
282 # Also checking vice versa
283 # Checking if the same short directory already exists, but has another long version.
284 if (( exists($shortdirhashreverse{$uniqueparentname}) ) && ( $shortdirhashreverse{$uniqueparentname} ne $originaluniqueparentname )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 3B): \"$uniqueparentname\".", "create_unique_directorynames"); }
286 $shortdirhash{$originaluniqueparentname} = $uniqueparentname;
287 $shortdirhashreverse{$uniqueparentname} = $originaluniqueparentname;
289 # Hyphen not allowed in database
290 $uniquename =~ s/\-/\_/g; # making "-" to "_"
291 $uniqueparentname =~ s/\-/\_/g; # making "-" to "_"
293 # And finally setting the values for the directories
294 $onedir->{'uniquename'} = $uniquename;
295 $onedir->{'uniqueparentname'} = $uniqueparentname;
297 # setting the installlocation directory
298 if ( $styles =~ /\bISINSTALLLOCATION\b/ )
300 if ( $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION already set: \"$installer::globals::installlocationdirectory\".", "create_unique_directorynames"); }
301 $installer::globals::installlocationdirectory = $uniquename;
302 $installer::globals::installlocationdirectoryset = 1;
307 #####################################################
308 # Adding ":." to selected default directory names
309 #####################################################
311 sub check_sourcedir_addon
313 my ( $onedir, $allvariableshashref ) = @_;
315 if (($installer::globals::languagepack) ||
316 ($installer::globals::helppack) ||
317 ($allvariableshashref->{'CHANGETARGETDIR'}))
319 my $sourcediraddon = "\:\.";
320 $onedir->{'defaultdir'} = $onedir->{'defaultdir'} . $sourcediraddon;
325 #####################################################
326 # The directory with the style ISINSTALLLOCATION
327 # will be replaced by INSTALLLOCATION
328 #####################################################
330 sub set_installlocation_directory
332 my ( $directoryref, $allvariableshashref ) = @_;
334 if ( ! $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION not set!", "set_installlocation_directory"); }
336 for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
338 my $onedir = ${$directoryref}[$i];
340 if ( $onedir->{'uniquename'} eq $installer::globals::installlocationdirectory )
342 $onedir->{'uniquename'} = "INSTALLLOCATION";
343 check_sourcedir_addon($onedir, $allvariableshashref);
346 if ( $onedir->{'uniquename'} eq $installer::globals::vendordirectory )
348 check_sourcedir_addon($onedir, $allvariableshashref);
351 if ( $onedir->{'uniqueparentname'} eq $installer::globals::installlocationdirectory )
353 $onedir->{'uniqueparentname'} = "INSTALLLOCATION";
358 #####################################################
359 # Getting the name of the top level directory. This
360 # can have only one letter
361 #####################################################
363 sub get_last_directory_name
365 my ($completepathref) = @_;
367 if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ )
369 $$completepathref = $1;
373 #####################################################
374 # Creating the defaultdir for the file Director.idt
375 #####################################################
377 sub create_defaultdir_directorynames
379 my ($directoryref, $shortdirnamehashref) = @_;
381 my @shortnames = ();
382 if ( $installer::globals::updatedatabase ) { @shortnames = values(%{$shortdirnamehashref}); }
383 elsif ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); }
385 for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
387 my $onedir = ${$directoryref}[$i];
388 my $hostname = $onedir->{'HostName'};
390 $hostname =~ s/\Q$installer::globals::separator\E\s*$//;
391 get_last_directory_name(\$hostname);
392 my $uniquename = $onedir->{'uniquename'};
393 my $shortstring;
394 if (( $installer::globals::updatedatabase ) && ( exists($shortdirnamehashref->{$uniquename}) ))
396 $shortstring = $shortdirnamehashref->{$uniquename};
398 elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) ))
400 $shortstring = $installer::globals::saved83dirmapping{$uniquename};
402 else
404 $shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames);
407 my $defaultdir;
409 if ( $shortstring eq $hostname )
411 $defaultdir = $hostname;
413 else
415 $defaultdir = $shortstring . "|" . $hostname;
418 $onedir->{'defaultdir'} = $defaultdir;
420 my $fontdir = "";
421 if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; }
423 my $fontdefaultdir = "";
424 if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; }
426 if (( $fontdir eq $installer::globals::fontsdirhostname ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname ))
428 $installer::globals::fontsdirname = $onedir->{'defaultdir'};
429 $installer::globals::fontsdirparent = $onedir->{'uniqueparentname'};
434 ###############################################
435 # Fill content into the directory table
436 ###############################################
438 sub create_directorytable_from_collection
440 my ($directorytableref, $directoryref) = @_;
442 for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
444 my $onedir = ${$directoryref}[$i];
445 my $hostname = $onedir->{'HostName'};
446 my $dir = "";
448 if ( $onedir->{'Dir'} ) { $dir = $onedir->{'Dir'}; }
450 if (( $dir eq "PREDEFINED_PROGDIR" ) && ( $hostname eq "" )) { next; } # removing files from root directory
452 my $oneline = $onedir->{'uniquename'} . "\t" . $onedir->{'uniqueparentname'} . "\t" . $onedir->{'defaultdir'} . "\n";
454 push(@{$directorytableref}, $oneline);
458 ###############################################
459 # Defining the root installation structure
460 ###############################################
462 sub add_root_directories
464 my ($directorytableref, $allvariableshashref, $onelanguage) = @_;
466 my $oneline = "";
468 if (( ! $installer::globals::languagepack ) && ( ! $installer::globals::helppack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} ))
470 my $productname;
472 $productname = $allvariableshashref->{'PRODUCTNAME'};
473 my $productversion = $allvariableshashref->{'PRODUCTVERSION'};
474 my $baseproductversion = $productversion;
476 if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} ))
478 $baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'}; # for example "2.0" for OOo
481 my $realproductkey = $productname;
482 my $productkey = $productname . " " . $baseproductversion;
484 if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} ))
486 $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
487 $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
489 if ( $allvariableshashref->{'NOVERSIONINDIRNAME'} )
491 $productkey = $productname;
492 $realproductkey = $productname;
494 if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} )
496 $productkey =~ s/\ /\_/g;
497 $realproductkey =~ s/\ /\_/g;
500 my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir"); # third parameter not used
501 $shortproductkey =~ s/\s/\_/g; # changing empty space to underline
503 $oneline = "$installer::globals::officemenufolder\t$installer::globals::programmenufolder\t$shortproductkey|$realproductkey\n";
504 push(@{$directorytableref}, $oneline);
507 $oneline = "TARGETDIR\t\tSourceDir\n";
508 push(@{$directorytableref}, $oneline);
510 $oneline = "WindowsFolder\tTARGETDIR\tWindows\n";
511 push(@{$directorytableref}, $oneline);
513 $oneline = "$installer::globals::programfilesfolder\tTARGETDIR\t.\n";
514 push(@{$directorytableref}, $oneline);
516 $oneline = "$installer::globals::programmenufolder\tTARGETDIR\t.\n";
517 push(@{$directorytableref}, $oneline);
519 $oneline = "$installer::globals::startupfolder\tTARGETDIR\t.\n";
520 push(@{$directorytableref}, $oneline);
522 $oneline = "$installer::globals::desktopfolder\tTARGETDIR\t.\n";
523 push(@{$directorytableref}, $oneline);
525 $oneline = "$installer::globals::startmenufolder\tTARGETDIR\t.\n";
526 push(@{$directorytableref}, $oneline);
528 $oneline = "$installer::globals::commonfilesfolder\tTARGETDIR\t.\n";
529 push(@{$directorytableref}, $oneline);
531 $oneline = "$installer::globals::commonappdatafolder\tTARGETDIR\t.\n";
532 push(@{$directorytableref}, $oneline);
534 $oneline = "$installer::globals::localappdatafolder\tTARGETDIR\t.\n";
535 push(@{$directorytableref}, $oneline);
537 if ( $installer::globals::usesharepointpath )
539 $oneline = "SHAREPOINTPATH\tTARGETDIR\t.\n";
540 push(@{$directorytableref}, $oneline);
543 my $localtemplatefoldername = $installer::globals::templatefoldername;
544 my $directorytableentry = $localtemplatefoldername;
545 my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir");
546 if ( $shorttemplatefoldername ne $localtemplatefoldername ) { $directorytableentry = "$shorttemplatefoldername|$localtemplatefoldername"; }
547 $oneline = "$installer::globals::templatefolder\tTARGETDIR\t$directorytableentry\n";
548 push(@{$directorytableref}, $oneline);
550 if ( $installer::globals::fontsdirname )
552 $oneline = "$installer::globals::fontsfolder\t$installer::globals::fontsdirparent\t$installer::globals::fontsfoldername\:$installer::globals::fontsdirname\n";
554 else
556 $oneline = "$installer::globals::fontsfolder\tTARGETDIR\t$installer::globals::fontsfoldername\n";
559 push(@{$directorytableref}, $oneline);
563 ###############################################
564 # Creating the file Director.idt dynamically
565 ###############################################
567 sub create_directory_table
569 my ($directoryref, $languagesarrayref, $basedir, $allvariableshashref, $shortdirnamehashref, $loggingdir) = @_;
571 # Structure of the directory table:
572 # Directory Directory_Parent DefaultDir
573 # Directory is a unique identifier
574 # Directory_Parent is the unique identifier of the parent
575 # DefaultDir is .:APPLIC~1|Application Data with
576 # Before ":" : [sourcedir]:[destdir] (not programmed yet)
577 # After ":" : 8+3 and not 8+3 the destination directory name
579 for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
581 my $onelanguage = ${$languagesarrayref}[$m];
582 $installer::globals::installlocationdirectoryset = 0;
584 my @directorytable = ();
585 my $infoline;
587 overwrite_programfilesfolder($allvariableshashref);
588 create_unique_directorynames($directoryref, $allvariableshashref);
589 $already_checked_the_frigging_directories_for_uniqueness++;
590 create_defaultdir_directorynames($directoryref, $shortdirnamehashref); # only destdir!
591 set_installlocation_directory($directoryref, $allvariableshashref);
592 installer::windows::idtglobal::write_idt_header(\@directorytable, "directory");
593 add_root_directories(\@directorytable, $allvariableshashref, $onelanguage);
594 create_directorytable_from_collection(\@directorytable, $directoryref);
596 # Saving the file
598 my $directorytablename = $basedir . $installer::globals::separator . "Director.idt" . "." . $onelanguage;
599 installer::files::save_file($directorytablename ,\@directorytable);
600 $infoline = "Created idt file: $directorytablename\n";
601 push(@installer::globals::logfileinfo, $infoline);
605 ################################################
606 # Check if the string starts with another string
607 ################################################
609 sub starts_with
611 my ($first, $second) = @_;
613 return substr($first, 0, length($second)) eq $second;
616 ###############################################
617 # Check if the directory prefix is a standard
618 # directory name. If it is the case, then the
619 # standard directory name is returned in $var.
620 ###############################################
622 sub has_standard_directory_prefix
624 my ($dir, $var) = @_;
626 for my $d (@msistandarddirectorynames) {
627 if (starts_with($dir, $d) && $dir ne $d) {
628 installer::logger::print_message("... match found: [$d]\n");
629 ${$var} = $d;
630 return 1;
634 return 0;