#!/usr/bin/perl

# ARGV[0] = Path to the package that contains a
# Contents/Resources/BundleVersions.plist

# ARGV[1] = Root path into which the bundles mentioned in
# Contents/Resources/BundleVersions.plist have just now been installed
# (e.g. if running during postflight) or currently exist (e.g. if
# running at build time).

=pod

stampBuildNumInPlistStrings       (Can be run as a postflight_actions script)

For each bundle being installed:

(Modified 5-28-04 to only include BuildVersion -- api)

Looks inside this package's BundleVersions.plist file for the
ProductBuildVersions (e.g. 7A37) and SourceVersion (e.g. 6470000 but
we just show the 647 part) and concatenates these with a dash
(e.g. 7A37-647) to produce what we'll call the "ComboVersion".

Finds all the "InfoPlist.strings" files in the bundle -- there may be
many since these might be inside the localized .lproj directories.

In each InfoPList.strings file, adds "CFBundleVersion" key if not
already present.

Then, replaces contents of "CFBundleVersion" with the ComboVersion.

Then, inserts the "ComboVersion after any numeric version number it
finds in the CFBundleGetInfoString.  So for example, in that entry:

"2.0"              becomes "2.0 (7A37-647)"
"2.0 "             becomes "2.0 (7A37-647) "
"2.0,"             becomes "2.0 (7A37-647),"
"2.0 (anything)"   becomes "2.0 (7A37-647)"
"2.0 (anything),"  becomes "2.0 (7A37-647),"

NOTE: Alters only .strings files.  .plist files are never altered.

=cut

use File::Path;
use File::Spec;

use strict;
use Data::Dumper;

# Look at our own BundleVersions.plist to get  mappings from installed bundle paths => SourceVersion and => ProductBuildVersion

{
	my $PackagePath 	= $ARGV[0];
	my $DestPath	 	= $ARGV[1];
	my $BVs				= `cat "$PackagePath/Contents/Resources/BundleVersions.plist"` or warn("'$PackagePath' seems to have no BundleVersion.plist"), exit;

	## Insist on at least BuildVersion keys for each.  Items lacking
	## one won't get any stamping at all.  But also add in
	## SourceVersion and EngBuildVersion keys if found.  (Join all
	## existing with dashes.)
	
	my $BuildVersions	= {$BVs =~ m{<key>\.([^<>]+?)Contents/version.plist</key>.*?<key>ProductBuildVersion</key>.*?<string>([^<>]*?)     </string>}gsx};
	my $SourceVersions	= {$BVs =~ m{<key>\.([^<>]+?)Contents/version.plist</key>.*?<key>SourceVersion      </key>.*?<string>([^<>]*?)\d{4}</string>}gsx};
	my $EngBuildVersions= {$BVs =~ m{<key>\.([^<>]+?)Contents/version.plist</key>.*?<key>EngBuildVersion    </key>.*?<string>([^<>]*?)     </string>}gsx};
	
	warn("BundleVersion.plist in '$PackagePath' seemed to have no ProductBuildVersion(s)"), exit unless keys %$BuildVersions;
	
	## my $ComboVersions	= {map {($_ => join("-", grep {length} ($BuildVersions   ->{$_}, 
	##															$SourceVersions  ->{$_},  
	##															$EngBuildVersions->{$_})))}             keys %$BuildVersions};
	
	## ComboVersions now looks like this: {'/Applications/Remote Desktop.app/' => '7A37-647'} (with a key for every package)
	## print &Dumper($BuildVersions, $SourceVersions, $ComboVersions); 
	
	foreach my $BundlePath (keys %$BuildVersions) 
	{&StampBuildNumsInBundle("$DestPath$BundlePath", $BuildVersions->{$BundlePath});}
}

sub StampBuildNumsInBundle
{
	my ($FullPath, $ExtraVersInfo) = @_;
	
	$FullPath				=~ s{//}{/}g;
	(my $FullPathEscaped	= $FullPath) =~ s{\'}{''}g;
	
	my $InfoPlistStringsFiles = [map {chomp; $_} `/usr/bin/find '$FullPathEscaped' -name InfoPlist.strings`];
	## print &Dumper($BundlePath, $FullPath, $ExtraVersInfo, $InfoPlistStringsFiles);
	
	## Convert some strings to UTF16 for later convenience in searching and inserting.
	my $CFBGI	= UTF16Str(qq{CFBundleGetInfoString}  );
	my $CFBV	= UTF16Str(qq{CFBundleVersion}        );
	my $CFBVNil	= UTF16Str(qq{\nCFBundleVersion = "";});
	my $Quote	= UTF16Str(qq{\"}                     );
	my $Vers16	= UTF16Str($ExtraVersInfo);
	
	## Process each (possibly localized) InfoPlist.strings file in this bundle.
	foreach my $Path (@$InfoPlistStringsFiles)
	{
		my $Buffer	= (eval{local $/ = undef; use IO::File; IO::File->new("<$Path")->getline()})
			or warn("Failed to read '$Path'"), next;
		
		## If InfoPList.strings does not have a CFBundleVersion key, then add one at the end.
		$Buffer		=~ m{$CFBV\x00\W} or $Buffer .= $CFBVNil;
		
		## Replace value of CFBundleVersion with our "ExtraVersionInfo".
		$Buffer		=~ s{($CFBV.*?$Quote)(.*?)($Quote)}{$1\x00 $Vers16$3}g 
			or warn("Failed to insert '$ExtraVersInfo' into CFBundleVersion key in '$Path'");
		
		## Augment any numeric version number surrounded by spaces in CFBundleGetInfoString with our "ExtraVersionInfo", in parens.
		$Buffer		=~ s{($CFBGI.*?(?:\x00\d)(?:\x00\.\x00\d){1,2})(?:\x00 \x00\(.*?\x00\))?(\x00 )?}{$1\x00 \x00($Vers16\x00)$2}g
			; ## or warn("Did not insert '$ExtraVersInfo' into CFBundleGetInfoString key in '$Path'");
		
		my $Wrote	= (eval{use IO::File; IO::File->new(">$Path")->print($Buffer)})
			or warn("Failed to replace modified file: '$Path'"), next;
	}
}

## Utility to convert an ASCII string to UTF16.
sub UTF16Str {(my $x = $_[0]) =~ s/(.)/\x00$1/gs; $x}

