1#************************************************************** 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20#************************************************************** 21 22 23 24package installer::windows::directory; 25 26use installer::exiter; 27use installer::files; 28use installer::globals; 29use installer::pathanalyzer; 30use installer::windows::idtglobal; 31use installer::windows::msiglobal; 32use installer::scriptitems; 33 34use strict; 35 36############################################################## 37# Collecting all directory trees in global hash 38############################################################## 39 40sub collectdirectorytrees 41{ 42 my ( $directoryref ) = @_; 43 44 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 45 { 46 my $onedir = ${$directoryref}[$i]; 47 my $styles = ""; 48 if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; } 49 50 if ( $styles ne "" ) 51 { 52 foreach my $treestyle ( keys %installer::globals::treestyles ) 53 { 54 if ( $styles =~ /\b$treestyle\b/ ) 55 { 56 my $hostname = $onedir->{'HostName'}; 57 # -> hostname is the key, the style the value! 58 $installer::globals::hostnametreestyles{$hostname} = $treestyle; 59 } 60 } 61 } 62 } 63} 64 65############################################################## 66# Overwriting global programfilesfolder, if required 67############################################################## 68 69sub overwrite_programfilesfolder 70{ 71 my ( $allvariables ) = @_; 72 73 if ( $allvariables->{'PROGRAMFILESFOLDERNAME'} ) 74 { 75 $installer::globals::programfilesfolder = $allvariables->{'PROGRAMFILESFOLDERNAME'}; 76 } 77} 78 79 80 81 82=head2 make_short_dir_version($longstring) 83 84 Transform the given string into one that is at most 70 characters long. 85 That is done in two steps: 86 - Cut all parts separated by '_' or '-' down to a length of 5. 87 - Cut down the result to a length of 60 and fill it up to length 70 88 with the MD5 checksum. 89 90 This transform always returns the same result for the same string. 91 There is no counter and reference to a global set of names to make the string unique. 92 93=cut 94sub make_short_dir_version ($) 95{ 96 my ($longstring) = @_; 97 98 my $shortstring = ""; 99 my $cutlength = 60; 100 my $length = 5; # So the directory can still be recognized 101 my $longstring_save = $longstring; 102 103 # Splitting the string at each "underline" and allowing only $length characters per directory name. 104 # Checking also uniqueness and length. 105 106 my @outer_parts = split(/_/, $longstring); 107 foreach my $onestring (@outer_parts) 108 { 109 my $partstring = ""; 110 111 if ( $onestring =~ /\-/ ) 112 { 113 my @inner_parts = split(/-/, $onestring); 114 @inner_parts = map {substr($_,0,$length)} @inner_parts; 115 $partstring = join("-", @inner_parts); 116 $partstring =~ s/^\s*\-//; 117 } 118 else 119 { 120 $partstring = substr($onestring, 0, $length); 121 } 122 123 $shortstring .= "_" . $partstring; 124 } 125 126 $shortstring =~ s/^\s*\_//; 127 128 # Setting unique ID to each directory 129 # No counter allowed, process must be absolute reproducable due to patch creation process. 130 131 my $subid = installer::windows::msiglobal::calculate_id($longstring_save, 9); # taking only the first 9 digits 132 $shortstring = substr($shortstring, 0, $cutlength) . "_" . $subid; 133 134 return $shortstring; 135} 136 137 138 139 140=head2 get_unique_name ($hostname, $unique_map, $shortdirhash, $shortdirhashreverse) 141 142 Return a long and a short unique name for the given $hostname. 143 Despite the function name and unlike the generation of unique 144 names for files, the returned names are not really unique. Quite 145 the opposite. The returned names are quaranteed to return the 146 same result for the same input. 147 148 The returned short name has at most length 70. 149 150=cut 151sub get_unique_name ($$) 152{ 153 my ($hostname, $hostnamehash) = @_; 154 155 # Make sure that we where not called for this hostname before. Otherwise the other test would be triggered. 156 if (defined $hostnamehash->{$hostname}) 157 { 158 installer::exiter::exit_program( 159 "ERROR: get_unique_name was already called for hostname ".$hostname, 160 "get_unique_name"); 161 } 162 $hostnamehash->{$hostname} = 1; 163 164 my $uniquename = $hostname; 165 166 $uniquename =~ s/^\s*//g; # removing beginning white spaces 167 $uniquename =~ s/\s*$//g; # removing ending white spaces 168 $uniquename =~ s/\s//g; # removing white spaces 169 $uniquename =~ s/\_//g; # removing existing underlines 170 $uniquename =~ s/\.//g; # removing dots in directoryname 171 $uniquename =~ s/OpenOffice/OO/g; 172 173 $uniquename =~ s/\Q$installer::globals::separator\E/\_/g; # replacing slash and backslash with underline 174 175 $uniquename =~ s/_registry/_rgy/g; 176 $uniquename =~ s/_registration/_rgn/g; 177 $uniquename =~ s/_extension/_ext/g; 178 $uniquename =~ s/_frame/_frm/g; 179 $uniquename =~ s/_table/_tbl/g; 180 $uniquename =~ s/_chart/_crt/g; 181 182 my $short_uniquename = make_short_dir_version($uniquename); 183 184 return ($uniquename, $short_uniquename); 185} 186 187 188 189 190=head2 check_unique_directorynames($directories) 191 192 The one really important check is made in get_unique_name(). It 193 checks that get_unique_name() is not called twice for the same 194 directory host name. The tests in this function contain the 195 legacy tests that basically only check if there where a collision 196 of the partial MD5 sum that is used to make the short unique names 197 unique. 198 199 The maps $unique_map, $shortdirhash, $shortdirhashreverse are used 200 only to check that _different_ input names are mapped to different 201 results. They are not used to influence the result. That assumes 202 that this function is called only once for every directory 203 hostname. 204=cut 205sub check_unique_directorynames ($) 206{ 207 my ($directories) = @_; 208 209 my %completedirhashstep1 = (); 210 my %shortdirhash = (); 211 my %shortdirhashreverse = (); 212 213 # Check unique name of directories. 214 foreach my $directory (@$directories) 215 { 216 my ($long_uniquename, $short_uniquename) = ($directory->{'long_uniquename'}, $directory->{'uniquename'}); 217 218 # The names after this small changes must still be unique! 219 if (exists($completedirhashstep1{$long_uniquename})) 220 { 221 installer::exiter::exit_program( 222 sprintf("ERROR: Unallowed modification of directory name, not unique (step 1): \"%s\".", 223 $short_uniquename), 224 "check_unique_directorynames"); 225 } 226 $completedirhashstep1{$long_uniquename} = 1; 227 228 229 # Checking if the same directory already exists, but has another short version. 230 if (exists($shortdirhash{$long_uniquename}) 231 && ( $shortdirhash{$long_uniquename} ne $short_uniquename )) 232 { 233 installer::exiter::exit_program( 234 sprintf( 235 "ERROR: Unallowed modification of directory name, not unique (step 2A): \"%s\".", 236 $short_uniquename), 237 "check_unique_directorynames"); 238 } 239 $shortdirhash{$long_uniquename} = $short_uniquename; 240 241 # Also checking vice versa 242 # Checking if the same short directory already exists, but has another long version. 243 if (exists($shortdirhashreverse{$short_uniquename}) 244 && ( $shortdirhashreverse{$short_uniquename} ne $long_uniquename )) 245 { 246 installer::exiter::exit_program( 247 sprintf( 248 "ERROR: Unallowed modification of directory name, not unique (step 2B): \"%s\".", 249 $short_uniquename), 250 "check_unique_directorynames"); 251 } 252 $shortdirhashreverse{$short_uniquename} = $long_uniquename; 253 } 254 255 # Check unique name of parents 256 foreach my $directory (@$directories) 257 { 258 my ($long_uniquename, $short_uniquename) 259 = ($directory->{'long_uniqueparentname'}, $directory->{'uniqueparentname'}); 260 261 # Again checking if the same directory already exists, but has another short version. 262 if (exists($shortdirhash{$long_uniquename}) 263 && ( $shortdirhash{$long_uniquename} ne $short_uniquename )) 264 { 265 installer::exiter::exit_program( 266 sprintf( 267 "ERROR: Unallowed modification of directory name, not unique (step 3A): \"%s\".", 268 $short_uniquename), 269 "check_unique_directorynames"); 270 } 271 $shortdirhash{$long_uniquename} = $short_uniquename; 272 273 # Also checking vice versa 274 # Checking if the same short directory already exists, but has another long version. 275 if (exists($shortdirhashreverse{$short_uniquename}) 276 && ( $shortdirhashreverse{$short_uniquename} ne $long_uniquename )) 277 { 278 installer::exiter::exit_program( 279 sprintf( 280 "ERROR: Unallowed modification of directory name, not unique (step 3B): \"%s\".", 281 $short_uniquename), 282 "check_unique_directorynames"); 283 } 284 $shortdirhashreverse{$short_uniquename} = $long_uniquename; 285 } 286} 287 288 289 290 291sub get_unique_parent_name ($$) 292{ 293 my ($uniqueparentname, $styles) = @_; 294 295 my $keepparent = 1; 296 297 if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ ) # the underline is now the separator 298 { 299 $uniqueparentname = $1; 300 $keepparent = 0; 301 } 302 else 303 { 304 $uniqueparentname = $installer::globals::programfilesfolder; 305 $keepparent = 1; 306 } 307 308 if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ ) 309 { 310 $uniqueparentname = $installer::globals::programfilesfolder; 311 $keepparent = 1; 312 } 313 if ( $styles =~ /\bCOMMONFILESFOLDER\b/ ) 314 { 315 $uniqueparentname = $installer::globals::commonfilesfolder; 316 $keepparent = 1; 317 } 318 if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ ) 319 { 320 $uniqueparentname = $installer::globals::commonappdatafolder; 321 $keepparent = 1; 322 } 323 if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ ) 324 { 325 $uniqueparentname = $installer::globals::localappdatafolder; 326 $keepparent = 1; 327 } 328 329 if ( $styles =~ /\bSHAREPOINTPATH\b/ ) 330 { 331 $uniqueparentname = "SHAREPOINTPATH"; 332 $installer::globals::usesharepointpath = 1; 333 $keepparent = 1; 334 } 335 336 # also setting short directory name for the parent 337 338 my $originaluniqueparentname = $uniqueparentname; 339 340 if ( ! $keepparent ) 341 { 342 $uniqueparentname = make_short_dir_version($uniqueparentname); 343 } 344 345 return ($originaluniqueparentname, $uniqueparentname); 346} 347 348 349 350 351############################################################## 352# Adding unique directory names to the directory collection 353############################################################## 354 355sub create_unique_directorynames ($) 356{ 357 my ($directories) = @_; 358 359 $installer::globals::officeinstalldirectoryset = 0; 360 361 my %hostnamehash = (); 362 my $infoline = ""; 363 my $errorcount = 0; 364 365 foreach my $directory (@$directories) 366 { 367 next if defined $directory->{'uniquename'}; 368 369 my $styles = $directory->{'Styles'}; 370 $styles = "" unless defined $styles; 371 372 my ($originaluniquename, $uniquename) = get_unique_name( 373 $directory->{'HostName'}, 374 \%hostnamehash); 375 376 my ($originaluniqueparentname, $uniqueparentname) = get_unique_parent_name( 377 $originaluniquename, 378 $styles); 379 380 381 # Hyphen not allowed in database 382 $uniquename =~ s/\-/\_/g; # making "-" to "_" 383 $uniqueparentname =~ s/\-/\_/g; # making "-" to "_" 384 385 # And finally setting the values for the directories 386 $directory->{'uniquename'} = $uniquename; 387 $directory->{'uniqueparentname'} = $uniqueparentname; 388 $directory->{'long_uniquename'} = $originaluniquename; 389 $directory->{'long_uniqueparentname'} = $originaluniqueparentname; 390 } 391 392 # Find the installation directory. 393 foreach my $directory (@$directories) 394 { 395 next unless defined $directory->{'Styles'}; 396 397 # setting the installlocation directory 398 next unless $directory->{'Styles'} =~ /\bISINSTALLLOCATION\b/; 399 400 if ( $installer::globals::installlocationdirectoryset ) 401 { 402 installer::exiter::exit_program( 403 sprintf( 404 "ERROR: Directory with flag ISINSTALLLOCATION alread set: \"%s\".", 405 $installer::globals::installlocationdirectory), 406 "create_unique_directorynames"); 407 } 408 409 $installer::globals::installlocationdirectory = $directory->{'uniquename'}; 410 $installer::globals::installlocationdirectoryset = 1; 411 } 412} 413 414 415 416 417##################################################### 418# Adding ":." to selected default directory names 419##################################################### 420 421sub update_defaultdir ($$) 422{ 423 my ( $onedir, $allvariableshashref ) = @_; 424 425 if ($installer::globals::addchildprojects 426 || $installer::globals::patch 427 || $installer::globals::languagepack 428 || $allvariableshashref->{'CHANGETARGETDIR'}) 429 { 430 my $sourcediraddon = "\:\."; 431 return $onedir->{'defaultdir'} . $sourcediraddon; 432 } 433 else 434 { 435 return $onedir->{'defaultdir'}; 436 } 437} 438 439##################################################### 440# The directory with the style ISINSTALLLOCATION 441# will be replaced by INSTALLLOCATION 442##################################################### 443 444sub set_installlocation_directory 445{ 446 my ( $directoryref, $allvariableshashref ) = @_; 447 448 if ( ! $installer::globals::installlocationdirectoryset ) 449 { 450 installer::exiter::exit_program( 451 "ERROR: Directory with flag ISINSTALLLOCATION not set!", 452 "set_installlocation_directory"); 453 } 454 455 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 456 { 457 my $onedir = ${$directoryref}[$i]; 458 459 if ( $onedir->{'uniquename'} eq $installer::globals::installlocationdirectory ) 460 { 461 $onedir->{'uniquename'} = "INSTALLLOCATION"; 462 $onedir->{'defaultdir'} = update_defaultdir($onedir, $allvariableshashref); 463 } 464 465 if ( $onedir->{'uniquename'} eq $installer::globals::vendordirectory ) 466 { 467 $onedir->{'defaultdir'} = update_defaultdir($onedir, $allvariableshashref); 468 } 469 470 if ( $onedir->{'uniqueparentname'} eq $installer::globals::installlocationdirectory ) 471 { 472 $onedir->{'uniqueparentname'} = "INSTALLLOCATION"; 473 } 474 } 475} 476 477##################################################### 478# Getting the name of the top level directory. This 479# can have only one letter 480##################################################### 481 482sub get_last_directory_name 483{ 484 my ($completepathref) = @_; 485 486 if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ ) 487 { 488 $$completepathref = $1; 489 } 490} 491 492sub setup_global_font_directory_name ($) 493{ 494 my ($directories) = @_; 495 496 $installer::logger::Info->printf("setup_global_font_directory_name, checking %d entries\n", 497 scalar @$directories); 498 $installer::logger::Info->printf(" fontsdirhostname is %s\n", 499 $installer::globals::fontsdirhostname); 500 $installer::logger::Info->printf(" fontsdirname is %s\n", 501 $installer::globals::fontsdirname); 502 foreach my $directory (@$directories) 503 { 504 next unless defined $directory->{'Dir'}; 505 next unless defined $directory->{'defaultdir'}; 506 507 $installer::logger::Info->printf(" Dir is %s\n", $directory->{'Dir'}); 508 $installer::logger::Info->printf(" defaultdir is %s\n", $directory->{'defaultdir'}); 509 next if $directory->{'Dir'} ne "PREDEFINED_OSSYSTEMFONTDIR"; 510 next if $directory->{'defaultdir'} ne $installer::globals::fontsdirhostname; 511 512 $installer::globals::fontsdirname = $installer::globals::fontsdirhostname; 513 $installer::globals::fontsdirparent = $directory->{'uniqueparentname'}; 514 515 $installer::logger::Info->printf("%s, fdhn %s, dd %s, ipn %s, HN %s\n", 516 "PREDEFINED_OSSYSTEMFONTDIR", 517 $installer::globals::fontsdirhostname, 518 $directory->{'defaultdir'}, 519 $directory->{'uniqueparentname'}, 520 $directory->{'HostName'}); 521 installer::scriptitems::print_script_item($directory); 522 } 523 foreach my $onedir (@$directories) 524 { 525 my $fontdir = ""; 526 if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; } 527 528 my $fontdefaultdir = ""; 529 if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; } 530 531 if (( $fontdir eq "PREDEFINED_OSSYSTEMFONTDIR" ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname )) 532 { 533 $installer::logger::Info->printf("fontsdirname = %s\n", $onedir->{'defaultdir'}); 534 $installer::logger::Info->printf(" is %s\n", $installer::globals::fontsdirname); 535 $installer::logger::Info->printf("fontsdirparent = %s\n", $onedir->{'uniqueparentname'}); 536 $installer::logger::Info->printf(" is %s\n", $installer::globals::fontsdirparent); 537 } 538 } 539} 540 541##################################################### 542# Creating the defaultdir for the file Director.idt 543##################################################### 544 545sub create_defaultdir_directorynames ($) 546{ 547 my ($directoryref) = @_; 548 549 my @shortnames = (); 550 if ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); } 551 552 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 553 { 554 my $onedir = ${$directoryref}[$i]; 555 my $hostname = $onedir->{'HostName'}; 556 557 $hostname =~ s/\Q$installer::globals::separator\E\s*$//; 558 get_last_directory_name(\$hostname); 559 # installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$hostname); # making program/classes to classes 560 my $uniquename = $onedir->{'uniquename'}; 561 my $shortstring; 562 if (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) )) 563 { 564 $shortstring = $installer::globals::saved83dirmapping{$uniquename}; 565 } 566 else 567 { 568 $shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames); 569 } 570 571 my $defaultdir; 572 573 if ( $shortstring eq $hostname ) 574 { 575 $defaultdir = $hostname; 576 } 577 else 578 { 579 $defaultdir = $shortstring . "|" . $hostname; 580 } 581 582 $onedir->{'defaultdir'} = $defaultdir; 583 } 584} 585 586############################################### 587# Fill content into the directory table 588############################################### 589 590sub create_directorytable_from_collection ($$) 591{ 592 my ($directorytableref, $directoryref) = @_; 593 594 foreach my $onedir (@$directoryref) 595 { 596 # Remove entries for special directories. 597 if (defined $onedir->{'HostName'} 598 && $onedir->{'HostName'} eq "" 599 && defined $onedir->{'Dir'} 600 && $onedir->{'Dir'} eq "PREDEFINED_PROGDIR") 601 { 602 next; 603 } 604 605 my $oneline = sprintf( 606 "%s\t%s\t%s\n", 607 $onedir->{'uniquename'}, 608 $onedir->{'uniqueparentname'}, 609 $onedir->{'defaultdir'}); 610 611 push @{$directorytableref}, $oneline; 612 } 613} 614 615############################################### 616# Defining the root installation structure 617############################################### 618 619sub process_root_directories ($$) 620{ 621 my ($allvariableshashref, $functor) = @_; 622 623 my $oneline = ""; 624 625 if (( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} )) 626 { 627 my $productname = $allvariableshashref->{'PRODUCTNAME'}; 628 my $productversion = $allvariableshashref->{'PRODUCTVERSION'}; 629 my $baseproductversion = $productversion; 630 631 if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} )) 632 { 633 $baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'}; # for example "2.0" for OOo 634 } 635 636 my $realproductkey = $productname . " " . $productversion; 637 my $productkey = $productname . " " . $baseproductversion; 638 639 if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} )) 640 { 641 $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; 642 $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; 643 } 644 if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} ) 645 { 646 $productkey =~ s/\ /\_/g; 647 $realproductkey =~ s/\ /\_/g; 648 } 649 650 my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir", undef); 651 $shortproductkey =~ s/\s/\_/g; # changing empty space to underline 652 653 &$functor( 654 $installer::globals::officemenufolder, 655 $installer::globals::programmenufolder, 656 $shortproductkey . "|". $realproductkey); 657 } 658 659 &$functor("TARGETDIR", "", "SourceDir"); 660 &$functor($installer::globals::programfilesfolder, "TARGETDIR", "."); 661 &$functor($installer::globals::programmenufolder, "TARGETDIR", "."); 662 &$functor($installer::globals::startupfolder, "TARGETDIR", "."); 663 &$functor($installer::globals::desktopfolder, "TARGETDIR", "."); 664 &$functor($installer::globals::startmenufolder, "TARGETDIR", "."); 665 &$functor($installer::globals::commonfilesfolder, "TARGETDIR", "."); 666 &$functor($installer::globals::commonappdatafolder, "TARGETDIR", "."); 667 &$functor($installer::globals::localappdatafolder, "TARGETDIR", "."); 668 669 if ( $installer::globals::usesharepointpath ) 670 { 671 &$functor("SHAREPOINTPATH", "TARGETDIR", "."); 672 } 673 674 &$functor($installer::globals::systemfolder, "TARGETDIR", "."); 675 676 my $localtemplatefoldername = $installer::globals::templatefoldername; 677 my $directorytableentry = $localtemplatefoldername; 678 my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir"); 679 if ( $shorttemplatefoldername ne $localtemplatefoldername ) 680 { 681 $directorytableentry = $shorttemplatefoldername . "|" . $localtemplatefoldername; 682 } 683 &$functor($installer::globals::templatefolder, "TARGETDIR", $directorytableentry); 684 685 $installer::logger::Info->printf("process_root_directories: fontsdirname=%s, fontsfoldername=%s\n", 686 $installer::globals::fontsdirname, 687 $installer::globals::fontsfoldername); 688 if ( $installer::globals::fontsdirname ) 689 { 690 &$functor( 691 $installer::globals::fontsfolder, 692 $installer::globals::fontsdirparent, 693 $installer::globals::fontsfoldername . ":" . $installer::globals::fontsdirname); 694 } 695 else 696 { 697 &$functor( 698 $installer::globals::fontsfolder, 699 "TARGETDIR", 700 $installer::globals::fontsfoldername); 701 } 702} 703 704 705 706 707sub find_missing_directories ($$) 708{ 709 my ($directories, $allvariableshashref) = @_; 710 711 # Set up the list of target directories. 712 my %target_directories = map {$_->{'uniquename'} => 1} @$directories; 713 # Add special directories. 714 process_root_directories( 715 $allvariableshashref, 716 sub($$$){ 717 my ($uniquename, $parentname, $defaultdir) = @_; 718 $target_directories{$uniquename} = 1; 719 } 720 ); 721 722 # Set up the list of source directories. 723 my $source_directory_map = $installer::globals::source_msi->GetDirectoryMap(); 724 my $source_file_map = $installer::globals::source_msi->GetFileMap(); 725 my %source_directories = map {$_->{'unique_name'} => $_} values %$source_directory_map; 726 727 # Find the missing source directories. 728 my @missing_directories = (); 729 foreach my $source_uniquename (keys %source_directories) 730 { 731 if ( ! $target_directories{$source_uniquename}) 732 { 733 push @missing_directories, $source_directories{$source_uniquename}; 734 } 735 } 736 737 # Report the missing directories. 738 $installer::logger::Info->printf("found %d missing directories\n", scalar @missing_directories); 739 my $index = 0; 740 foreach my $directory_item (@missing_directories) 741 { 742 # Print information about the directory. 743 $installer::logger::Info->printf("missing directory %d: %s\n", 744 ++$index, 745 $directory_item->{'full_target_long_name'}); 746 while (my($key,$value) = each %$directory_item) 747 { 748 $installer::logger::Info->printf(" %s -> %s\n", $key, $value); 749 } 750 751 # Print the referencing files. 752 my @filenames = (); 753 while (my ($key,$value) = each %$source_file_map) 754 { 755 if ($value->{'directory'}->{'unique_name'} eq $directory_item->{'unique_name'}) 756 { 757 push @filenames, $key; 758 } 759 } 760 $installer::logger::Info->printf(" referencing files are %s\n", join(", ", @filenames)); 761 } 762 763 foreach my $directory (@$directories) 764 { 765 $installer::logger::Lang->printf("target directory %s -> HN %s\n", 766 $directory->{'uniquename'}, 767 $directory->{'HostName'}); 768 installer::scriptitems::print_script_item($directory); 769 } 770 771 # Setup a map of directory uniquenames to verify that the new 772 # entries don't use unique names that are already in use. 773 my %unique_names = map {$_->{'uniquename'} => $_} @$directories; 774 775 # Create script items for the missing directories. 776 my @new_source_directories = (); 777 foreach my $source_directory_item (@missing_directories) 778 { 779 my $new_directory_item = { 780 'uniquename' => $source_directory_item->{'unique_name'}, 781 'uniqueparentname' => $source_directory_item->{'parent_name'}, 782 'defaultdir' => $source_directory_item->{'default_dir'}, 783 'HostName' => $source_directory_item->{'full_target_long_name'}, 784 'componentname' => $source_directory_item->{'component_name'}, 785 }; 786 787 if (defined $unique_names{$new_directory_item->{'uniquename'}}) 788 { 789 installer::logger::PrintError("newly created directory entry collides with existing directory"); 790 last; 791 } 792 793 push @new_source_directories, $new_directory_item; 794 } 795 796 return @new_source_directories; 797} 798 799 800 801 802sub prepare_directory_table_creation ($$) 803{ 804 my ($directories, $allvariableshashref) = @_; 805 806 foreach my $directory (@$directories) 807 { 808 delete $directory->{'uniquename'}; 809 } 810 811 overwrite_programfilesfolder($allvariableshashref); 812 create_unique_directorynames($directories); 813 check_unique_directorynames($directories); 814 create_defaultdir_directorynames($directories); # only destdir! 815 setup_global_font_directory_name($directories); 816 set_installlocation_directory($directories, $allvariableshashref); 817 818 if ($installer::globals::is_release) 819 { 820 my @new_directories = find_missing_directories($directories, $allvariableshashref); 821 push @$directories, @new_directories; 822 } 823} 824 825 826 827 828############################################### 829# Creating the file Director.idt dynamically 830############################################### 831 832sub create_directory_table ($$$) 833{ 834 my ($directoryref, $basedir, $allvariableshashref) = @_; 835 836 # Structure of the directory table: 837 # Directory Directory_Parent DefaultDir 838 # Directory is a unique identifier 839 # Directory_Parent is the unique identifier of the parent 840 # DefaultDir is .:APPLIC~1|Application Data with 841 # Before ":" : [sourcedir]:[destdir] (not programmed yet) 842 # After ":" : 8+3 and not 8+3 the destination directory name 843 844 $installer::logger::Lang->add_timestamp("Performance Info: Directory Table start"); 845 846 my @directorytable = (); 847 installer::windows::idtglobal::write_idt_header(\@directorytable, "directory"); 848 849 # Add entries for the root directories (and a few special directories like that for fonts). 850 process_root_directories( 851 $allvariableshashref, 852 sub($$$){ 853 push(@directorytable, join("\t", @_)."\n"); 854 } 855 ); 856 857 # Add entries for the non-root directories. 858 create_directorytable_from_collection(\@directorytable, $directoryref); 859 860 # Saving the file 861 862 my $directorytablename = $basedir . $installer::globals::separator . "Director.idt"; 863 installer::files::save_file($directorytablename ,\@directorytable); 864 $installer::logger::Lang->printf("Created idt file: %s\n", $directorytablename); 865 866 $installer::logger::Lang->add_timestamp("Performance Info: Directory Table end"); 867} 868 869 8701; 871