File patch_license of Package obs-service-format_spec_file.374

#! /usr/bin/perl -w
#
# vim:sw=2:et
# 

BEGIN {
  unshift @INC, ".";
  unshift @INC, "/usr/lib/build/";
}

use Time::localtime;
use Data::Dumper;
use strict;

my @oldspec = ();
my @newspec = ();
my $base_package = "";
my $icecreamforbuild = "";
my @copyrights = ();
my $needsrootforbuild = 0;
my $needsbinariesforbuild = 0;
my $nodebuginfo = 0;
my $vim_modeline;
my $nosrc_result = 0;
my $current_section = "header";
my $had_debug_package = 0;
my %pkg_version = ();
my $main_license;
my %seen_licenses = ();
my $main_group;
my %seen_groups = ();
my $build_root = $ENV{'BUILD_ROOT'};
my $disabled_packs;
my $ifhandler;
my $definelist;
my $debug = 0;

my @global_tags_list = 
(
 'Autoreq',
 'Autoreqprov',
 'BuildArch',
 'BuildArchitectures',
 'BuildRequires',
 'Conflicts',
 'DocDir',
 'Enhances',
 'Enhances',
 'EssentialFor',
 'ExcludeArch',
 'ExclusiveArch',
 'Freshens',
 'Group',
 'Name',
 'NoPatch',
 'NoSource',
 'Obsoletes', 
 'Patch\d*',
 'Prefix',
 'PreReq',
 'Provides',
 'Recommends',
 'Requires',
 'Source\d*',
 'Suggests',
 'Summary',
 'Supplements',
 'Url',
);

my $global_tags_re = '^\s*(' . join("|", @global_tags_list) . ')\s*:';

my $section_tags_re ='^\s*%(?:clean|check|prep|build|install|pre|post|preun|postun|posttrans|package|' .
                     'description|files|triggerin|triggerun|triggerpostun)\b';

sub unify {
  my %h = map {$_ => 1} @_;
  return grep(delete($h{$_}), @_);
}

sub capitalize_case($)
{
  my ($tag) = @_;

  $tag = lc($tag);

  $tag =~ s/docdir/DocDir/i;
  $tag =~ s/arch/Arch/i;
  $tag =~ s/patch/Patch/i;
  $tag =~ s/source/Source/i;
  $tag =~ s/req/Req/i;
  $tag =~ s/prov/Prov/i;
  $tag =~ s/^(\w)/uc($1)/e;

  return $tag;
}

sub compare_arrays {
    my ($first, $second) = @_;
    return 0 unless @$first == @$second;
    for (my $i = 0; $i < @$first; $i++) {
	return 0 if $first->[$i] ne $second->[$i];
    }
    return 1;
}  

