#!/usr/bin/env perl
# $Id: mkgrub-menu-fedora.generic,v 1.7 2019/03/06 06:58:16 friedman Exp $

use strict;
use warnings qw(all);

use POSIX;

#my $menu_file = "/boot/grub2/grub-menu-fedora.cfg";
my $menu_file  = "/dev/stdout";

my $update_default = 0;

my @options = (qw(threadirqs
                  sysrq_always_enabled));

my $tmpl_host = hostnick ();
my $tmpl_part = sprintf ("(hd0,%d)", bootpart ());
my $tmpl_root = "/dev/${tmpl_host}_vg0/f%3\$d%4\$s_root";

my $template = "
	menuentry '%1\$s' {
	    save_default
	    if [ -z \"\$root\" ]; then set root='$tmpl_part'; fi
	    linux    /vmlinuz-%2\$s root=$tmpl_root ro %5\$s
	    initrd /initramfs-%6\$s.img
	}\n";

my %fsplat = ( x86_64 => 'x64',
               i686   => 'x86', );

sub xcat
{
  my $fh;
  open ($fh, $_[0]) || die "open: $_[0]: $!\n";
  local $/ = undef;
  scalar <$fh>;
}

sub bootpart
{
  for my $file (qw(/etc/fstab /proc/mounts))
    {
      my $tbl = xcat ($file);
      return $1 if $tbl =~ m=^/\S+(\d+)\s+/boot\s=m;
    }
  return "0";
}

sub swappart
{
  my $tbl = xcat ("/etc/fstab");
  return $1 if $tbl =~ m=^\s*(/\S+)\s+\S+\s+(swap)\s=m;
  return;
}

sub hostnick
{
  (my $h = (POSIX::uname())[1]) =~ s/\..*//;
  return $h unless $h =~ /-/;
  my $g;
  $g .= $1 while $h =~ /(?:^|-)(.)/g;
  return $g;
}

sub version_cmp
{
  my $f = defined $_[2] ? $_[2] : 0 ;
  my @a = split (/-/, $_[0], $f);
  my @b = split (/-/, $_[1], $f);
  my $j = (@a < @b ? @a : @b);
  for (my $i = 0; $i < $j; $i++)
    {
      my @aa = split (/[.\/]/, $a[$i]);
      my @bb = split (/[.\/]/, $b[$i]);
      my $jj = (@aa < @bb ? @aa : @bb);

      for (my $ii = 0; $ii < $jj; $ii++)
        {
          my $cmp = ($aa[$ii] =~ /^\d+$/ && $bb[$ii] =~ /^\d+$/
                     ? $aa[$ii] <=> $bb[$ii]
                     : $aa[$ii] cmp $bb[$ii]);
          return $cmp if $cmp != 0;
        }

      return -1 if @aa < @bb;
      return  1 if @aa > @bb;
    }
  return -1 if @a < @b;
  return  1 if @a > @b;
}

sub kernel_versions
{
  sort { version_cmp ($b, $a) } map { s=.*/vmlinuz-==; $_ } </boot/vmlinuz-*>;
}

sub main
{
  my $fh;

  if (@_ && $_[0] eq '-') { $fh = *STDOUT{IO} }
  else { open ($fh, "> $menu_file") || die "$menu_file: $!\n" }

  if (-f "/etc/systemd/sleep.conf" || -d "/etc/systemd/sleep.conf.d")
    {
      my $swapdev = swappart();
      push @options, "resume=$swapdev" if defined $swapdev;
    }

  my $koptions = join (" ", @options);
  my @kversions = kernel_versions ();
  my $latest;
  for my $version (@kversions)
    {
      if ($version =~ /rescue/)
        {
          my $label = sprintf ("Fedora rescue (%s)", $version);
          my $entry = sprintf ($template, $label,
                               $version, 0, "", $koptions,
                               $version);
          $entry =~ s/^\t//gm;
          print $fh $entry;

          $latest ||= $label;
        }
      else
        {
          my $osver  = $1 if $version =~ m/\.fc?(\d+)\./;
          my $osplat = $1 if $version =~ m/\.([^.]+)$/;
          my $label  = sprintf ("Fedora %d %s (%s)", $osver, $osplat, $version);
          my $plat   = $fsplat{$osplat};

          my $entry  = sprintf ($template, $label,
                                $version, $osver, $plat, $koptions,
                                $version);
          $entry =~ s/^\t//gm;
          print $fh $entry;

          $latest ||= $label;
        }
    }
  system (qw(grub2-editenv /boot/grub2/grubenv set), "saved_default=$latest")
    if $update_default;
}

main (@ARGV);

# eof
