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