#!/usr/bin/perl
##
## doconfig is Copyright 1997 by Brandon Gillespie, All Rights Reserved.
##

$sys_base = "/sys";
$sys_conf = "i386/conf";
$log = "$sys_base/$sys_conf/kernelbuild.log";

## we dont actually need anything here
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin";

# basic verification stuff
if ($< || $>) {
    &abort_usage("doconfig must be executed as root.");  
}

if (!-d "/usr/src" || !-d $sys_base) {
    &abort("Kernel source not installed, install 'sys' source package");
}

##############################################################

sub valid_configname {
    local($valid_configname) = $_[0];
    local($tmpid, $err);

    if (!length($valid_configname)) {
        return;
    }

    if ($valid_configname =~ /[^A-Z]/) {
        $tmpid = $valid_configname;
        $tmpid =~ tr/[a-z]/[A-Z]/;
        $tmpid =~ s/[^A-Z]+//g;
        chop($err = <<END);
** The kernel config file name may only contain capitalized alphabetic
** characters.  Try '$tmpid' instead of '$valid_configname'.
END
        &error($err);
        return 0;
    }
    return 1;
}

$version = "1.1";
$autokernconf = "/usr/local/sbin/autokernconf";
$autokernconf_args = "";
$autoinstall = 0;
$autoreboot = 0;
$autoconfig = 1;
$editopt = 1;
$editor = "";
$configopts = "-n";

