#!/usr/bin/perl
#
# USAGE: specparser.pl file.spec
#
# Output:
#   stdout:
#     list of:
#       <md5><space><url>\n
#     for given specfile.
#     except for lines prefixed with: ERROR:
#   exit status:
#     0 - normal
#     2 - cannot open spec file
#

sub next_spec($)
{
  $spec = shift;
  $base_spec = $spec;
  $base_spec =~ s|.*/||;
  %macro = ();
  $err_cnt = 0;
}

sub error($)
{
  $err_cnt++;
  print "ERROR: $base_spec: $_[0]\n";
}

sub warning($)
{
  print "ERROR: $base_spec: $_[0]\n";
}

sub trim_spaces($)
{
  my $v = shift;
  
  $v =~ s/\s+$//;
  $v =~ s/^\s+//;
  
  return $v;
}

# expand macros in string
sub expand($)
{
  my $v = trim_spaces(shift);
  my $cnt = 20;

  while ($v =~ /\%\{([^\{]+)\}/) {
    my $value;
    if (defined $macro{$1}) {
      $value = $macro{$1};
    } else {
      error("undefined macro $1");
      $value = "UNDEFINED";
    }
    $v =~ s/\%\{([^\{]+)\}/$value/;

    return $v if (length $v > 1000 or $cnt-- <= 0)
  }

  while ($v =~ s/\%\(\s*echo\s+([^\|]+?)\s*\|\s*tr\s*(-d|)\s+([^\)]+?)\s*\)/\@\@tr-me\@\@/) {
    my ($what, $d_opt, $how) = ($1, $2, $3);
    my ($from, $to) = ($how, "");
    ($from, $to) = ($1, $2) 
      if $how =~ /^([^\s]+)\s+([^\s]+)$/;
    if ($d_opt and $to ne "") {
      error("tr -d with second string)");
    } elsif (($from . $to) =~ /^[a-zA-Z0-9\+_\-\.]+$/) {
      if ($d_opt) {
        eval "\$what =~ tr/$from//d;";
      } else {
        eval "\$what =~ tr/$from/$to/;";
      }
    } else {
      error("illegal characters in tr string(s) '$from' '$to'");
    }
    $v =~ s/\@\@tr-me\@\@/$what/;
    
    return $v if (length $v > 1000 or $cnt-- <= 0)
  }

  error("unexpanded macros in $v")
    if ($v =~ /\%[^0-9]/);
  
  return $v;
}

# define given macro
sub define($$)
{
  my ($n, $v) = @_;
  $macro{$n} = trim_spaces($v);
}

# sets hash of macros defined with %define or %global
# also define %{name}, %{version} and %{source_N}
sub parse_defines($)
{
  open(F, "< $_[0]") or die;
  while (<F>) {
    chomp;
    if (/^\s*\%(define|global)\s+([^\s]+)\s+([^\s].*)$/) {
     define($2, $3);
    } elsif (/^Version\s*:\s*(.*)/i) {
      define("version", $1);
    } elsif (/^Name\s*:\s*(.*)/i) {
      define("name", $1);
    } elsif (/^Source(\d+)\s*:\s*(.*)/i) {
      define("source_$1", expand($2));
    } elsif (/^Patch(\d+)\s*:\s*(.*)/i) {
      define("patch_$1", expand($2));
    } elsif (/^NoSource\s*:\s*(\d+)\s*$/i) {
      define("no_source_$1", "1");
    }
  }
  close(F);
}

sub print_md5($)
{
  open(F, "< $_[0]") or die;
  while (<F>) {
    chomp;
    if (/^\s*#\s*source(\d+)-md5\s*:\s*([a-f0-9]{32})/i) {
      my $no = $1;
      my $md5 = $2;
      if (defined $macro{"no_source_$no"}) {
        error("both NoSource: $no and md5 given");
      } elsif (defined $macro{"source_$no"}) {
        my $s = $macro{"source_$no"};
	if ($s =~ /^([a-z0-9A-Z:\=\?\@\+\~\.\-\/_]|\%[0-9])+$/) {
	  if ($s =~ /^(ftp|http):\/\//) {
	    if ($s =~ /\/$/) {
	      error("source $no ($s) is directory");
	    } else {
	      if ($s =~ /:\/\/distfiles\.pld-linux\.org\/src/) {
	        $s =~ s|.*/||;
	        print "$md5 no-url-copy://$s\n";
	      } else {
                print "$md5 $s\n";
              }
	    }
	  } else {
	    $s =~ s|.*/||;
	    print "$md5 no-url://$s\n";
	  }
	} else {
	  error("source $no url $s is ill-formatted");
	}
      } else {
        error("source $no not defined");
      }
    }
  }
  close(F);
}

next_spec(shift);
parse_defines($spec);
print_md5($spec);

exit(0);
