merge the formfield patch from ooo-build
[ooovba.git] / solenv / bin / modules / installer / windows / update.pm
blobe3a17bf5db17780b80c230be8c97155eddd375f8
1 #*************************************************************************
3 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 #
5 # Copyright 2008 by Sun Microsystems, Inc.
7 # OpenOffice.org - a multi-platform office productivity suite
9 # $RCSfile: update.pm,v $
11 # $Revision: 1.2 $
13 # This file is part of OpenOffice.org.
15 # OpenOffice.org is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU Lesser General Public License version 3
17 # only, as published by the Free Software Foundation.
19 # OpenOffice.org is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU Lesser General Public License version 3 for more details
23 # (a copy is included in the LICENSE file that accompanied this code).
25 # You should have received a copy of the GNU Lesser General Public License
26 # version 3 along with OpenOffice.org. If not, see
27 # <http://www.openoffice.org/license.html>
28 # for a copy of the LGPLv3 License.
30 #*************************************************************************
32 package installer::windows::update;
34 use installer::converter;
35 use installer::exiter;
36 use installer::files;
37 use installer::globals;
38 use installer::pathanalyzer;
39 use installer::systemactions;
41 #################################################################################
42 # Extracting all tables from an msi database
43 #################################################################################
45 sub extract_all_tables_from_msidatabase
47 my ($fulldatabasepath, $workdir) = @_;
49 my $msidb = "msidb.exe"; # Has to be in the path
50 my $infoline = "";
51 my $systemcall = "";
52 my $returnvalue = "";
53 my $extraslash = ""; # Has to be set for non-ActiveState perl
55 # Export of all tables by using "*"
57 if ( $^O =~ /cygwin/i ) {
58 # msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
59 $fulldatabasepath =~ s/\//\\\\/g;
60 $workdir =~ s/\//\\\\/g;
61 $extraslash = "\\";
64 $systemcall = $msidb . " -d " . $fulldatabasepath . " -f " . $workdir . " -e " . $extraslash . "*";
65 $returnvalue = system($systemcall);
67 $infoline = "Systemcall: $systemcall\n";
68 push( @installer::globals::logfileinfo, $infoline);
70 if ($returnvalue)
72 $infoline = "ERROR: Could not execute $systemcall !\n";
73 push( @installer::globals::logfileinfo, $infoline);
74 installer::exiter::exit_program("ERROR: Could not exclude tables from msi database: $fulldatabasepath !", "extract_all_tables_from_msidatabase");
76 else
78 $infoline = "Success: Executed $systemcall successfully!\n";
79 push( @installer::globals::logfileinfo, $infoline);
83 #################################################################################
84 # Collecting the keys from the first line of the idt file
85 #################################################################################
87 sub collect_all_keys
89 my ($line) = @_;
91 my @allkeys = ();
92 my $rownumber = 0;
93 my $onekey = "";
95 while ( $line =~ /^\s*(\S+?)\t(.*)$/ )
97 $onekey = $1;
98 $line = $2;
99 $rownumber++;
100 push(@allkeys, $onekey);
103 # and the last key
105 $onekey = $line;
106 $onekey =~ s/^\s*//g;
107 $onekey =~ s/\s*$//g;
109 $rownumber++;
110 push(@allkeys, $onekey);
112 return (\@allkeys, $rownumber);
115 #################################################################################
116 # Analyzing the content of one line of an idt file
117 #################################################################################
119 sub get_oneline_hash
121 my ($line, $allkeys, $rownumber) = @_;
123 my $counter = 0;
124 my %linehash = ();
126 $line =~ s/^\s*//;
127 $line =~ s/\s*$//;
129 my $value = "";
130 my $onekey = "";
132 while ( $line =~ /^(.*?)\t(.*)$/ )
134 $value = $1;
135 $line = $2;
136 $onekey = ${$allkeys}[$counter];
137 $linehash{$onekey} = $value;
138 $counter++;
141 # the last column
143 $value = $line;
144 $onekey = ${$allkeys}[$counter];
146 $linehash{$onekey} = $value;
148 return \%linehash;
151 #################################################################################
152 # Analyzing the content of an idt file
153 #################################################################################
155 sub analyze_idt_file
157 my ($filecontent) = @_;
159 my %table = ();
160 # keys are written in first line
161 my ($allkeys, $rownumber) = collect_all_keys(${$filecontent}[0]);
163 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
165 if (( $i == 0 ) || ( $i == 1 ) || ( $i == 2 )) { next; }
167 my $onelinehash = get_oneline_hash(${$filecontent}[$i], $allkeys, $rownumber);
168 my $linekey = $i - 2; # ! : The linenumber is the unique key !? Always decrease by two, because of removed first three lines.
169 $table{$linekey} = $onelinehash;
172 return \%table;
175 #################################################################################
176 # Reading all idt files in a specified directory
177 #################################################################################
179 sub read_all_tables_from_msidatabase
181 my ($workdir) = @_;
183 my %database = ();
185 my $ext = "idt";
187 my $allidtfiles = installer::systemactions::find_file_with_file_extension($ext, $workdir);
189 for ( my $i = 0; $i <= $#{$allidtfiles}; $i++ )
191 my $onefilename = ${$allidtfiles}[$i];
192 my $longonefilename = $workdir . $installer::globals::separator . $onefilename;
193 if ( ! -f $longonefilename ) { installer::exiter::exit_program("ERROR: Could not find idt file: $longonefilename!", "read_all_tables_from_msidatabase"); }
194 my $filecontent = installer::files::read_file($longonefilename);
195 my $idtcontent = analyze_idt_file($filecontent);
196 my $key = $onefilename;
197 $key =~ s/\.idt\s*$//;
198 $database{$key} = $idtcontent;
201 return \%database;
204 #################################################################################
205 # Checking, if this is the correct database.
206 #################################################################################
208 sub correct_database
210 my ($product, $pro, $langs, $languagestringref) = @_;
212 my $correct_database = 0;
214 # Comparing $product with $installer::globals::product and
215 # $pro with $installer::globals::pro and
216 # $langs with $languagestringref
218 my $product_is_good = 0;
220 my $localproduct = $installer::globals::product;
221 if ( $installer::globals::languagepack ) { $localproduct = $localproduct . "LanguagePack"; }
223 if ( $product eq $localproduct ) { $product_is_good = 1; }
225 if ( $product_is_good )
227 my $pro_is_good = 0;
229 if ((( $pro eq "pro" ) && ( $installer::globals::pro )) || (( $pro eq "nonpro" ) && ( ! $installer::globals::pro ))) { $pro_is_good = 1; }
231 if ( $pro_is_good )
233 my $langlisthash = installer::converter::convert_stringlist_into_hash(\$langs, ",");
234 my $langstringhash = installer::converter::convert_stringlist_into_hash($languagestringref, "_");
236 my $not_included = 0;
237 foreach my $onelang ( keys %{$langlisthash} )
239 if ( ! exists($langstringhash->{$onelang}) )
241 $not_included = 1;
242 last;
246 if ( ! $not_included )
248 foreach my $onelanguage ( keys %{$langstringhash} )
250 if ( ! exists($langlisthash->{$onelanguage}) )
252 $not_included = 1;
253 last;
257 if ( ! $not_included ) { $correct_database = 1; }
262 return $correct_database;
265 #################################################################################
266 # Searching for the path to the reference database for this special product.
267 #################################################################################
269 sub get_databasename_from_list
271 my ($filecontent, $languagestringref, $filename) = @_;
273 my $databasepath = "";
275 for ( my $i = 0; $i <= $#{$filecontent}; $i++ )
277 my $line = ${$filecontent}[$i];
278 if ( $line =~ /^\s*$/ ) { next; } # empty line
279 if ( $line =~ /^\s*\#/ ) { next; } # comment line
281 if ( $line =~ /^\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*\t+\s*(.+?)\s*$/ )
283 my $product = $1;
284 my $pro = $2;
285 my $langs = $3;
286 my $path = $4;
288 if (( $pro ne "pro" ) && ( $pro ne "nonpro" )) { installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename. Only \"pro\" or \"nonpro\" allowed in column 1! Line: \"$line\"", "get_databasename_from_list"); }
290 if ( correct_database($product, $pro, $langs, $languagestringref) )
292 $databasepath = $path;
293 last;
296 else
298 installer::exiter::exit_program("ERROR: Wrong syntax in file: $filename! Line: \"$line\"", "get_databasename_from_list");
302 return $databasepath;
305 #################################################################################
306 # Reading an existing database completely
307 #################################################################################
309 sub readdatabase
311 my ($allvariables, $languagestringref, $includepatharrayref) = @_;
313 my $database = "";
314 my $infoline = "";
316 if ( ! $allvariables->{'UPDATE_DATABASE_LISTNAME'} ) { installer::exiter::exit_program("ERROR: If \"UPDATE_DATABASE\" is set, \"UPDATE_DATABASE_LISTNAME\" is required.", "Main"); }
317 my $listfilename = $allvariables->{'UPDATE_DATABASE_LISTNAME'};
319 # Searching the list in the include pathes
320 my $listname = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$listfilename, $includepatharrayref, 1);
321 if ( $$listname eq "" ) { installer::exiter::exit_program("ERROR: List file not found: $listfilename !", "readdatabase"); }
322 my $completelistname = $$listname;
324 # Reading list file
325 my $listfile = installer::files::read_file($completelistname);
327 # Get name and path of reference database
328 my $databasename = get_databasename_from_list($listfile, $languagestringref, $completelistname);
330 # If the correct database was not found, this is not necessarily an error. But in this case, this is not an update packaging process!
331 if (( $databasename ) && ( $databasename ne "" )) # This is an update packaging process!
333 $installer::globals::updatedatabase = 1;
334 installer::logger::print_message( "... update process, using database $databasename ...\n" );
335 $infoline = "\nDatabase found in $completelistname: \"$databasename\"\n\n";
336 # Saving in global variable
337 $installer::globals::updatedatabasepath = $databasename;
339 else
341 # installer::logger::print_message( "... no update process, no database found ...\n" );
342 $infoline = "\nNo database found in $completelistname. This is no update process!\n\n";
344 push( @installer::globals::logfileinfo, $infoline);
346 if ( $installer::globals::updatedatabase )
348 if ( ! -f $databasename ) { installer::exiter::exit_program("ERROR: Could not find reference database: $databasename!", "readdatabase"); }
350 my $msifilename = $databasename;
351 installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$msifilename);
353 installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase start");
355 # create directory for unpacking
356 my $databasedir = installer::systemactions::create_directories("database", $languagestringref);
358 # copy database
359 my $fulldatabasepath = $databasedir . $installer::globals::separator . $msifilename;
360 installer::systemactions::copy_one_file($databasename, $fulldatabasepath);
362 installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase: before extracting tables");
364 # extract all tables from database
365 extract_all_tables_from_msidatabase($fulldatabasepath, $databasedir);
367 installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase: before reading tables");
369 # read all tables
370 $database = read_all_tables_from_msidatabase($databasedir);
372 # Test output:
374 # foreach my $key1 ( keys %{$database} )
376 # print "Test1: $key1\n";
377 # foreach my $key2 ( keys %{$database->{$key1}} )
379 # print "\tTest2: $key2\n";
380 # foreach my $key3 ( keys %{$database->{$key1}->{$key2}} )
382 # print "\t\tTest3: $key3: $database->{$key1}->{$key2}->{$key3}\n";
387 # Example: File table
389 # my $filetable = $database->{'File'};
390 # foreach my $linenumber ( keys %{$filetable} )
392 # print "Test Filenumber: $linenumber\n";
393 # foreach my $key ( keys %{$filetable->{$linenumber}} )
395 # print "\t\tTest: $key: $filetable->{$linenumber}->{$key}\n";
399 # Example: Searching for ProductCode in table Property
401 # my $column1 = "Property";
402 # my $column2 = "Value";
403 # my $searchkey = "ProductCode";
404 # my $propertytable = $database->{'Property'};
405 # foreach my $linenumber ( keys %{$propertytable} )
407 # if ( $propertytable->{$linenumber}->{$column1} eq $searchkey )
409 # print("Test: $searchkey : $propertytable->{$linenumber}->{$column2}\n");
413 installer::logger::include_timestamp_into_logfile("Performance Info: readdatabase end");
416 return $database;
419 #################################################################################
420 # Files can be included in merge modules. This is also important for update.
421 #################################################################################
423 sub readmergedatabase
425 my ( $mergemodules, $languagestringref, $includepatharrayref ) = @_;
427 installer::logger::include_timestamp_into_logfile("Performance Info: readmergedatabase start");
429 my $mergemoduledir = installer::systemactions::create_directories("mergedatabase", $languagestringref);
431 my %allmergefiles = ();
433 $installer::globals::mergemodulenumber = $#{$mergemodules} + 1;
435 foreach my $mergemodule ( @{$mergemodules} )
437 my $filename = $mergemodule->{'Name'};
438 my $mergefile = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
440 if ( $$mergefile eq "" ) { installer::exiter::exit_program("ERROR: msm file not found: $filename !", "readmergedatabase"); }
441 my $completesource = $$mergefile;
443 my $mergegid = $mergemodule->{'gid'};
444 my $workdir = $mergemoduledir . $installer::globals::separator . $mergegid;
445 if ( ! -d $workdir ) { installer::systemactions::create_directory($workdir); }
447 my $completedest = $workdir . $installer::globals::separator . $filename;
448 installer::systemactions::copy_one_file($completesource, $completedest);
449 if ( ! -f $completedest ) { installer::exiter::exit_program("ERROR: msm file not found: $completedest !", "readmergedatabase"); }
451 # extract all tables from database
452 extract_all_tables_from_msidatabase($completedest, $workdir);
454 # read all tables
455 my $onemergefile = read_all_tables_from_msidatabase($workdir);
457 $allmergefiles{$mergegid} = $onemergefile;
460 foreach my $mergefilegid ( keys %allmergefiles )
462 my $onemergefile = $allmergefiles{$mergefilegid};
463 my $filetable = $onemergefile->{'File'};
465 foreach my $linenumber ( keys %{$filetable} )
467 # Collecting all files from merge modules in global hash
468 $installer::globals::mergemodulefiles{$filetable->{$linenumber}->{'File'}} = 1;
472 installer::logger::include_timestamp_into_logfile("Performance Info: readmergedatabase end");
475 #################################################################################
476 # Creating several useful hashes from old database
477 #################################################################################
479 sub create_database_hashes
481 my ( $database ) = @_;
483 # 1. Hash ( Component -> UniqueFileName ), required in File table.
484 # Read from File table.
486 my %uniquefilename = ();
487 my %allupdatesequences = ();
488 my %allupdatecomponents = ();
489 my %allupdatefileorder = ();
490 my %allupdatecomponentorder = ();
491 my %revuniquefilename = ();
492 my %revshortfilename = ();
493 my %shortdirname = ();
494 my %componentid = ();
495 my %componentidkeypath = ();
496 my %alloldproperties = ();
497 my %allupdatelastsequences = ();
498 my %allupdatediskids = ();
500 my $filetable = $database->{'File'};
502 foreach my $linenumber ( keys %{$filetable} )
504 my $comp = $filetable->{$linenumber}->{'Component_'};
505 my $uniquename = $filetable->{$linenumber}->{'File'};
506 my $filename = $filetable->{$linenumber}->{'FileName'};
507 my $sequence = $filetable->{$linenumber}->{'Sequence'};
509 my $shortname = "";
510 if ( $filename =~ /^\s*(.*?)\|\s*(.*?)\s*$/ )
512 $shortname = $1;
513 $filename = $2;
516 # unique is the combination of $component and $filename
517 my $key = "$comp/$filename";
519 if ( exists($uniquefilename{$key}) ) { installer::exiter::exit_program("ERROR: Component/FileName \"$key\" is not unique in table \"File\" !", "create_database_hashes"); }
521 my $value = $uniquename;
522 if ( $shortname ne "" ) { $value = "$uniquename;$shortname"; }
523 $uniquefilename{$key} = $value; # saving the unique keys and short names in hash
525 # Saving reverse keys too
526 $revuniquefilename{$uniquename} = $key;
527 if ( $shortname ne "" ) { $revshortfilename{$shortname} = $key; }
529 # Saving Sequences for unique names (and also components)
530 $allupdatesequences{$uniquename} = $sequence;
531 $allupdatecomponents{$uniquename} = $comp;
533 # Saving unique names and components for sequences
534 $allupdatefileorder{$sequence} = $uniquename;
535 $allupdatecomponentorder{$sequence} = $comp;
538 # 2. Hash, required in Directory table.
540 my $dirtable = $database->{'Directory'};
542 foreach my $linenumber ( keys %{$dirtable} )
544 my $dir = $dirtable->{$linenumber}->{'Directory'}; # this is a unique name
545 my $defaultdir = $dirtable->{$linenumber}->{'DefaultDir'};
547 my $shortname = "";
548 if ( $defaultdir =~ /^\s*(.*?)\|\s*(.*?)\s*$/ )
550 $shortname = $1;
551 $shortdirname{$dir} = $shortname; # collecting only the short names
555 # 3. Hash, collecting info from Component table.
556 # ComponentID and KeyPath have to be reused.
558 my $comptable = $database->{'Component'};
560 foreach my $linenumber ( keys %{$comptable} )
562 my $comp = $comptable->{$linenumber}->{'Component'};
563 my $compid = $comptable->{$linenumber}->{'ComponentId'};
564 my $keypath = $comptable->{$linenumber}->{'KeyPath'};
566 $componentid{$comp} = $compid;
567 $componentidkeypath{$comp} = $keypath;
570 # 4. Hash, property table, required for ProductCode and Installlocation.
572 my $proptable = $database->{'Property'};
574 foreach my $linenumber ( keys %{$proptable} )
576 my $prop = $proptable->{$linenumber}->{'Property'};
577 my $value = $proptable->{$linenumber}->{'Value'};
579 $alloldproperties{$prop} = $value;
582 # 5. Media table, getting last sequence
584 my $mediatable = $database->{'Media'};
585 $installer::globals::updatelastsequence = 0;
587 foreach my $linenumber ( keys %{$mediatable} )
589 my $cabname = $mediatable->{$linenumber}->{'Cabinet'};
590 my $lastsequence = $mediatable->{$linenumber}->{'LastSequence'};
591 my $diskid = $mediatable->{$linenumber}->{'DiskId'};
592 $allupdatelastsequences{$cabname} = $lastsequence;
593 $allupdatediskids{$cabname} = $diskid;
595 if ( $lastsequence > $installer::globals::updatelastsequence ) { $installer::globals::updatelastsequence = $lastsequence; }
598 $installer::globals::updatesequencecounter = $installer::globals::updatelastsequence;
600 return (\%uniquefilename, \%revuniquefilename, \%revshortfilename, \%allupdatesequences, \%allupdatecomponents, \%allupdatefileorder, \%allupdatecomponentorder, \%shortdirname, \%componentid, \%componentidkeypath, \%alloldproperties, \%allupdatelastsequences, \%allupdatediskids);