#! /usr/bin/perl

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# the usual fuss...

BEGIN { unshift @INC, ( $0 =~ /(.*?)((?<![^\/])bin\/)?[^\/]+$/ )[0] . "lib" }
use ReadConfig;
use MakeExt2Image;
use AddFiles;
use Conv2Image;

sub lsplit;
sub add_quotes;
sub real_name;

die "usage: $Script\n" if @ARGV;

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# some config data

$cfg = "${CfgPath}module.config";
$deps = "${BasePath}tmp/modules-config/module_deps";
$fnames = "${BasePath}tmp/modules-config/module_list";
$mdisk_c = "${BasePath}tmp/modules-config/module_disks_create";
$mdisk_l = "${BasePath}tmp/modules-config/module_disks_list";
$mdisk_i = "${BasePath}tmp/modules-config/module_disks_images";
$modinfo = "${BasePath}tmp/modules-config/modules.info";
$mod_no = "${BasePath}tmp/modules-config/module_missing";
$mod_no_all = "${BasePath}tmp/modules-config/module_missing_all";
$dst = "${DataPath}initrd/gen/module.";
$all_mods = "${DataPath}initrd/all_modules";
$mod_type = $ConfigData{module_type};
$added_mods = "${BasePath}tmp/modules-config/module_added";

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# now we really start...

$debug = exists($ENV{'debug'}) ?  $ENV{'debug'} : "";

$arch = `uname -m`; chomp $arch; $arch = "i386" if $arch =~ /^i\d86$/;
$arch = $ENV{TEST_ARCH} if exists $ENV{TEST_ARCH};

$err = 0;

die "$Script: $deps: $!\n" unless open F, $deps;
while(<F>) {
  chomp;
  @i = split " ", $_;
  $i[0] =~ s/:$//;
  $i = shift @i;
  $dep{$i} = [ @i ];
}
close F;


die "$Script: $fnames: $!\n" unless open F, $fnames;
while(<F>) {
  chomp;
  if(/^(\S+):\s*(\S+)/) {
    $fname{$2} = "$1/$2.$mod_type";
  }
}
close F;

@fname_list = (sort keys %fname);

die "$Script: $modinfo: $!\n" unless open F, $modinfo;
undef $f;
while(<F>) {
  chomp;
  if(/^filename:.*\/(\S+?)\.ko/) {
    $f = $1;
    next;
  }
  if(m#^description:\s*(.+?)\s*$#) {
    $descr{$f} = $1 if defined $f;
    undef $f;
    next;
  }
}
close F;