sub maybe_add_empty_line()
{
  push @oldspec, "XXXBLANKLINE" 
    if ($current_section ne "description" && $oldspec[-1] !~ /^\s*$/ && $oldspec[-1] !~ /^[#%]/);
}

sub change_section($)
{
  my ($new_section) = @_;

  maybe_add_empty_line();

  $current_section = $new_section;
  warn "section changed to $current_section\n" if $debug;
}

my %license_replace = ();
use File::Basename;

sub load_license_map()
{
    return if defined $license_replace{"GPL-2.0"};
    my $scriptdir = File::Basename::dirname($0);
    open(MAP, "$scriptdir/licenses_changes.txt") || die "can't open licenses_changes.txt";
    # ignore header
    readline(*MAP);
    my %spdx;
    while (<MAP>) {
	chomp;
	my ($license, $oldstring) = split(/\t/, $_, 2);
	#$license =~ s,\s*$,,;
	#$oldstring =~ s,\s*$,,;
	next unless length($license);
	#print STDERR "$license\t$oldstring\n";
	die "$oldstring is given twice in $_" if defined $license_replace{$oldstring};
	$license_replace{$oldstring} = $license;
        $spdx{$license} = 1;
    }
    close(MAP);
    for (keys %spdx) {
	$license_replace{$_} = $_;
    }
}

sub replace_single_spdx($)
{
    my ($l) = @_;

    return '' if $l eq '';

    load_license_map();
    $l =~ s,ORlater,or later,g;
    $l =~ s,ORsim,or similar,g;
    $l =~ s,^\s+,,;
    $l =~ s,\s+$,,;

    if (defined $license_replace{$l}) {
	$l = $license_replace{$l};
    } else {
	print STDERR "Unknown license '$l'\n";
	exit(1);
    }
    return $l;
}

sub replace_spdx_and($);
sub replace_spdx_and($)
{
    my ($license) = @_;

    # special case as or later is common in our spec files
    $license =~ s,or later,ORlater,g;
    $license =~ s,or similar,ORsim,g;
    
    #print STDERR "ORIG '$license'\n";
    my @licenses = ();
    if ( $license =~ /^(.*?)\(([^)]*)\)(.*?)$/ ) {
	my ($head, $paren, $tail) = ($1, $2, $3);
        if ($paren =~ /and|or/) {
	    $head = replace_spdx_and($head);
	    $tail = replace_spdx_and($tail);
	    $paren = replace_spdx_and($paren);
	    #print STDERR "AFTE '$head($paren)$tail'\n";
	    return "$head($paren)$tail";
        }
    }

    for (split(/(\s+(?:and|or)\s+)/, $license, -1)) {
	$_ = replace_single_spdx($_) unless $_ eq '' || /(\s+(?:and|or)\s+)/;
        s/\s+/ /g;
	push @licenses, $_;
    }
    #print STDERR "AFTE '" . join('', @licenses) . "'\n";
    return join('', @licenses);
}

sub replace_spdx($)
{
    my ($license) = @_;

    my @licenses = ();
    for (split(/\s*;\s*/, $license)) {
	push @licenses, replace_spdx_and($_);
    }
    return join(' ; ', @licenses);
}

sub set_current_pkg {
  my ( $arg ) = @_;
  print "DEBUG: set_current_pkg receiving $arg\n" if $debug;
  my ( @argarray ) = split ( '\s+' , $arg );
  my $curpack = $base_package;
  my $curlang = "";
  while (my $carg = shift @argarray) {
    next if ($carg eq "%description" || $carg eq "%package" || $carg eq "%prep");
    if ($carg eq "-l") {
      $curlang = shift @argarray;
    } elsif ($carg eq "-n") {
      $curpack = shift @argarray;
    } else {
      $curpack = "$base_package-" if $base_package;
      $curpack .= $carg;
    }
  }
  print "DEBUG: set_current_pkg returning $curpack, $curlang\n" if $debug;
  return ($curpack, $curlang);
}

sub read_and_parse_old_spec {
  my ( $specfile, $base_package ) = @_;
  my $current_package = $base_package;
  my $current_lang = "";
  my $check_printed = "false";
  my $print_comments = "false";
  my %version;
  my $ifhandler;
  $ifhandler->{"disabled"} = 0;

  my @readspec;
  open ( SPEC , "$specfile" ) || die "can't read specfile";
  @readspec = <SPEC>;
  close SPEC;
  chomp @readspec;

  while (@readspec) {
    $_ = shift @readspec;

    if ( /^\s*$/ && $current_section ne "description") {
	# stop preamble parsing on two blank lines
        if ($print_comments eq "false" && $oldspec[0] && $oldspec[-1] eq "XXXBLANKLINE") {
		$print_comments = "true";
		push @oldspec, "XXXDOUBLELINE";
		next;
	}
	push @oldspec, "XXXBLANKLINE";
	next;
    }

    if ( /^# vim:/ ) {
	$vim_modeline = $_;
	next;
    }
    
    if ( /^#\s*needsrootforbuild\s*$/ ) {
        $needsrootforbuild = 1;
        next;
    }
    if ( /^#\s*needsbinariesforbuild\s*$/ ) {
        $needsbinariesforbuild = 1;
        next;
    }
    if ( /^#\s*norootforbuild/ ) {
	next;
    }

    if ( /^#\s*nodebuginfo\s*$/ ) {
        $nodebuginfo = 1;
        next;
    }
    if ( /^#\s*icecream/ ) {
        $icecreamforbuild = $_;
	$icecreamforbuild =~ s/^#\s*icecream\s*//;
        next;
    }
    if ( /^#\s*Copyright\s*/ ) {
        my $lastlineblank = 0;
        for (;;)
          {
            # check if line is ONLY a standard copyright line, if so, ignore.
            my $c = $_;
            $c =~ s{\s*(\d+|copyrights?|\(c\)|suse|linux|products|gmbh|nuremberg|n..?rnberg|germany|\W+)\s*}{}gi;
            push(@copyrights, $_) if length $c > 5;
            last if length $readspec[0] < 10 || $readspec[0] =~ m{modifications and additions}i || $readspec[0] !~ /^[\#\s]/
		|| grep { $readspec[0] =~ /^#\s*$_/ } ("needsrootforbuild","needsbinariesforbuild","nodebuginfo","icecream","usedforbuild","Commandline","MD5SUM","!BuildIgnore");
            $_ = shift @readspec;
          }
        next;
    }
    # evil epoch removal
    next if ( /^Epoch:/ );
    $_ =~ s/%{?epoch}?[:-]//g;
    $_ =~ s/ 0:/ /g if ( /^requires/i || /^buildreq/i );

    if ( /^BuildRequires:/ ) {
	my $cur_buildreq = $_;
	$cur_buildreq =~ s/^BuildRequires:\s*//;
	my %aa;
	while ($cur_buildreq =~ m{([^,\s]+(\s*[<=>]+\s*[^,\s]+)?)}g) {
	  $aa{$1}=1;
	}
	# ignore line if it looks like a "usedforbuild" line, i.e.
	# if it contains too many base packages
	next if (grep {$aa{$_}} qw{gcc rpm glibc bash}) > 2;
	for my $br (sort keys(%aa)) {
	    push @oldspec, "BuildRequires:  $br";
	}
	next;
    }
    next if ( /^#\s*usedforbuild/ );
    if ( /^%\?__\*BuildRequires:/ ) {
      push @oldspec, $_;
      next;
    }
    if ( /^#!__\*BuildRequires:/ ) {
      push @oldspec, $_;
      next;
    }
    if ( /^#!BuildIgnore:/ ) {
      push @oldspec, $_;
      next;
    }

    if ( /^#/ && $current_section ne "description") {
      warn "$_ $current_section\n" if $debug;
      if ( $print_comments eq "true" || $readspec[0] =~ /^%define/ || $readspec[0] =~ /^%if/) {
        push @oldspec, $_;
      }
      next;
    }

    if ( /^%debug_package/ ) {
	# remove, we add this ourselves
	next;
    }
    $print_comments = "true" unless /^#/;

    if ( /^%define\s*vendor\s/ || /^%define\s*distribution\s/ ) {
      next;
    }

    if ( /^\s*%if/ || /^\s*%\{/ || /^\s*%define/ || /^\s*%el/ || /^\s*%endif/ ) {
      change_section("header") if ($current_section eq "description");
      push @oldspec, $_;
      if ( /^\s*%if\s/ ) {
	my @args = split (/\s+/,$_);
	$_ =~ s/[\{\}\"]//g for (@args);
	$ifhandler->{"last_if_disabled"} = 0;
	$ifhandler->{"last_if_if"} = 1;
	$ifhandler->{"depth"}++;
	my $if_not = 0;
	if ( $args[1] =~ /^\!/ ) {
		$args[1] =~ s/^\!//;
		$if_not = 1;
	}
	$args[2] = "" unless $args[2];
	if (	($args[1] eq "0")
		|| ($args[1] eq "%name" && $args[2] eq "!=" && $args[3] eq $base_package)
		|| ($args[1] eq "%name" && $args[2] eq "==" && $args[3] ne $base_package)
		|| ($args[1] && !$args[3] && !$if_not && $definelist->{$args[1]} && $definelist->{$args[1]} eq "0")
		|| ($args[2] eq "==" && $args[3] ne "0" && $definelist->{$args[1]} && $definelist->{$args[1]} eq "0")
		|| ($args[2] eq "!=" && $args[3] eq "0" && $definelist->{$args[1]} && $definelist->{$args[1]} eq "0")
		|| ($args[1] && !$args[3] && $if_not && $definelist->{$args[1]} && $definelist->{$args[1]} eq "1")
		|| ($args[1] && $args[2] eq "!=" && $args[3] eq "1" && $definelist->{$args[1]} && $definelist->{$args[1]} eq "1") ) {
		$ifhandler->{"disabled"} = $ifhandler->{"depth"};
		$ifhandler->{"last_if_disabled"} = 1;
	}
      } elsif ( /^\s*%if/ ) {
	$ifhandler->{"last_if_disabled"} = 0;
        $ifhandler->{"last_if_if"} = 0;
	$ifhandler->{"depth"}++;
      } elsif ( /^\s*%endif/ ) {
	$ifhandler->{"disabled"} = 0 if $ifhandler->{"disabled"} == $ifhandler->{"depth"};
	$ifhandler->{"depth"}--;
      } elsif ( /^\s*%else/ ) {
	if ($ifhandler->{"disabled"} == $ifhandler->{"depth"} && $ifhandler->{"last_if_disabled"} == 1) {
		$ifhandler->{"disabled"} = 0;
	} elsif ($ifhandler->{"disabled"} == 0 && $ifhandler->{"depth"} == 1 && $ifhandler->{"last_if_if"} == 1) {
		$ifhandler->{"disabled"} = 1;
	}
      } elsif ( /^\s*%define\s/ ) {
	my @args = split (/\s+/,$_);
	$_ =~ s/[\{\}\"]//g for (@args);
	$args[2] =~ s/\Q$_\E/$definelist->{$_}/g for sort { length($b) <=> length($a) } keys (%{$definelist});
	if ( $args[2] !~ /[\(\)\{\}\@\%\"\\]/ ) {
		$definelist->{"%".$args[1]} = $args[2] if $ifhandler->{"disabled"} == 0;
		$definelist->{"%{".$args[1]."}"} = $args[2] if $ifhandler->{"disabled"} == 0;
		$definelist->{"%{?".$args[1]."}"} = $args[2] if $ifhandler->{"disabled"} == 0;
	}
	while ($_ =~ /\\$/) {
	  $_ = shift @readspec;
	  push @oldspec, $_;
	}
      }
      next;
    }
    if ( /^%package\b/i or /^%prep\b/i ) {
      if (/^%package\b/i) {
	change_section("header");
      } else {
	change_section("prep");
      }
      $_ =~ s/^(%\w+)/lc($1)/e;
      if ($debug) {
    	warn "key: $_ value: $definelist->{$_}\n" for (sort { length($b) <=> length($a) } keys (%{$definelist}));
      }
      push @oldspec, $_;
      for my $xx (sort { length($b) <=> length($a) } keys (%{$definelist})) {
        $_ =~ s/\Q$xx\E/$definelist->{$xx}/;
      }
      $_ =~ s/%{\?[^\}]*}//;
      if ($debug) {
	warn "after: $_\n";
      }
      ($current_package, $current_lang) = set_current_pkg ( $_ );
      if ($ifhandler->{"disabled"}) {
	$disabled_packs->{$current_package} = 1;
	warn "$current_package is disabled\n" if $debug;
      }
      next;
    }
    if ( /^%description\b/i ) {
      change_section("description");
      push @oldspec, $_;
      next;
    }
    if ( /^%install\b/i ) {
      change_section("install");
      push @oldspec, $_;
      next;
    }
    if ( /^%changelog\b/i ) {
      change_section("changelog");
      # changelog comes always from *.changes.  Skip what is in spec file
      # at the moment.
      next;
    }
    if (/^%files\b/i) {
      change_section("files");
      $current_section = "files";
    }
    if ( /^%/ ) {
      if ( m/$section_tags_re/oi ) {
        $_ =~ s/^(%\w+)/lc($1)/e;
        change_section("header") if (! m/\s*%files/i && !m/\s*%build/i);
	change_section("build") if m/\s*%build/i;
	warn "changed to $current_section for $_\n" if $debug;
      }

      push @oldspec, "$_";
      next;
    }

    if ($current_section eq "header") {
      my $c_pack = $current_package;
      $c_pack .= "_disabled" if $ifhandler->{"disabled"};

      if ( /^Vendor:/ || /^Distribution:/ || /^Packager:/ ) {
        next;
      }
      # remove default value of Autoreqprov
      if ( /^Autoreqprov\s*:\s*(.*)/i ) {
	  next if ( lc($1) eq "on" || lc($1) eq "yes");
      }
      # reset Release
      if ( /^Release\s*:\s*(.*)/i ) {
	  # will be after Version
	  next;
      }
      if ( /^Summary\s*:\s*(.*)\s*$/i ) {
	  push @oldspec, sprintf("%-16s%s", "Summary:", $1);
	  push @oldspec, "XXXPOSTSUMMARY $current_package";
	  next;
      }

      # remove license and print out after license later
      if ( /^License\s*:\s*(.*)\s*$/i || /^Copyright\s*:\s*(.*)\s*$/i ) {
	  my $license = replace_spdx($1);
	  $main_license = $license if (!$main_license);
	  $seen_licenses{$current_package} = $license;
	  next;
      }

      # remove groups and print out after summary later
      if ( /^Group\s*:\s*(.*)\s*$/i ) {
	  my $group = $1;
	  $main_group = $group if (!$main_group);
	  $seen_groups{$current_package} = $group;
	  next;
      }

      if ( /^BuildArchitectures\s*:/i ) {
        $_ =~ s/^[^:]+:/BuildArch:/;
      }

      if ( /^BuildRoot\s*:/i ) {
        push @oldspec, "BuildRoot:      %{_tmppath}/%{name}-%{version}-build";
        next;
      }

      if ( m/$global_tags_re\s*(.*)/oi ) {
        my ($tag, $value) = ($1, $2);
        $nosrc_result = 1 if ($tag =~ /(?:nosource|nopatch)/i);
        push @oldspec, sprintf("%-16s%s", capitalize_case($tag) . ":", $value);
        next;
      }
      if ( /^Version:/ ) {
	warn "found Version, section = $current_section\n" if $debug;
        $version{$c_pack} = $_;
        $version{$c_pack} =~ s/^Version:\s*(.*)\s*/$1/;
        push @oldspec, sprintf("%-16s%s","Version:",$version{$c_pack});
	push @oldspec, sprintf("%-16s%s","Release:", "0");
        next;
      }
    }
    if ( $current_section ne "changelog" ) {
      push @oldspec, $_;
      next;
    }
  }

}

my $specfile = shift ( @ARGV );
if ( ! stat($specfile) ) {
  die "$specfile is no file";
}


my @specpath = split ('/' ,$specfile);
my $specbase = pop @specpath;
my $specdir = join ('/', @specpath);

if ( $specdir eq "" ) {
  $specdir = ".";
}
my $xdefinelist;
my $seen_name = 0;
open ( SPE , "$specfile" );
while ( <SPE> ) {
  chomp;

  if ( m/^License\s*:\s*(.*)\s*$/ ) {
	printf("%-16s%s\n","License:", replace_spdx($1));
  } else {
	print "$_\n";
  }	
}