while ($#ARGV != -1) {
    $_ = shift(@ARGV);
    if (/^-c(.*)$/) {
        $config = $2 || shift(@ARGV);
        (!$config && &abort_usage("Config file not specified with -c option."));
        $config =~ s/^\s+//;
        $config =~ s/\s+$//;
        if ($config =~ /\//) {
            if (!-e $config) {
                &abort_usage("Config file $config does not exist.");
            } else {
                if (!-f $config) {
                    &abort_usage("Config file $config is not a plainfile.");
                }
                @configfile = split(/\//, $config);
                $configfile = $configfile[$#configfile];
                !&valid_configname($configfile) && &abort_usage;
            }
            $configpath = "$sys_base/$sys_conf/$configfile";
            if (-e $configpath) {
                print "The kernel config file $configpath already exists\n";
                print "Replace? [yes] ";
                chop($yesno = <STDIN>);
                if ($yesno && $yesno !~ /(y|ye|yes)/i) {
                    &abort("Config file already exists.");
                }
                print "Saving $configpath as $configpath.bak.\n";
                if (!rename($configpath, "$configpath.bak")) {
                    &abort("Unable to rename $configpath: $!\n");
                }
            }
            system("/bin/cp $sys_base/$sys_conf/GENERIC $configpath") && exit;
        } else {
            !&valid_configname($config) && &abort_usage;
            $configfile = $config;
            $configpath = "$sys_base/$sys_conf/$config";
            if (!-e $configpath) {
                &abort("Config file $configpath does not exist.");
            }
            if (!-f $configpath) {
                &abort("Config file $configpath is not a plain file.");
            }
        }
        $autoconfig = 0;
    } elsif (/^-e(.*)$/) {
        $editopt = 1;
        $editor = $2;
        if (!$editor) {
            $editor = shift(@ARGV);
            if ($editor && $editor !~ /^\//) {
                unshift(@ARGV, $editor);
                $editor = "";
            }
        }
    } elsif (/^-i$/) {
        $autoinstall = 1;
    } elsif (/^-[gp]$/) {
        $configopts .= " $_";
    } elsif (/^-n/) {
        $configopts =~ s/\s*-n\s*//;
    } elsif (/^-r$/) {
        $autoinstall = 1;
        $autoreboot = 1;
    } elsif (/^-a$/) {
        $autokernconf_args = "-a";
    } else {
        &usage();
        &abort("Unknown option '$_'");
    }
}

if (-f $log) {
    print("Remove existing kernel log $log? [yes] ");
    chop($yesno = <STDIN>);
    if ($yesno && $yesno !~ /(y|ye|yes)/i) {
        &abort("Kernel build log exists.");
    }
    if (!unlink($log)) {
        &abort("Unable to remove kernel build log: $!");
    }
}

if ($autoconfig) {
    $name = `/bin/hostname -s`;
    $name =~ tr/[a-z]/[A-Z]/;
    $name =~ s/[^A-Z]+//g;
    $configfile = "";
    while (!$configfile) {
        print "Kernel Build Name [$name] ";
        chop($config = <STDIN>);
        if (!$config) {
            $config = $name;
        }
        !&valid_configname($config) && next;
        $configpath = "$sys_base/$sys_conf/$config";
        if (-e $configpath) {
            print("Kernel Config file $config already exists.\nUse it instead? [no] ");
            chop($yesno = <STDIN>);
            if (!length($yesno) || $yesno =~ /(n|no)/i) {
                next;
            }
            $configfile = $config;
            last;
        }
        $configfile = $config;
        system("$autokernconf $autokernconf_args -c $configpath");
        if ($? >> 8) {
            die("autokernconf exited with error code " . ($? >> 8) . "\n");
        }
    }
}

if ($configfile eq "GENERIC") {
    &abort("You cannot alter the GENERIC template.");
}

#if ($editopt || $autoconfig) {
    if (!$editor) {
        $editor = $ENV{EDITOR} || $ENV{VISUAL};
    }
    if (!$editor) {
        $editor = "/usr/bin/vi";
    }
    system("$editor $configpath");
    if (($? >> 8) > 0) {
        &abort("Error exit status from editor, aborting...");
    }
#}

##############################################################
# some stuff to make times look better--FEH perl4

@months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");

sub date {
    local($time) = $_[0];
    local($out);
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($time);

    return sprintf("$mday-%s-%d", $months[$mon], $year+1900);
}

sub timestamp {
    local($out);
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();

    return sprintf("%2d-%s-%-2d %2d:%02d.%02d",
                  $mday, $months[$mon], $year+1900, $hour, $min, $sec);
}

##############################################################
# Ok, kernel config creation is done, now run 'config'

if (!chdir($sys_base)) {
    &abort("Unable to change directory to $sys_base");
}

if (!chdir("$sys_base/$sys_conf")) {
    &abort("Unable to change directory to $sys_base");
}

system("/usr/sbin/config $configopts $configfile") && exit;

##############################################################
# That seems to have gone well, now actually compile it

$compiledir = "$sys_base/compile/$configfile";
if (!chdir($compiledir)) {
    &abort("Unable to change directory to $compiledir");
}

&build("Dependancies", "depend");
&build("Kernel", "");

sub build {
    local($what, $args) = @_;

    &run_and_log("Building $what", "make $args", $log);
    $output = `/usr/bin/egrep '^Stop.\$' $log`; 
    if ($output) {
        &abort("Errors encountered, see $log");
    }
}

## ok, to be able to give notices to the user that yes, we are
## working, run the program in this odd way
sub run_and_log {
    local($what, $prog, $log) = @_;

    if (!open(LOG, ">>$log")) {
        &abort("Unable to open compile log '$log'");
    }

    select(STDOUT); $| = 1;
    print("${what}.. " . &timestamp() . "\n");

    if (!open(PROG, "$prog 2>&1 |")) {
        &abort("Unable to execute '$prog'");
    }

    $term = 1;
    while (<PROG>) {
        print LOG;
        if (/^(lex|echo|yacc|make|sh|cc|rm|mv|cp|mkdir|rmdir|mkdep)[\s>|]/ ||
            /^MKDEP_/ ||
            /.\/[a-z]+/ ||
            /^Warning: Object directory not changed/)
        {
            print("\r${what}.. " . &timestamp());
            $term = 0;
        } else {
            chop;
            if (/^\s*$/) {
                next;
            }
            if (!$term) {
                print "\n";
            }
            $term++;
            ## beutify it
            if (length($_) + 4 < 80) {
                print "  > $_\n";
            } else {
                $line = "  >";
                foreach $word (split(/\s+/)) {
                    if (length($line) + length($word) < 79) {
                        $line .= " $word"; 
                    } else {
                        print "$line\n";
                        $line = "  >  ";
                    }
                }
                if (length($line) > 3) {
                    print "$line\n";
                }
            }
        }
    }
    print "\n";
    select(STDOUT); $| = 0;
    close(PROG);
    close(LOG);
}

##############################################################
# now wrap things up..

if ($autoinstall) {
    print "Installing Kernel.. " . &timestamp() . "\n";

    if (-f "/kernel.old") {
        ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
         $atime,$mtime,$ctime,$blksize,$blocks) = stat("/kernel.old");
        $date = &date($mtime);
        print "A backup kernel already exists, rename to /kernel.$date? [yes] ";
        chop($yesno = <STDIN>);
        if (!$yesno || $yesno =~ /(y|ye|yes)/i) {
            if (!(rename("/kernel.old", "/kernel.$date"))) {
                &abort(<<END);
Unable to rename /kernel.old to /kernel.$date because:
abort:     $!
abort: You still need to move $compiledir/kernel to /
abort: and reboot
END
            }
        }
    }
    if (!(rename("/kernel", "/kernel.old"))) {
        &abort(<<END);
Unable to rename /kernel to /kernel.old because:
abort:     $!
abort: You still need to move $compiledir/kernel to /
abort: and reboot
END
    }

    ## and incase its on a different filesystem, use 'mv'
    ## instead of perl rename()
    system("/bin/mv $compiledir/kernel /kernel") && exit;

    if ($autoreboot) {
        print <<END;
*** 
*** Ready to reboot.  If the system is unable to bootup, you can type:
*** 
***     /kernel.old
*** 
*** At the 'Boot:' prompt, to boot from the old kernel.
***
END
        print "\n[Press ENTER to Reboot]";
        <STDIN>;
        exec("/sbin/reboot");
    }
}

##############################################################

sub abort {
    die("abort: $_[0]\n");
}

sub abort_usage {   
    &usage();     
    &error("** $_[0]");
    print "\n";   
    exit(0);      
}   

sub error {       
    print STDERR "$_[0]\n";
}   

sub usage {
    print STDERR <<END;
Version: $version
Syntax: doconfig [options]

Options:

  -c FILE   Specify existing configuration file.
  -i        Install kernel
  -r        Reboot after building and installing (will prompt first)
  -e [PATH] Use editor
  -a        passed to 'autokernconf'
  -g        configure system for debugging
  -p        configure system for profiling
  -n        new compile, REMOVE the old compile directory (this is
            opposite what config(8) uses -n for)

See Also:

    doconfig(8), autokernconf(8), config(8)

END

}