if(open F, $all_mods) {
  while(<F>) {
    $all_mods{$1} = 1 if /^([^# ]\S+)/;
  }
  close F;
}

if(open my $f, $cfg) {
  while(<$f>) {
    push @cfg_file, $_;
  }
  close $f;
}
else {
  die "$Script: $cfg: $!\n";
}

if(open my $f, $added_mods) {
  while(<$f>) {
    push @cfg_file, $_;
  }
  close $f;
}

open NO_MOD, ">$mod_no";
open NO_MOD_ALL, ">$mod_no_all";

for (@cfg_file) {
  chomp;
  s/^\s*([#;].*)?//;
  next if $_ eq "";
  if(/^\[(.+)\]/) {
    $sect = $1;
    push @sect, $sect unless exists $sect{$sect};
    $sect{$sect}{tag} = undef;
    next;
  }
  if(/MoreModules=(.*)/i) {
    $tmp = $1;
    if($tmp =~ /^([^,]+),([^,]+)$/) {
      $sect{$sect}{MoreModules} = $1;
      $sect{$sect}{MoreModulesLxrc} = $2;
    }
    else {
      $sect{$sect}{MoreModules} =
      $sect{$sect}{MoreModulesLxrc} = $tmp;
    }
    $sect{$sect}{MoreModules} = $1;
    next;
  }

  if(/ModuleClass=(.*)/i) {
    $sect{$sect}{ModuleClass} = $1;
    next;
  }

  if(/ModDisk=(.*)/i) {
    $tmp = $1;
    if($tmp =~ /^([^,]+),([^,]+)$/) {
      $sect{$sect}{ModDisk} = $1;
      $sect{$sect}{ModDiskNext} = $2;
    }
    else {
      $sect{$sect}{ModDisk} =
      $sect{$sect}{ModDiskNext} = $tmp;
    }
    next;
  }

  @l = lsplit $_;
  $r = $l[0] =~ s/^-// ? 1 : 0;
  if($l[0] =~ m#/#) {
    $l0 = $l[0];
    for $m1 (@fname_list) {
      $l[0] = $m1;
      if($fname{$l[0]} =~ m#^$l0(?:\.xz|\.zst)?$#) {
        # print "$l0 - $fname{$l[0]} - $l[0]\n";

        $all{$l[0]} = 1;

        $notuseful{$l[0]} = 1 if $sect eq 'notuseful';

        $old_d = $l[1];
        $l[1] = $descr{$l[0]} if $old_d eq '' && $descr{$l[0]};
        print "dup: $l[0] ($sect - $l0)\n" if $sect{$sect}{'ref'}{$l[0]};
        push @{$sect{$sect}{'m'}}, [ @l ] unless $sect{$sect}{'ref'}{$l[0]};
        $l[1] = $old_d;

        $sect{$sect}{'ref'}{$l[0]} = 1;
      }
    }
  }
  elsif(defined $fname{$l[0]}) {
    $all{$l[0]} = 1;

    $notuseful{$l[0]} = 1 if $sect eq 'notuseful';

    $old_d = $l[1];
    $l[1] = $descr{$l[0]} if $old_d eq '' && $descr{$l[0]};
    print "dup: $l[0] ($sect - )\n" if $sect{$sect}{'ref'}{$l[0]};
    push @{$sect{$sect}{'m'}}, [ @l ] unless $sect{$sect}{'ref'}{$l[0]};
    $l[1] = $old_d;

    $sect{$sect}{'ref'}{$l[0]} = 1;

    $sect{$sect}{'r'}{$l[0]} = 1 if $r;
  }
  else {
    # once per module is enough
    # print STDERR "warning: no such module: $l[0]\n" unless $mod_warned{$l[0]};
    print NO_MOD "$l[0]\n" unless $mod_warned{$l[0]};
    print NO_MOD_ALL "$l[0]\n" unless $mod_warned{$l[0]} || $all_mods{$l[0]};
    $mod_warned{$l[0]} = 1;
  }
}

close NO_MOD;

# fix pre/post install fields
for (@sect) {
  for $m (@{$sect{$_}{'m'}}) {
    $pre = $$m[3];
    $post = $$m[4];
    $mod = $$m[0];
    undef %l;
    undef @new_pre;
    undef @new_post;

    @p = split ' ', $pre;
    for $p (@p) {
      for $p1 (@{$dep{$p}}) {
        unshift @new_pre, $p1 unless $l{$p1};
        $l{$p1} = 1;
      }
      if(defined $fname{$p}) {
        push @new_pre, $p unless $l{$p};
        $l{$p} = 1;
      }
      else {
        print STDERR "warning: \"$p\" not needed\n";
      }
    }

    for $p (@{$dep{$mod}}) {
      unshift @new_pre, $p unless $l{$p};
      $l{$p} = 1;
    }
    $l{$mod} = 1;

    @p = split ' ', $post;
    for $p (@p) {
      for $p1 (@{$dep{$p}}) {
        unshift @new_post, $p1 unless $l{$p1};
        $l{$p1} = 1;
      }
      if(defined $fname{$p}) {
        push @new_post, $p unless $l{$p};
        $l{$p} = 1;
      }
      else {
        print STDERR "warning: \"$p\" not needed\n";
      }
    }

    for $k (keys %l) {
      $sect{$_}{'a'}{$k} = 1;
      if(!$all{$k}) {
        $err = 10;
        print STDERR "error: no config for \"$k\" (needed by $mod)\n";
      }
      elsif($_ ne 'notuseful' && $notuseful{$k}) {
        $err = 10;
        print STDERR "error: no useful config for \"$k\" (needed by $mod)\n";
      }
    }

    $$m[3] = join ' ', @new_pre;
    $$m[4] = join ' ', @new_post;
  }
}


for (sort { $fname{$a} cmp $fname{$b} } keys %fname) {
  if(!$all{$_}) {
    $err = 11;
    print STDERR "error: nothing known about \"$fname{$_}\"\n";
  }
}


mkdir "${DataPath}initrd/gen", 0755;

for (@sect) {
#  next if /notuseful/;

  $s = $_;
  $s =~ s/\s+//g;
  $s = $1 if exists($sect{$_}{MoreModules}) && $sect{$_}{MoreModules} =~ /^(\S+)-modules/;
  if(exists($sect{$_}{MoreModules}) && exists($sect{$_}{ModDisk})) {
    $moddisk_i{$sect{$_}{ModDisk}} .= " $sect{$_}{MoreModules}";
  }

  $s = $sect{$_}{ModuleClass} if exists $sect{$_}{ModuleClass};
  $moddisk_l{$sect{$_}{ModDisk}} .= " $s" if exists $sect{$_}{ModDisk};

  # to make it possible to put something on the disk that wasn't created by mk_modules
  # e.g. initrd
  if(exists $sect{$_}{ModDisk}) {
    if(
      !exists($sect{$_}{MoreModules}) ||
      !exists($sect{$_}{ModuleClass}) ||
      "$sect{$_}{ModuleClass}-modules" eq $sect{$_}{MoreModules}	# ???
    ) {
      $moddisk_c{$sect{$_}{ModDisk}} .= " $s";
    }
  }

  open W, ">${dst}config.$s";
  print W "[${\real_name($_)}]\n";
  print W "MoreModules=${\real_name($sect{$_}{MoreModulesLxrc})}\n" if exists $sect{$_}{MoreModulesLxrc};
  print W "ModDisk=$sect{$_}{ModDiskNext}\n" if exists $sect{$_}{ModDiskNext};
  print W "\n" if exists($sect{$_}{MoreModules}) || exists($sect{$_}{ModDiskNext});

  for $m (@{$sect{$_}{'m'}}) {
    if($$m[1] !~ /^--/) {
      $l = join ',', add_quotes(@$m);
      $l =~ s/,*$//;
      print W "$l\n";
    }
  }
  print W "\n\n";
  close W;

=head
  open W, ">${dst}shortconfig.$s";
  print W "[${\real_name($_)}]\n";
  print W "MoreModules=${\real_name($sect{$_}{MoreModulesLxrc})}\n" if exists $sect{$_}{MoreModulesLxrc};
  print W "ModDisk=$sect{$_}{ModDiskNext}\n" if exists $sect{$_}{ModDiskNext};
  print W "\n\n";
  close W;
=cut

  open W, ">${dst}files.$s";
  for $f (sort { $fname{$a} cmp $fname{$b} } keys %{$sect{$_}{'a'}}) {
    next if $sect{$_}{'r'}{$f};
    if($fname{$f}) {
      print W "a /lib/modules/<kernel_ver>/$fname{$f} /modules\n";
    }
  }
  close W;

  if($_ eq 'cd1') {
    open W, ">${dst}files.$s-instsys";
    for $f (sort { $fname{$a} cmp $fname{$b} } keys %{$sect{$_}{'a'}}) {
      next if $sect{$_}{'r'}{$f};
      if($fname{$f}) {
        print W "  /lib/modules/<kernel_ver>/$fname{$f}\n";
      }
    }
    close W;
  }

=head
  open W, ">${dst}list.$s";
  print W ";\n; ${\real_name($_)} modules\n;\n";
  for $m (@{$sect{$_}{'m'}}) {
    next if $sect{$_}{'r'}{$$m[0]};
    $d = $$m[1];
    $d =~ s/^--//;
    if($d) {
      printf W "%-15s\t%s\n", $$m[0], $d;
    }
  }
  print W "\n";
  close W;
=cut
}


=head
open W, ">$mdisk_c";
for (sort keys %moddisk_c) {
  print W "$_:$moddisk_c{$_}\n"
}
close W;

open W, ">$mdisk_l";
for (sort keys %moddisk_l) {
  print W "$_:$moddisk_l{$_}\n"
}
close W;

open W, ">$mdisk_i";
for (sort keys %moddisk_i) {
  print W "$_:$moddisk_i{$_}\n"
}
close W;
=cut

$err = 0 if $ENV{debug} =~ /\bignore\b/;

exit $err;


sub lsplit
{
  local $_;
  my ($l, $f, @l);

  $l = shift;

  while($l =~ s/^\s*([^,"]*|\".*?(?<!\\)\")\s*,//) {
    $f = $1;
    $f =~ s/^"(.*)"$/$1/;
    $f =~ s/\\\"/"/g;
    push @l, $f;
  }
  $l =~ s/^\s*|\s*$//g;
  $l =~ s/^"(.*)"$/$1/;
  $l =~ s/\\\"/"/g;
  push @l, $l if $l ne "";

  $l[1] = undef if $l[1] =~ /^-(?!-)/;

  return @l;
}

sub add_quotes
{
  local $_;
  my (@i);

  @i = @_;

  for (@i) {
    if(/[,"]/) {
      s/"/\\"/g;
      $_ = "\"$_\"";
    }
  }

  return @i
}


sub real_name
{
  local $_;

  $_ = shift;
  s/\@[^@]+?\b//;
  return $_;
}

