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