#!/usr/bin/perl -w
# this turns a spec template file into a real spec file. it uses a bunch
# of environment variables to handle the various parts used by CCE.
#

# build templates for each section
sub xlate_line
{
    my ($line, $tag, $tag2) = @_;
    my $arch = $ENV{BUILDARCH} ? "BuildArchitectures: $ENV{BUILDARCH}" : '';
    my $provides = $ENV{PROVIDES} ? "Provides: $ENV{PROVIDES}" : '';

    $line =~ s/\[VENDOR\]/$ENV{VENDOR}/go;
    $line =~ s/\[VENDORNAME\]/$ENV{VENDORNAME}/go;
    $line =~ s/\[SERVICE\]/$ENV{SERVICE}/go;
    $line =~ s/\[VERSION\]/$ENV{VERSION}/go;
    $line =~ s/\[RELEASE\]/$ENV{RELEASE}/go;
    $line =~ s/\[ROOTDIR\]/$ENV{CCEBASE}/go;
    $line =~ s/\[CCEWEB\]/$ENV{CCEWEB}/go; 
    $line =~ s/\[DEFLOCALE\]/$ENV{DEFLOCALE}/go; 
    $line =~ s/\[LABEL\]/$tag/g;
    $line =~ s/\[AUTOFILL\]/$tag2/g;
    $line =~ s/\[BUILDARCH\]/$arch/go;
    $line =~ s/\[PROVIDES\]/$provides/go; 
    return $line;
}
        
sub print_type
{
    my ($fh, $section, $type, $tag1, $tag2, %hash) = @_;

    print $fh xlate_line($hash{$section}{HEADER}, $tag1) 
    if $hash{$section}{HEADER};
        
    if ($hash{$section}{$type}) {
    print $fh xlate_line($hash{$section}{$type}, $tag1, $tag2);
    } elsif (ref(\$hash{$section}) eq 'SCALAR') {
    print $fh xlate_line($hash{$section}, $tag1, $tag2);
    }
    
}

# get the file path for RPM spec file %file section
# param: dirDepth: the depth of the file/directory relative to Sausalito root
# param: files: an array of all the full file names relative to the Sausalito
#     root
sub get_file_path
{
    my ($dirDepth, @files) = @_;

    my @fullPaths = ();
    my $file;
    foreach $file (@files) {
    $file =~ /^(([^\/]+\/){$dirDepth})(.*)$/;
    push @fullPaths, '%{RootDir}/' . $1 .
        '%{Vendor}/%{Service}/' . $3 . "\n";
    }

    return @fullPaths;
}

sub print_section 
{
    my ($fh, $section, %hash) = @_;
    my $requires = 'Requires: ' . $ENV{REQUIRES};
    my $provides = 'Provides: ' . $ENV{PROVIDES};
    
    # capstone comes last
    my @types = ('GLUE', 'UI', 'LOCALE', 'CAPSTONE'); 
    my $type;

    # locales
    my @locales = split ' ', $ENV{LOCALES};
    my $locale;

    # ui stuff
    my @uibits = ();
    # be backward compatible
    if ($ENV{UIDIRS})
    {    
        @uibits = split ' ', $ENV{UIDIRS};
    }
    else # handle old style
    {
        push @uibits, split(' ', $ENV{UIMENUS});
        push @uibits, split(' ', $ENV{UISERVICES});
    }

    # capstone bits
    my @capbits = ('constructor', 'destructor');

    # glue bits
    my @gluebits = ('conf', 'schemas', 'handlers', 'am', 'rules');

    foreach $type (@types) 
      {
       	next if ($type eq 'CAPSTONE' and $ENV{NOCAPSTONES});

        if (($type eq 'CAPSTONE') or $ENV{"BUILD$type"} eq 'yes') 
        {
            $type = lc $type;

            if ($section eq 'DESCRIPTION') 
            {
                # special-case locale 
                if ($type eq 'locale') 
                {
                    # only add this once so it doesn't multiply for each
                    # locale
                    $provides .= "[VENDOR]-[SERVICE]-locale ";
                    
                    foreach $locale (@locales) 
                    {
                        print_type($fh, $section, $type, 
                                "$type-$locale", xlate_line($provides), %hash);
                    }
                    
                    $requires .= "[VENDOR]-[SERVICE]-locale ";
                } 
                else 
                {
                    print_type($fh, $section, $type, $type, 
                        ($type eq 'capstone') ? 
                            xlate_line($requires) : '', %hash);
                    $requires .= "[VENDOR]-[SERVICE]-$type ";
                }

            } 
            elsif (($section eq 'FILES') and not ($type eq 'locale')) 
            {
                my $line;
                if ($type eq 'ui') 
                {
                    foreach my $uibit (@uibits) 
		    {
		      my $uidir = "ui/$ENV{VENDORNAME}/$uibit";
		      my $uidirSegments = 3;
		      if (! -d $uidir) 
                        {
			  $uidir = "ui/$uibit";
			  $uidirSegments = 2;
			  # see if the sub directory is there at all
			  if (! -d $uidir) { next; }
                        }
		      
		      # find all locale-sensitive files to exclude
		      my $exclude = '';
		      my $locale;
		      foreach $locale (@locales) 
                        {
			  $exclude .= " -and -not -path \"*\\.$locale\"";
                        }
		      
		      my @files = ();
		      
		      # FIXME: as with many things in this file there are 
		      # special cases.  The way things are setup needs to
		      # be rethought, so we can avoid the need for special
		      # cases all over the place.
		      if ($uibit eq 'lcd') # special case 1
                        {
			  #call recursive subroutine to get all the files
			    $line .= join("\n",
			  		&generate_full_paths($uidir, $ENV{LCDDIR},
			  				     '.\/CVS\/*|.\/\.'));
                        }
		      elsif ($uibit eq 'extensions') # special case 2
                        {
			  opendir(EXTS, $uidir) 
			    or die { "Can't open $uidir: $!\n" };
			  while (my $file = readdir(EXTS))
                            {
			      if (! -f "$uidir/$file") { next; }
			      $file =~ /^([^\.]+\.[^\.]+)\.([^\.]+\.[^\.]+)$/;
			      my ($install_file, $domain) = ($1, $2);
			      if ($install_file && $domain)
                                {
				  $line .= '%{RootDir}' 
				    . "/ui/$uibit/$domain/$install_file\n";
                                }
                            }
			  closedir(EXTS);
                        }
		      else # the default behaviour
                        {
			  # find all files to include
			  # exclude all hidden files (.whatever) since nfs 
			  # and cvs seem to like to drop these
			  @files = `find $uidir -type f -and -not -path "*/\\.*" $exclude | grep -v CVS`;
			  
			  # RPM needs full path
			  $line .= join '', 
			  get_file_path($uidirSegments, @files);
                        } # end if $uibit eq 'something'
                    }
		} 
                elsif ($type eq 'capstone') 
                {
                    # we also special-case capstones
                    my $name;
                    foreach $name (@capbits) 
                    {
                        my $capdir = "$name/$ENV{VENDORNAME}";
                        $capdir = "$name" unless -d $capdir;
                        if (opendir(CAPDIR, $capdir)) 
                        {
                            while ($_ = readdir(CAPDIR)) 
                            {
                                next if /^\.{1,2}/;
                                next unless -f "${capdir}/$_";
                                $line .= '%{RootDir}/' . $name. 
                                    '/%{Vendor}/%{Service}' . "\n";
                                last;
                            }
                            close(CAPDIR);
                        }
                    }

                } 
                elsif ($type eq 'glue') 
                {
                    my $name;
                    foreach $name (@gluebits) 
                    {
                        my $gluedir = "glue/$ENV{VENDORNAME}/$name";
                        if (! -d $gluedir) 
                        {
                            $gluedir = "glue/$name";
                        }
            
                        if (opendir(GDIR, $gluedir))
                        {
                            my $flag = 0;
                            my $tgtname = $name;
                            if ($name eq "am") 
                            {
                                $tgtname = "swatch/conf";
                            }
                            while ($_ = readdir(GDIR)) 
                            {
                                next if /^\.{1,2}/;
                                next unless -f "${gluedir}/$_";
                                $line .= '%{RootDir}/' . $tgtname. 
                                    '/%{Vendor}/%{Service}' . "\n";
                                $flag = 1;
                                print STDERR "$name:\n";
                                last;
                            }
                            $flag || 
                                print STDERR "WARNING: $gluedir was empty.\n";
                            close(GDIR);
                        } 
                        else 
                        {
                            print STDERR "Couldn't open glue/$name\n";
                        } 
                    }

                    # handle perl directory here too, hardcoded fix later
                    # if desirable
                    my $perldir = 'perl';
                    if (-d $perldir)
                    {
                        $line .= '%{RootDir}' . "/$perldir/" 
                            . ucfirst($ENV{VENDOR}) . "/*\n";
                    }

		    # Another hard-coded section.  Handle the ccewrap xml 
		    # files
		    my $ccewrapdir = 'ccewrap';
		    if (-d "glue/$ccewrapdir") 
		    {
		        $line .= '/etc/ccewrap.d/%{Vendor}/%{Service}' . "\n";
		    }
		    
		}

		# now print out what we have
                print STDERR "$section $type =\n$line\n\n";
                print_type($fh, $section, $type, $type, $line, %hash);

            } 
            elsif ($type eq 'locale') 
            {
	        # we always deal with locales differently 
                # figure out where locales are kept
                my $localedir = 'locale';
    
                if (-d 'locale/$ENV{VENDOR}') 
                {
                    $localedir = 'locale/$ENV{VENDOR}';
                }

                foreach $locale (@locales) 
		  {
		    next if ($ENV{XLOCALEPAT} =~ /"$locale"/);
		    
		    my $line = "";

                    # add all .mo files in locale directory
                    opendir LOCALE, "$localedir/$locale/" 
                        or die "Can't open $localedir/$locale/: $!\n";
                    while (my $entry = readdir(LOCALE)) 
                    {
                        if ($entry =~ /^(.*)\.po$/) 
                        {
                            $line .= "/usr/share/locale/$locale/LC_MESSAGES/";
                            $line .= ($ENV{LOCALE_NO_VENDOR}) ? 
                                        "${1}.mo\n" : "%{Vendor}-${1}.mo\n";
                        }
                        # this is to handle style XML files in gallery
                        elsif ($entry =~ /^(.*)\.xml$/i) 
                        {
                            $line .= "$ENV{CCEBASE}/ui/style/$1.xml.$locale\n";
                        }
                    }

                    close LOCALE;

                    # add .prop file if exist
                    if(-f "$localedir/$locale/$ENV{SERVICE}.prop") 
                    {
                        $line .= "/usr/share/locale/$locale/%{Vendor}-%{Service}.prop\n";
                    }

                    my $uidir = "ui/$ENV{VENDORNAME}/web";
                    my $uidirSegments = 3;
                    if (! -d $uidir) 
                    {
                        $uidir = "ui/web";
                        $uidirSegments = 2;
                    }

                    # find all locale-sensitive UI files to include
                    my @files = `find $uidir -type f -and -not -path "*/\\.*" -and -path "*\\.$locale" | grep -v CVS`;

                    # RPM needs full path
                    $line .= join '', get_file_path($uidirSegments, @files);

                    print_type($fh, $section, $type, "$type-$locale",
                            $line, %hash);
                }
            } 
            else 
            {
                print_type($fh, $section, $type, $type, '', %hash);
            }
        }
    }
}



die "$0 <defines> <template> <spec file>\n" if $#ARGV lt 2;
my $defines = $ARGV[0];
my $template = $ARGV[1];
my $spec = $ARGV[2];

# check for important environment variables
die "you need to pass in the following environment variables:
  CCEBASE, VENDOR, VENDORNAME, SERVICE, VERSION, RELEASE,
  BUILDUI, UIDIRS, BUILDGLUE, LOCALES, and BUILDLOCALE\n" 
    unless ($ENV{CCEBASE} and $ENV{VENDOR} and 
        $ENV{SERVICE} and $ENV{VERSION} and 
        $ENV{RELEASE} and $ENV{BUILDUI} and 
        $ENV{BUILDGLUE} and $ENV{BUILDLOCALE}
        and ($ENV{UIDIRS} or $ENV{UIMENUS} or $ENV{UISERVICES}) and $ENV{VENDORNAME}
        and defined($ENV{LOCALES}));

open(DEFINES, $defines) or die "Can't open $defines!\n";
open(TMPL, $template) or die "Can't open $template!\n";
open(SPEC, ">$spec") or die "Can't open $spec for writing!\n";

my %hash;
my $tmp;
while (<DEFINES>) 
{
    if (/<begin \$(\S+)>/) 
    {
        $tmp = $1;
        while (<DEFINES>) 
        {
            last if $_ =~ /<end \$$tmp>/;
            $hash{$tmp} .= $_;
        }
    } 
    elsif (/<begin \%(\S+)>/) 
    {
        my $hashname; 
        $tmp = $1;
        while (<DEFINES>) 
        {
            if (/<end \%$tmp>/) 
            {
                last;
            } 
            elsif (/<begin (\S+)>/) 
            {
                $hashname = $1;        
            } 
            elsif (/<end $hashname>/) 
            {
                $hashname = undef;
            } 
            elsif ($hashname) 
            {
                $hash{$tmp}{$hashname} .= $_;
            }
        }
    }
}
close(DEFINES);

while (<TMPL>) 
{
    if (/^\[(\S+)_SECTION\]/) 
    {
        print_section(\*SPEC, $1, %hash);
    } 
    else 
    {
        $_ = xlate_line($_);
        print SPEC;
    }
}

# recursive subroutine that generates a file list to add to a spec file
# arguments:
#       directory name - the directory the complete structure is under
#       install prefix - the directory prefix to prepend to files found
#       ignore regex - patterns that when found should be ignored as a string
#                      (ie 'CVS|.\/\.') to exclude CVS and dot files
#
# returns:
#       if directories or files are found returns the list as an array
#       with the install prefix prepended or the empty array.  

sub generate_full_paths
{
    my ($search_dir, $prefix, $regex) = @_;

    my @files = ();
    my @dirs = ();

    if (opendir(SEARCH, $search_dir))
    {
        my $error = 0;
        while (my $file = readdir(SEARCH))
        {
	  # skip stuff that should be skipped
            next if ("$search_dir/$file" =~ /$regex/);
           
            # avoid infinite recursion by skipping . and .. entries
            next if ($file =~ /^\.{1,2}$/);

            if (-f "$search_dir/$file")
            { # found a regular file, add it to the list
                push @files, "$prefix/$file";
	      }
            elsif (-d "$search_dir/$file")
            { #found a directory, push into @dirs so we can recurse later
	      push @dirs, $file;
	    } 
        }
	close(SEARCH);
    }
    else
    {
        warn("opendir failed: $!");
    }
    
    # recurse into each subdirectory.
    foreach my $dir (@dirs) {
      push @files, &generate_full_paths("$search_dir/$dir",
					"$prefix/$dir", $regex);
    }

    return @files;
}

