#!/usr/bin/perl
$|=1;	# unbuffer stdout

$SYSTEM="BSD/OS";	# System we are looking for
$VERSION="4.3";		# This is the version of BSD/OS we are looking for
$TYPE="BINARY";		# Type of CD we are looking for
$KEYBD="unknown";	# Type of keyboard
$CONSTABD="/keyboard";  # Keyboard maps directory
$METHOD="unknown";	# set to "express" to force an express installsw
$IMETHOD="unknown";	# set to "express" to force an express installsw
$DMETHOD="unknown";	# set to "express" to force an express disksetup
$BSDLOC="cdrom";	# where to read /bsd and /boot from

$CONSOLE="";
$CDROMFSTAB=1;		# XXX should be 1 -> write /cdrom in fstab
$ALLOWDOS=1;		# Set to 0 if you do not want to allow DOS
$INSWOPT="-sd";
$FUZZOUT=15;
$NETOUT=15;
$iSoftDisks=0;
@SoftDisks=();

$INSTALLDATA="/tmp/install.data";
$INSTALLCFG="/a/var/db/install.cf";
$EDITOR="pico";
$FSTABX="/tmp/fstab.install";
$FSTABD="/tmp/dfstabinfo.";

$BSDOSCD="/cdrom/PACKAGES/PACKAGES";
$CDVERSION="/cdrom/.version";
$NEEDSPACE=1024; # XXX - this should be calculated
$FLOPPY=1; # Are we running off the floppy  (1 = floppy setup, 0 = cdrom setup)
$SAVETEXT="";
$DEFHELP="Sorry, no help on this topic";
$HELP=$DEFHELP;

if (open(IN, "sysctl -n hw.physmem |")) {
        $MEMSIZE = <IN>;
        close(IN);
} else {
        $MEMSIZE = 16 * 1024 * 1024;
} 

$fuzindex = 0;
@fuzzies = (
"While the disk is being configured and initialized you
might want to familiarize yourself with the release
notes that came with the distribution.  They contain
answers to many common questions.",
"Configuring and initializing the disk may take some time.
This is not an indication of anything wrong.",
"Larger disks will take longer to configure and initialize
than smaller disks.  The speed of the drive may also impact
the length of this procedure.",
);

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

push(@INC, "/bin");

require 'v.pl';
require 'netutil.pl';

&querysystem();
&v_setterm();

$idata{"CDROMS"} = $found{"cdrom"};
$idata{"DISKS"} = $found{"disk"};
$idata{"NETWORK"} = $found{"network"};
$idata{"METHOD"} = $METHOD;
$idata{"IMETHOD"} = $IMETHOD;
$idata{"DMETHOD"} = $DMETHOD;
$idata{"LOC"} = "local";

if (@disks[0] eq "") {
	$TITLE = "BSD/OS Installation";
	$COMMENTS = "";
	$TEXT=
"BSD/OS requires a recognized hard disk drive in order to be installed.
No hard disk drive was recognized on this machine.";
	&hang("");
}

$EXPLAIN_INSTALL_HACK="You may press &lt;CTRL-C&gt; to abort the installation and start a shell\n\n";

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

if (-f $INSTALLDATA || -f "/cdrom/$INSTALLDATA") {
	# We've already done some stuff, so recover the data
	if (-f $INSTALLDATA) {
		&readsimple($INSTALLDATA);
	} else {
		&readsimple("/cdrom/$INSTALLDATA");
	}
	%idata = %fields;
} else {
	$TITLE="BSD/OS Installation Introduction";
	$COMMENTS="";
	&vexplain("BSD/OS Installation");

	# establish defaults
	$idata{"MEDIA"} = "cdrom";
	$idata{"LOC"} = "local";
}

$TITLE="Welcome To BSD/OS Installation";

&config_cons();

&writesimple($INSTALLDATA, %idata);

# &set_license();

&writesimple($INSTALLDATA, %idata);

# &select_express();
$BSDLOC = "http";
$METHOD = "custom";
$INSTALL= "Network Install";

&select_disksetup();
&select_install();

&writesimple($INSTALLDATA, %idata);

&select_cdrom();

&writesimple($INSTALLDATA, %idata);

&setup_disks();

&writesimple($INSTALLDATA, %idata);

&install_software();
	
&check_fstab_devs();

&vaddtext("Copying configuration data to hard-disk root...");
&writesimple($INSTALLCFG, %idata, 1);

# Get patches off the CDROM (if any)
if (-f "/cdrom/PATCHES.tar") {
	&vaddtext("Copying patches from installation cdrom...");
	mkdir("/a/var/tmp/PATCHES", 0755);
	system("cd /a/var/tmp/PATCHES; pax -rpe < /cdrom/PATCHES.tar");
} elsif ($idata{"LOC"} eq "remote") {
	local($tmp);
	$tmp = `rsh -l $netutil{RUSER} $netutil{RADDR} sh -c \\'test -f $netutil{DIRECTORY}/PATCHES.tar\\; echo \\\$?\\'`;
	chop $tmp;
	if ($tmp == 0) {
		&vaddtext("Copying patches from installation cdrom...");
		mkdir("/a/var/tmp/PATCHES", 0755);
		system("rsh -l $netutil{RUSER} $netutil{RADDR} cat $netutil{DIRECTORY}/PATCHES.tar | (cd /a/var/tmp/PATCHES; pax -rpe)");
	}
} elsif ($idata{"LOC"} eq "http") {
	local($tmp);
	if (!&sys("fetch -s -q $idata{\"HTTP\"}/PATCHES.tar 2> /dev/null > /dev/null")) {
		&vaddtext("Copying patches from installation cdrom...");
		mkdir("/a/var/tmp/PATCHES", 0755);
		system("fetch -q -o - $idata{HTTP}/PATCHES.tar | (cd /a/var/tmp/PATCHES; pax -rpe)");
	}
}

if (-x "/a/var/tmp/PATCHES/install.cdrom") {
	&v_reset();
	print "Installing patches...\n";
	system("/a/var/tmp/PATCHES/install.cdrom /a");
}

# Get patches off the floppy (if any)
if (-f "/PATCHES.tar") {
	&v_reset();
	print "Copying patches from installation floppy...\n";
	mkdir("/a/var/tmp/PATCHES", 0755);
	system("cd /a/var/tmp/PATCHES; pax -rpe < /PATCHES.tar");
	print "Installing patches...\n";
	system("/a/var/tmp/PATCHES/install.floppy /a");
}

&setpassword();

# Just to be sure...
&sys("sync");
&sys("sync");

# reboot ------------------------------------------------------------------

$TITLE="BSD/OS $INSTALL Complete";
$COMMENTS="";
$TEXT=
"Congratulations, the installation of BSD/OS is now complete!
The system will now be rebooted.  When the system reboots it will
guide you through the basic system configuration.

Please visit our ftp site for possible patches and improvements
to BSD/OS:

    ftp://ftp.bsdi.com/bsdi/patches/

Please read the \"Disaster Recovery\" chapter in the release notes
to learn about creating an emergency recovery floppy.

Please remove the boot media (floppy or cdrom) from the drive.
";
&vquery("Press <ENTER> to reboot");
&v_reset();
&umount_mounted;
&add_softdep;
exec "reboot";
sub check_cd {
	@cd_info = ("UNKNOWN", "0.0", "BINARY");
	$ver = $CDVERSION;
	if ($idata{"LOC"} eq "remote") {
		$ver = "/var/tmp/.version";
		if (&sys("rsh -l $netutil{\"RUSER\"} $netutil{\"RADDR\"} cat $netutil{\"DIRECTORY\"}/.version > $ver 2>/dev/null")) {
			return;
		}
	}
	if ($idata{"LOC"} eq "http") {
		$ver = "/var/tmp/.version";
		if (&sys("fetch -o $ver -q $idata{\"HTTP\"}/.version 2>/dev/null")) {
			return;
		}
	}
	if (open(CONF, $ver)) {
	    @cd_info = split(/\s+/, <CONF>);
	    close(CONF);
	} elsif (open(CONF, "/cdrom/CONTENTS")) {
	    while (<CONF>) {
		chop;
		if (/CD-ROM/) {
		    @tmp = split(/\s+/);
		    foreach $word (@tmp) {
			if ($word =~ /BSD/) {
			    $cd_info[0] = $word;
			} elsif ($word =~ /[0-9]+\.[0-9]/) {
			    $cd_info[1] = $word;
			} elsif ($word =~ /[Bb][Ii][Nn][Aa][Rr][Yy]/) {
			    $cd_info[2] = "BINARY";
			} elsif ($word =~ /[Ss][Oo][Uu][Rr][Cc][Ee]/) {
			    if ($cd_info[2] eq "") {
				$cd_info[2] = "SOURCE";
			    }
			}
		    }
		}
	    }
	    close(CONF);
	}

	if (!$cd_info[0]) { $cd_info[0] = "UNKNOWN"; }
	if (!$cd_info[1]) { $cd_info[1] = "0.0"; }
	if (!$cd_info[2]) { $cd_info[2] = "BINARY"; }
}
sub config_cons {
	if ($idata{"KEYBOARD"} ne "") {
		return;
	}
	opendir(D,$CONSTABD);
	@constabs = sort grep(!/^\..*$/,readdir(D));
	closedir(D);
	$idata{"KEYBOARD"} = "US";

	if ($KEYBD eq "unknown" && $constabs[1] ne "") {
		$COMMENTS="";
		$TEXT=
"BSD/OS supports different national keyboards for different countries.

The default keyboard uses the US layout, but you can choose different
layouts for different languages, eg. FR for French or DE for German.

The following choices are currently available:
    @constabs
If you don't find your keyboard listed, try the default.";

		$HELP="Type &lt;ENTER&gt; to select the US keyboard.
Other choices are FR for French or DE for German";
		$KEYBD = &vquery("Which keyboard do you have?", "US",
				(@constabs));
		if ($KEYBD ne "US") {
	 	    $idata{"KEYBOARD"} = $KEYBD;
		    system("setcons keymap $CONSTABD/$KEYBD >/tmp/errors 2>&1");
		}
	}
}
sub hang {
	local ($other) = @_;
	&vaddtext("$other
The installation has FAILED.  You should reboot the computer with the
INSTALL floppy in the floppy drive to retry the installation procedure.");
	for (;;) {
		sleep 10000;
	}
}
sub install_software {
	@cmd=();
	push(@cmd, "installsw");
	# normal args
	&v_reset();
	&sys("rm -f /var/tmp/installsw.script");
	&sys("rm -rf /a/var/tmp/installsw.script");
	&sys("mkdir -p /a/var/tmp/installsw.script");
	&sys("ln -s /a/var/tmp/installsw.script /var/tmp/installsw.script");
	push(@cmd, "-d");	# root of install tree
	push(@cmd, "/a");
	if ($idata{"LOC"} ne "http") {
		push(@cmd, "-m");	# media type
		push(@cmd, $idata{"MEDIA"});
	}
	push(@cmd, "-E");	# expert mode (turns off questions)
	if ($idata{"LOC"} eq "remote") {
		push(@cmd, "-h");	# cdrom mount point
		push(@cmd, $netutil{"RADDR"});
		push(@cmd, "-l");	# cdrom mount point
		push(@cmd, $netutil{"RUSER"});
		push(@cmd, "-c");	# cdrom mount point
		push(@cmd, $netutil{"DIRECTORY"});
	} elsif ($idata{"LOC"} eq "http") {
		push(@cmd, "-h");	# URL
		push(@cmd, $idata{"HTTP"});
		push(@cmd, "-H");	# Force HTTP
	} else {
		push(@cmd, "-L");	# it is local
		push(@cmd, "-c");	# cdrom mount point
		push(@cmd, "/cdrom");
	}

	if ($IMETHOD ne "custom") {
		push(@cmd, $INSWOPT);
	}

	for (;;) {
		&v_reset();
		print "Starting up the install procedure...\n";
		last if &sys("@cmd") == 0;
		$TEXT=
"Installsw appears to have failed for some reason.  If the failure
was caused by incorrect responses to its questions or some sort of
transient error, you should try it again now.  If the problem was 
more serious or if it recurs, you will need to fix whatever caused
the error and re-start the installation.  You should also review the
the installation procedure in the release notes

The command line was: ".@cmd;
		if ($IMETHOD ne "custom") {
			$TEXT.="
You may wish to restart the installation and use custom software
selection, rather than automatic.";
		}
		if ($idata{"LOC"} eq "local") {
		    $HELP=
"Re-running installsw will make another attempt at installing the
software from the CD-ROM.  If it continues to fail then either the
CD-ROM is damaged or there is a hardware problem with some part of
your system (perhaps too small of a disk?).  You will find and
correct the problem to continue forward.";
		} else {
		    $HELP=
"Since this is a remote install, the problem was probably an error
with the network, or with the remote machine, though it could be
an error on the local machine.  Check your network as well as the
CD-ROM drive on the remote machine.";
		}
		$_ = &vquery("Run installsw again now?", "yes", ("yes", "no"));
		if (/no/) {
			&hang("");
		}
	}
	for (;;) {
		$TEXT=
"Installation of the selected BSD/OS packages has been completed.
If you wish to install any of the packages distributed on floppies,
you may do so at this time.  If you wish to install additional
packages later, you can always use the `installfloppy' command.";
		$_ = &vquery("Do you wish to install packages from a floppy now?",
			"no", ("yes", "no"));
		if (/no/) {
			last;
		}
		&v_reset();
		system("installfloppy");
	}
	$TEXT="";
	&sys("rm -f /var/tmp/installsw.script");
	&sys("rm -rf /a/var/tmp/installsw.script");
}
sub mount_dirs_alarm {
	$TEXT=$fuzzies[$fuzindex];
	$fuzindex++;
	$fuzindex = 0 if $fuzindex > $#fuzzies;
	&vdisplay();
	alarm $FUZZOUT;
}

sub mount_dirs {
	$EZNEWFS=1;
	$ASKME=0;
	if ($DMETHOD ne "custom") {
		$TEXT=
"$INSTALL will now create the BSD/OS file systems on your disk.
This may take several minutes.  Please wait.";
		$_ = &vdisplay();
	} else {
		$TEXT=
"$INSTALL can initialize all the filesystems that have 
been defined in the disksetup step:";
		for $i (sort keys %dirs) {
			next if $types{$i} ne "ufs";
			$dev = $dirs{$i};
			$dev =~ s#/dev/##;
			$TEXT=$TEXT."\n    ($dev) $i";
		}
		$TEXT=$TEXT."
THIS WILL ERASE THE CONTENTS OF THESE FILESYSTEMS.
You may elect to initialize all, some, or none of the file systems.";
		$HELP=
"Choosing all will automatically initialize all the listed filesystems.
Choosing some will ask you about each individual file system.
Choosing none will not initialize any of the file systems.
You may escape to a shell with \"!shell\" and newfs or fsck
the file systems by hand if you choose and then choose none
in response to this question.";
		system("stty flushin");
		$_ = &vquery("Which file systems would you like to initialize?",
			     "all", ("all", "some", "none"));
		if (/some/) {
			$ASKME=1;
		} elsif (/none/) {
			$EZNEWFS=0;
		}
	}

	for $i (sort keys %dirs) {
		next if $types{$i} ne "ufs";
		$dev = $dirs{$i};
		$dev =~ s#/dev/##;
		if ($ASKME) {
			$TEXT=
"You may have $INSTALL automatically initialize $i ($dev).
You may also escape to a shell with \"!shell\" and newfs or fsck
the file system by hand and then respond no to this question.";
			$_ = &vquery("Automatically initialize $i?", "",
				     ("yes", "no"));
			if (/yes/) {
				$EZNEWFS=1;
			} else {
				$EZNEWFS=0;
			}
			
		}
		if ($EZNEWFS) {
			$TEXT="Initializing filesystem $dev.  Please wait...";
			&vdisplay();
		}
		for (;$EZNEWFS;) {
			# Run the newfs as a child ourselves so we
			# can wait on it and still print warm fuzzies
			if (($pid = fork) == 0) {
				# Child
				if ($i =~ /^\/$/) {
					$SoftDisks[$iSoftDisks++] = "/dev/r$dev";
					exec("newfs -U nosoft /dev/r$dev >/tmp/errors 2>&1");
				}
				else {
					# This was with softdep originally
					$SoftDisks[$iSoftDisks++] = "/dev/r$dev";
					exec("newfs -U nosoft /dev/r$dev >/tmp/errors 2>&1");
				}
			}

			# Parent
			$SIG{'ALRM'} = 'mount_dirs_alarm';
			alarm $FUZZOUT;	# XXX (hard coded timeout?)
			waitpid($pid, 0);	# wait restarts automatically
			alarm 0;
			$SIG{'ALRM'} = 'DEFAULT';
			last if $? == 0;	# newfs was successful?
			$TEXT=
"$INSTALL failed to create a filesystem.
$INSTALL will try again, though it will most likely fail.
You should investigate the problem and try $INSTALL
after you have resolved the problem.";
			if (open(IN, "/tmp/errors")) {
				while (<IN>) {
					chop;
					s/\t/ /g;
					$TEXT=$TEXT."\n$_";
				}
				close(IN);
			}
			&vquery("Press <ENTER> to continue:");
		}
	}
	
	# mount filesystems
	for $i (sort keys %dirs) {
		$dir = "/a";
		split(/\//, $i);
		for $j (@_) {
			$dir .= "/" . $j;
			if (! -d $dir && mkdir($dir, 0755) == 0) {
				$TEXT="Failed to create $dir\n$!";
				&hang("");
			}
		}
		next if $types{$i} ne "ufs";
		if (&sys("mount -o async $dirs{$i} /a/$i >/dev/null 2>&1")) {
			$TEXT="Failed to mount $dirs{$i} on /a/$i\n$!";
			&hang("");
		}
	}
}

sub check_mounted_disk {
	local($disk) = @_;

	$mounted = 0;
	if (open(MNT, "mount|")) {
		while (<MNT>) {
			($dev, $on, $mpt, $flags) = split;
			if ($dev =~ /\/dev\/$disk/) {
				$mounted=1;
				last
			}
		}
		close(MNT);
	}
	return $mounted;
}

sub check_mounted {
	$root = "/a";
	$mounted = 0;
	if (open(MNT, "mount|")) {
		while (<MNT>) {
			($dev, $on, $mpt, $flags) = split;
			if ($mnt =~ /^$root/) {
				$mounted=1;
				last
			}
		}
		close(MNT);
	}
	return $mounted;
}

sub umount_mounted_disk {
	local($disk) = @_;
	local(@dirs, $i);
	@dirs = ();
	$i = 0;

	$mounted = 0;
	if (open(MNT, "mount|")) {
		while (<MNT>) {
print "Read $_\n";
			($dev, $on, $mpt, $flags) = split;
			if ($dev =~ /\/dev\/$disk/) {
print "Added $mpt\n";
				$dirs[$i] = $mpt;
				$i++;
			}
		}
		close(MNT);
	}
	while ($i-- > 0) {
print "unmount $dirs[$i]";
		system("umount -f $dirs[$i]");
	}
}

sub umount_mounted {
	local(@dirs, $i);

	$root = "/a";
	$mounted = 0;
	@dirs = ();
	$i = 0;

	if (open(MNT, "mount|")) {
		while (<MNT>) {
			($dev, $on, $mpt, $flags) = split;
			if ($mpt =~ /^$root/) {
				$dirs[$i] = $mpt;
				$i++;
			}
		}
		close(MNT);
	}
	while ($i-- > 0) {
		system("umount -f $dirs[$i]");
	}
}

sub add_softdep {
	while ($iSoftDisks--) {
		print "Turning on soft dependencies for $SoftDisks[$iSoftDisks]\n";
		system("tunefs -n enbale $SoftDisks[$SoftDisks]");
	}
	sleep(15);
}

sub run_diskdefect {
	@wd = sort keys %wd_devs;
	for $i (@wd) {
		$status = &sys("diskdefect $i >/var/tmp/diskdefect_hack 2>&1");
		$TITLE="BSD/OS Hard Drive Setup";
		$COMMENTS="";
		if ($status != 0 && $DMETHOD eq "custom") {
			$TEXT=
"Modern IDE drives provide \"Perfect Media\" to BSD/OS.  That is,
it will appear as if there are no bad sectors on the disk.
ESDI drives do not provide \"Perfect Media.\"  If your this
drive ($i) is an ESDI disk or an older IDE disk you should have
the disk scanned for bad sectors.  

Scanning the disk may take some time.";
			$HELP=
"Answering no to this question will cause an empty bad-block table
to written to the drive.  This should be fine for IDE drives.
Answering yes will cause the diskdefect program to scan the drive
for bad sectors and remap them to the bsd-block table at the end
of the disk.";
			$_ = &vquery("Scan the disk for bad sectors?", "no", ("yes", "no"));
			if ($_ eq "yes") {
				&v_clear();
				print "Scanning $i for bad sectors...\n";

				if (&sys("diskdefect -svw $i")) {
			    	    &v_reset();
				    die "diskdefect failed -- aborting installation\n";
				}
			} elsif (&sys("diskdefect -y $i 0 > /dev/null 2>&1")) {
				&v_reset();
				die "diskdefect failed -- aborting installation\n";
			}
		} elsif ($status != 0) {
			if (&sys("diskdefect -y $i 0 > /dev/null 2>&1")) {
			    &v_reset();
			    die "diskdefect failed -- aborting installation\n";
			}
		}
	}
}
sub run_disksetup {
	$bdisk="";
    	@disks = sort(split(/\s/,$idata{"DISKS"}));
	$TITLE="BSD/OS Disk Drive Setup";
	$COMMENTS="The root (/) partition should be on the boot drive!";
	$TEXT="The disks discovered on your system are:\n    @disks\n";

	foreach $d (@disks) {
		$HELP=
"The boot drive is the drive that has C: assigned to it when running DOS.
It is the drive that the machine first accesses when booting.  If an IDE
drive is defined in the CMOS then the primary IDE drive will be your
boot drive.  If there are no IDE drives defined in the CMOS then it must
be the first SCSI drive the is the boot drive.";
		$_ = &vquery("Configure $d?","yes",("yes","no"));
		next if /no/;
		if ( ! -b "/dev/${d}a" ) {
			&make_disk_dev("/dev", ${d});
		}
		if (&check_mounted_disk($d)) {
			$OLDTEXT=$TEXT;
			$TEXT=
"There are currently filesystems mounted from this disk.  Disksetup
should not be run on disks which have mounted filesystems.";
			$_ = &vquery("unmount filesystems on $d?",
				"yes", ("yes","no"));
			if (/yes/) {
				&umount_mounted_disk($d);
			}
			$TEXT=$OLDTEXT;
		}
		if ($d eq "amir0") {
			$OLDTEXT=$TEXT;
			$TEXT="";
			$_ = &vquery("Is amir0 the boot disk?",
				"yes", ("yes","no"));
			if (/yes/) {
				$bdisk = "amir0";
			}
			$TEXT=$OLDTEXT;
		}
		&v_reset();
		&sys("disksetup -t $FSTABD -i $d 2> /tmp/disksetup.errors");
	}
}
sub run_ezdisksetup {
	$bdisk="";
	@bootdisks = ( );
	foreach $d (sort @disks) {
		if ($d =~ /[rd]0/) {
			push(@bootdisks, $d)
		}
	}

	$bdisk = @bootdisks[0];
	if ($#bootdisks > 0) {
		$TITLE="BSD/OS Boot Drive Discovery";
		$COMMENTS=
"$INSTALL requires BSD/OS to be installed on the Boot Drive";
		$TEXT=
"$INSTALL has detected more than one disk that may be the
boot drive.  BSD/OS must be installed on the boot drive.  The
possible boot drives are:
";
		for ($i = 0; $i <= $#bootdisks; $i += 1) {
			if ($bootdisks[$i] eq "sd0") {
				$bootdisks[$i] = "SCSI";
				$TEXT.="
        SCSI Disk";
			} elsif ($bootdisks[$i] eq "sr0") {
				$bootdisks[$i] = "Removable";
				$TEXT.="
        Removable SCSI Disk";
			} elsif ($bootdisks[$i] eq "wd0") {
				$bdisk = "IDE";
				$bootdisks[$i] = "IDE";
				$TEXT.="
        IDE Disk";
			} elsif ($bootdisks[$i] eq "amir0") {
				$bdisk = "AMIRAID";
				$bootdisks[$i] = "AMIRAID";
				$TEXT.="
        AMI MegaRAID Disk";
			} else {
				$TEXT.="
        $bootdisks[$i] Disk";
			}
			if ($i == 0) {
				$bdisk = $bootdisk[$i];
			}
		}
		$HELP=
"The boot drive is the drive that has C: assigned to it when running DOS.
It is the drive that the machine first accesses when booting.  If an IDE
drive is defined in the CMOS then the primary IDE drive will be your
boot drive.  If there are no IDE drives defined in the CMOS then it must
be the first SCSI drive the is the boot drive.";
		$bdisk = &vquery("Which type of disk do you boot from?", $bdisk,
			@bootdisks);
		if ($bdisk eq "SCSI") {
			$bdisk = "sd0";
		} elsif ($bdisk eq "IDE") {
			$bdisk = "wd0";
		} elsif ($bdisk eq "AMIRAID") {
			$bdisk = "amir0";
		} elsif ($bdisk eq "Removable") {
			$bdisk = "sr0";
		}
	}

	if ($bdisk =~ /[rd]0/) {
		$TITLE="BSD/OS Hard Drive Setup";
		# Make sure dev entry exists before running disksetup
		if (! -b "/dev/${bdisk}a") {
			&make_disk_dev("/dev",$bdisk);
		}
		if ($capacity{$bdisk}) {
			$AVAIL=$capacity{$bdisk} - $NEEDSPACE;
			$RECDOS=int($AVAIL / 10);
			if ($RECDOS < 5) {
				$RECDOS = 5;
			}
			if ($RECDOS > $AVAIL) {
				$RECDOS = 0;
			}
			if ($AVAIL < 0) {
				$TITLE="BSD/OS $INSTALL";
				$COMMENTS="";
				$TEXT=
"BSD/OS requires at least $NEEDSPACE MB of available space to install
using Express Install/Automatic disk setup.  You will need to use
Custom Install/Custom disk setup for installing on this disk.";
				&hang("");
			}
			if ($ALLOWDOS) {
				$COMMENTS=
"BSD/OS will be installed on the boot drive of this machine.
It requires $NEEDSPACE MB of the available $capacity{$bdisk} MB on the disk.
Space may be reserved to allow the loading of DOS at a later time.";
				$TEXT=
"Would you like to share this disk with DOS?  If so, you may assign
up to $AVAIL MB of space to DOS.  $INSTALL would recommend
$RECDOS MB for DOS.";
				$HELP=
"Reserving room for DOS may be useful if you have hardware that needs to
be initialized from a DOS executable, or if you want to play DOOM every
now and then.";
				$_= &vquery("Reserve space for DOS?", "no",
				("yes", "no"));
				if ($_ eq "yes") {
					$HELP=
"DOS typically requires at least 5MB.  You will want to make sure that
you leave enough space in BSD/OS for your files and any future software
you may desire to load.";
					$DOSSPACE = &vquery("Reserve how much space (in MB)?", $RECDOS);
				} else {
					$DOSSPACE="0";
				}
			} else {
				$DOSSPACE="0";
			}
			$COMMENTS="";
			if ($bdisk =~ /s/) {
				$drivetype = "SCSI";
			} else {
				$drivetype = "IDE";
			}
			$TEXT=
"$INSTALL will now reconfigure your $drivetype $bdisk hard drive.

THIS WILL ERASE ANY DATA STORED ON THE DRIVE.  IF YOU HAVE NOT BACKED
UP ANY DATA YOU WISH SAVED THEN YOU SHOULD REMOVE THE FLOPPY DISK FROM
THE DRIVE AND REBOOT YOUR COMPUTER NOW.

Please enter \"erase\" to continue on, or \"no\" to halt the installation.
Enter \"custom\" to use the custom disksetup facility.";

			&umount_mounted_disk($bdisk);

			for (;;) {
				$HELP=
"$INSTALL must reconfigure the drive to install BSD/OS.
If you do not want to reconfigure the drive you should remove the floppy
disk from the drive and reboot your computer.

If you want to configure your disks by hand, enter \"custom\" to use
the custom disksetup facility.";
				$_ = &vquery("Erase and reconfigure drive for BSD/OS?", "", ("erase", "no", "custom"));
				if ($_ eq "erase") {
					last;
				}
				if ($_ eq "custom") {
					$DMETHOD = "custom";
					return;
				}
				$COMMENTS=
"$INSTALL must reconfigure the drive to install BSD/OS.
If you do not want to reconfigure the drive you should remove the floppy
disk from the drive and reboot your computer.";
			}

			&sys("disksetup -p biosboot -q bootbios -Z $DOSSPACE -Q -S -t $FSTABD $bdisk 2> /tmp/disksetup.errors");
		} else {
			&v_reset();
			&sys("disksetup -p biosboot -q bootbios -S -t $FSTABD $bdisk 2> /tmp/disksetup.errors");
		}
	} else {
		$TITLE="BSD/OS $INSTALL";
		$COMMENTS="";
		$TEXT=
"This is strange, this machine appears to have neither a bootable
IDE nor a bootable SCSI disk, and yet there are disks detected.
Please review your hardware setup.  If you find nothing wrong,
please contact support.";
		&hang("");

	}
}
sub select_cdrom {
	if ($BSDLOC eq "nfs") {
		$idata{"LOC"} = "nfs";
	} elsif ($BSDLOC eq "http") {
		$idata{"LOC"} = "http";
	} elsif (@cdroms[0] ne "" && $METHOD eq "custom") {
		$TITLE="BSD/OS $INSTALL";
		$COMMENTS="";
		$TEXT=
"$INSTALL has determined there is a local CD-ROM drive on this
machine.  It is highly recommended that you install from a local CD-ROM
drive.  The alternative to installing from a local CD-ROM drive is
to install from a remote CD-ROM drive.  This requires the setup of an
network interface and rsh access to a machine which has a CD-ROM drive.";
		$HELP=
"$INSTALL can install from either a CD-ROM drive on the local
machine or from a CD-ROM drive on a remote machine to which you have
rsh access and the ability to mount the BSD/OS BINARY CD-ROM.  The
remote machine must support the Rock Ridge extensions in order for
$INSTALL to succeeded.

A local installation is always the best choice.";
		$_ = &vquery("Local or Remote install", $idata{"LOC"},
			("local", "remote"));
		$idata{"LOC"} = $_;
	} elsif (@cdroms[0] eq "") {
		$idata{"LOC"} = "remote";
	} elsif ($METHOD eq "http") {
		$idata{"LOC"} = "http";
	} else {
		$idata{"LOC"} = "local";
	}

	if ($idata{"LOC"} eq "local") {
		$TITLE="BSD/OS CD-ROM Discovery";
		$COMMENTS="$INSTALL will now locate the BSD/OS BINARY CD-ROM";
		$idata{"CDROM"}="";
		while ($idata{"CDROM"} eq "") {
			$TEXT="";
			$cd_info[0]="";
			$cd_info[1]="";
			$cd_info[2]="";
			$ANY=0;
			foreach $i (@cdroms) {
				if ($idata{"CDROM"} eq "") {
					if (! -b "/dev/${i}a") {
						&make_disk_dev("/dev",${i});
					}
					&vaddtext("checking ${i}...");
					system("umount /cdrom > /tmp/errors 2>&1");
					if (system("mount_cd9660 /dev/${i}a /cdrom > /dev/null 2> /dev/null")) {
						next;
					}
					$ANY=1;

					&check_cd();
					if ($cd_info[0] eq $SYSTEM &&
					    $cd_info[1] eq $VERSION &&
					    $cd_info[2] eq $TYPE) {
						$idata{"CDROM"} = $i;
					}
				}
			}
			$HELP=
"$INSTALL must have access to the CD-ROM that is labeled
$SYSTEM $VERSION $TYPE.  This CD-ROM contains all the binaries that
$INSTALL will install on the disk.  Please make sure that
CD-ROM is correctly inserted in the CD-ROM drive.";
			if ($ANY == 0) {
				$TEXT=
"Please insert the $SYSTEM $VERSION $TYPE CD into your CD-ROM drive.";
			} elsif ($cd_info[0] ne $SYSTEM) {
				$TEXT=
"Please remove the CD from your CD-ROM drive
and insert the $SYSTEM $VERSION $TYPE CD.";
			} elsif ($cd_info[1] ne $VERSION) {
				$TEXT=
"This appears to be the wrong version of the $SYSTEM $TYPE CD.
Please remove the CD from your CD-ROM drive
and insert the $SYSTEM $VERSION $TYPE CD.";
			} elsif ($cd_info[2] ne $TYPE) {
				$TEXT=
"Please remove the BSD/OS SOURCE CD from your CD-ROM drive
and insert the $SYSTEM $VERSION $TYPE CD.";
			}
			if ($idata{"CDROM"} eq "") {
				&vquery("Press <Enter> when ready");
			}
		}
	} elsif ($idata{"LOC"} ne "http") {
		$COMMENTS=
"$INSTALL needs to configure your network for a $idata{\"LOC\"} install.";
		&setup_network();
	}

	if ($idata{"LOC"} eq "nfs") {
		$TITLE="BSD/OS $INSTALL NFS Setup";
		$TEXT=
"$INSTALL needs to NFS mount the CD-ROM from the remote machine.
You may use the \"!shell\" escape to mount it yourself, or you can have
$INSTALL automatically mount it for you.";
		$COMMENTS="";
		while (! -f $BSDOSCD ) {
			$_ = &vquery("Automatically mount the remote CD-ROM?",
				      "yes", ("yes", "no"));
			if ($_ eq "yes") {
				system("umount /cdrom > /dev/null 2>&1");
				system("mount_nfs $netutil{\"RADDR\"}:$netutil{\"DIRECTORY\"} /cdrom > /dev/null 2>&1");
			}
			$COMMENTS=
"Make sure $netutil{\"RADDR\"} has exported $netutil{\"DIRECTORY\"}
Also make sure that the BSD/OS BINARY CD-ROM is mounted on $netutil{\"DIRECTORY\"}.";
		}
	}
}
sub select_disksetup {
	if ($DMETHOD eq "unknown") {
		$COMMENTS="";
		$TEXT=
"The disks onto which BSD/OS will be installed may be setup by either
Automatic or Custom disk setup.

Automatic disk setup does all the work of preparing the main (boot)
drive of the system for installation of the BSD/OS software.  It can
also create a DOS partition onto which DOS may be installed later.
Automatic disksetup should be acceptable for most new installations.

Custom disk setup may be used in more demanding situations.  It has
increased flexibility in terms of setting up disks and filesystems
at the cost of added complexity.

To upgrade an existing BSD/OS system, remove the floppy diskette from
the floppy drive and shut down this computer.  Read through the Upgrade
instructions in the release notes and follow them to upgrade your system.";

		$HELP=
"Automatic disk setup will prepare the boot drive for the BSD/OS software,
and not ask too many questions about configuration.  This is the best
choice for people who want to install just BSD/OS on the disk.

Custom disk setup is for those who want to be able to control all the
knobs or use more elaborate disk layouts.  This requires some basic
understanding of BSD/OS and how file systems are set up.";

		$DMETHOD = &vquery("Use Automatic or Custom disk setup?",
			$idata{"DMETHOD"}, ("automatic", "custom"));
		$idata{"DMETHOD"} = $DMETHOD;
	}
}
sub select_express {
	if ($idata{"METHOD"} eq "unknown") {
		$idata{"METHOD"} = "express";
	}
	if ($METHOD eq "unknown") {
		$COMMENTS="";
		$TEXT=
"BSD/OS may be installed by using either Express or Custom Installation.

Express Installation installs everything you need to run BSD/OS from
the main (boot) drive on the system.  Express Installation can also
create a DOS partition onto which DOS may be installed later.
Express Installation should be acceptable for most situations.
        
Custom Installation enables installation in more demanding situations.
It has increased flexibility in terms of setting up disks and selecting
the software packages to install, at the cost of added complexity.

To upgrade an existing BSD/OS system, remove the floppy diskette from
the floppy drive and shut down this computer.  Read through the Upgrade
instructions in the release notes and follow them to upgrade your system.";


		$HELP=
"Express Installations will install BSD/OS on the boot drive, and not
ask too many questions about configuration.  This is the best choice
for people who want to install just BSD/OS on the disk.  The software
packages most commonly used will be selected.

Custom Installation is for those who want to be able to control all the
knobs or use more elaborate disk layouts.  This requires some basic
understanding of BSD/OS and how file systems are set up.

You should use the upgrade procedure outlined in the Release Notes
to upgrade an existing BSD/OS 2.1 system.";

		$METHOD = &vquery("Use Express or Custom Installation?",
		    $idata{"METHOD"},
		    ("express", "custom", "floppy", "nfs", "http", "recover"));
		$idata{"METHOD"} = $METHOD;
	}
	if ($METHOD eq "recover") {
		$TEXT=
"Please remove the INSTALL floppy from the disk drive and insert
the RECOVERY floppy.  An image of the recovery floppy can be found
in the FLOPPIES directory on the CD-ROM.  Please see the README file
there for instructions on creating a RECOVERY floppy.  If you do
not have a RECOVERY floppy, press &lt;CTRL-C&gt; to get to a shell prompt.";
		&vquery("Press <ENTER> to continue:");
		&v_reset();
		system("mount -r /dev/fd0a /floppy && /floppy/setup");
		exit 0
	}
	if ($METHOD eq "floppy" || $METHOD eq "nfs" || $METHOD eq "http") {
		$BSDLOC = $METHOD;
		$METHOD = "custom";
	}
	if ($METHOD eq "http") {
		$idata{"MEDIA"} = "http";
		$idata{"LOC"} = "http";
	}
	if ($METHOD eq "express") {
		$IMETHOD="automatic";
		$DMETHOD="automatic";
		$INSTALL="Express Installation";
	} else {
		$IMETHOD="unknown";
		$DMETHOD="unknown";
		$INSTALL="Custom Installation";
		$TITLE="BSD/OS $INSTALL Information";
		$COMMENTS="";
		$TEXT=
"At any time during the installation that you are prompted to
either press &lt;ENTER&gt; or answer a question, you may respond with
the string \"!shell\" (you do not type the \" marks) followed
by &lt;ENTER&gt; and a shell will be spawned.  When you exit the shell
you will be re-asked the question to which you typed \"!shell\".";
		&vquery("Press <ENTER> to continue:");
	}
	if ($idata{"IMETHOD"} eq "unknown") {
		$idata{"IMETHOD"} = "automatic";
	}
	if ($idata{"DMETHOD"} eq "unknown") {
		$idata{"DMETHOD"} = "automatic";
	}
}
sub select_install {
	if ($IMETHOD eq "unknown") {
		$COMMENTS="";
		$TEXT=
"The BSD/OS software to be installed may be selected by either
Automatic or Custom software selection.

Automatic software selection selects everything you need to run BSD/OS.
This should be acceptable for most situations.

Custom software selection allows the selection of which packages to
install.  This typically is not needed.";

		$HELP=
"Automatic software selection will cause all the standard BSD/OS
software to be installed, which should be sufficient for most
installations.  This is typically the best choice.

Custom software selection should only be used after reading the
installation notes and determining that the standard software is
not the correct choice for this machine.";

		$IMETHOD = &vquery("Use Automatic or Custom software selection?",
			$idata{"IMETHOD"}, ("automatic", "custom"));
		$idata{"IMETHOD"} = $IMETHOD;
	}
}
sub set_license {
    $COMMENTS="";
    while (1) {
	$TEXT=
"BSD/OS now supports licenses to help administrators determine if they
are exceeding their licensed number of users.  Licenses from BSDI also
include a hostid which is unique among all BSDI licensed machines.
While a license is not required to install and use the BSD/OS operating
system, entering your license now will prevent several warning messages
from being printed each time the system is brought up as well as when
more than a single unique user is using the machine.  To install without
a license, enter the license of \"none\".";

	$HELP=$TEXT;
	$LICENSE="";
	if (open (IN, "/etc/license")) {
		$LICENSE=<IN>;
		chop $LICENSE;
		$LICENSE =~ s/\ $//g;
		close(IN);
	}

	$LICENSE = &vquery("Please enter the BSD/OS license:", $LICENSE);
	$LICENSE =~ s/\ //g;
	if ($LICENSE eq "none") {
		return;
	}
	if (!system("/bin/sysctl -w kern.license=$LICENSE > /dev/null 2>&1")) {
		system("/bin/sysctl -n kern.license > /etc/license");
		return;
	}
        $COMMENTS="The license \"$LICENSE\" is invalid.";
    }
}
sub setpassword {
	$TITLE="BSD/OS Root Password Assignment";
	$TEXT=
"Installation of the BSD/OS software is now complete.  Before the system
is brought up for the first time, a password for the root account should
be assigned.  The root account, also known as \"Super User\", has complete
privileges on the system and therefore should be limited to only trusted
system administrators.  To prevent unauthorized access to this system a
root password must now be assigned.";
	&vquery("Press <ENTER> to continue");

	if ($ENV{'TERM'} ne "vt100") {
		$TEXT=
"BSD/OS can be easily and securely configured by using the new
MAXIM web administration tool.  Due to limitations in various HTML
browsers, including Netscape, which are used by MAXIM, the root
password should not contain non-printable characters (control
characters and the escape characters) if you are going to use MAXIM.

Because of this limitation we have enabled wide passwords (up to
128 significant characters) for the root account, by default.";
		&vquery("Press <ENTER> to continue");
	}

	&v_reset();
        for (;;) {
                print <<ETX;
Please set the super-user (root) password for this machine.  The
password will not be displayed on the screen.

ETX
                last if !system("/a/usr/sbin/chroot /a /usr/bin/passwd -l root");
        }
}
sub setup_disks {
# run disksetup if necessary ----------------------------------------------

	if ($DMETHOD ne "custom") {
		if (&check_mounted) {
			&umount_mounted;
		}
		&run_ezdisksetup;
	}

	# run_ezdisksetup might reset us back to custom...

	if ($DMETHOD eq "custom") {
		&run_disksetup;
	}

	$idata{"DSETUP"} = 1;
	&writesimple($INSTALLDATA, %idata);

# generate reasonable fstab -----------------------------------------------

	$TITLE="BSD/OS Hard Drive Setup";
	$COMMENTS="";

	if (-f $FSTABX) {
		if ($DMETHOD ne "custom") {
			unlink($FSTABX);
		} else {
			$TEXT.=
"There is already an fstab file from a previous run of setup.
It is possible this file is no longer valid.";
			$_ = &vquery("Remove the old fstab and rebuild it?",
			    "yes", ("yes", "no"));
			if (/yes/) {
				unlink($FSTABX);
			}
		}
	}

	%filesys = ();
	%devices = ();
	@FSTAB = ();
	$fstabi = 0;
	$swapdev = undef;
	%wd_devs = ();
	%dirs = ();

	if (-f $FSTABX) {
	    if (!open(IN, $FSTABX)) {
"Could not reopen the $FSTABX file.  $INSTALL cannot continue.";
		&hang("");
	    }
	    &crack_fstab(0);
	    close(IN);
	} else {
	    $_ = `echo ${FSTABD}*`;
	    chop;
	    if ($_ ne "${FSTABD}\*") {
		    @dfiles = split;
	    } else {
		    @dfiles = ();
	    }
	    if ($#dfiles >= 0 && ! -f $FSTABX) {
		for $i (@dfiles) {
			if (!open (IN, $i)) {
				printf("Could not open $i: $!");
				exit 1;
			}
			&crack_fstab(1);
			close(IN);
		}

		if (!open (OUT, ">$FSTABX")) {
			&hang("\nCould not open $FSTABX for writing ($!)");
		}
		print OUT <<ETX;
#
# Filesystem mount table information.  See the fstab(5) man page
# and the /etc/fstab.sample file for more information and examples.
# 
# Each line is of the form:
#
#  device       mount_point     type    flags   dump    fsck_pass
#
# Note that multiple flags (when used) are specified as a 
# comma separated list without spaces.  
#
# Blank lines and lines beginning with `#' are comments.
#
ETX
		if ($FSTAB[0] ne "") {
			print OUT @FSTAB;
		} else {
			print OUT <<ETX;
# Uncomment the appropriate lines below and/or add appropriate
# lines before continuing with the installation.
#
# The following are sample entries for wd0 and sd0 root/usr partitions:
#
# /dev/wd0a	/		ufs	rw	1 1
# /dev/wd0h	/usr		ufs	rw	1 2
#
# /dev/sd0a	/		ufs	rw	1 1
# /dev/sd0h	/usr		ufs	rw	1 2
#
ETX
		}
		if ($idata{"CDROM"} ne "" && $idata{"LOC"} eq "local") {
			$filesys{"/cdrom"}++;
			print OUT "/dev/$idata{\"CDROM\"}a\t/cdrom\tcd9660\tna,ro,noexec,nosuid,nodev\t0 0\n";
		}
	    }
	    $MFSSIZE = 0;
	    if ($swapdev ne "" && $MEMSIZE >= 15*1024*1024 &&
		$filesys{"/tmp"} == 0) {
		    $MFSSIZE = 16;
	    }
	    if ($swapdev ne "" && $METHOD eq "custom" &&
				  $filesys{"/tmp"} == 0) {
		$COMMENTS="A MFS based /tmp can improve performance";
		$TEXT=
"Systems with at least 16MB of memory may choose to have /tmp mounted in
on a Memory base File System.  This may reduce the amount of swap space
on the system up to the size of /tmp.  A size of 0 will prevent /tmp from
being mounted on an MFS.";
		for (;;) {
			$HELP=
"A memory based /tmp can help improve performance if there is available
memory.  When the system runs out of physical memory, pages of memory
which are not currently in use will be temporarily stored in the swap
partition.  Even when the system runs low on physical memory, a MFS
based /tmp can still help.  In general, a system with at least 16MB of
memory can benefit from a MFS based /tmp.  Typically it should be at
least 16MB.";
			$_ = &vquery("How many MB should be used for an MFS based /tmp?", $MFSSIZE);
			last if /^\d*$/;
			$INVALID="Invalid entry: $_";
		}
		$MFSSIZE = $_;
		$COMMENTS="";
		$HELP="";
	    }
	    if ($MFSSIZE != 0) {
		$MFSSIZE = $MFSSIZE * 2 * 1024;
		$filesys{"/tmp"}++;
		print OUT "$swapdev\t/tmp\t\tmfs\trw,-s=$MFSSIZE\t0 0\n";
	    }
	    close(OUT);
	}

	$first_time = 1;
	for (;;) {
		%filesys = ();
		%devices = ();
		@FSTAB = ();
		$fstabi = 0;
		$swapdev = undef;
		%wd_devs = ();
		%dirs = ();
		if (!open(IN, $FSTABX)) {
			$TEXT=
"Could not reopen the $FSTABX file.  $INSTALL cannot continue.";
			&hang("");
		}
		&crack_fstab(0);
		&sort_fstab;
		close(IN);
		unlink($FSTABX);
		if (!open (OUT, ">$FSTABX")) {
			&hang("\nCould not open $FSTABX for writing ($!)");
		}
		print OUT @FSTAB;
		close(OUT);

		$TEXT=
"The following potential problems were discovered in the generated
/etc/fstab file:

";
		$no_way = 0;
		$okay = 1;
		if ($FSTAB[0] eq "") {
			$no_way = 1;
			$okay = 0;
			$TEXT.=
"For some reason no filesystem data is available from disksetup.  You
will need to edit the fstab by hand.  If you used the default BSD label
in disksetup (one disk with the `a' partition for root and the `h'
partition for /usr), you will simply need to uncomment the appropriate
lines for your type of root drive (e.g., wd0 or sd0).";
		}
		if ($filesys{"/"} < 1) {
			$no_way = 1;
			$okay = 0;
			$TEXT.=
"Missing root (/) filesystem.
";
		}
		if ($first_time && $filesys{"/usr"} < 1) {
			$TEXT.=
"Missing /usr filesystem.
";
			$okay = 0;
			$first_time = 0;
		}
		foreach $fs (keys(%filesys)) {
			if ($filesys{$fs} > 1) {
				$okay = 0;
				$TEXT.=
"Filesystem $fs is multiply defined.
";
				if ($fs eq "/") {
					$no_way = 1;
				}
			}
		}
		foreach $fs (keys(%devices)) {
			if ($devices{$fs} > 1) {
				$okay = 0;
				$TEXT.=
"Device $fs is multiply defined.
";
			}
		}
		if ($okay) {
			last;
		}
		if ($no_way) {
			&vquery("Press <ENTER> to continue:");
		} else {
			$_ = &vquery("Edit the fstab to correct these conditions?",
			"yes", ("yes", "no"));
			if (/no/) {
				last;
			}
		}

		$COMMENTS=
"The installation procedure will now invoke the editor on the current
fstab file.  You should create valid fstab lines for all local
(ufs) filesystems at this time.  Valid entries for / (the root
filesystem) and /usr must exist before you exit the editor.";
		$TEXT=
"A choice of editors is available.  Common choices are pico (an easy
to use editor with on screen help), vi (the standard screen oriented
editor), ed (the standard line-oriented editor), or jove (an emacs
like editor).

If you don't know what editor you want, try pico (the default).";

		$HELP=
"Typically the lines required for an IDE drive will look like:

/dev/wd0a  /     ufs  rw  1 1
/dev/wd0h  /usr  ufs  rw  1 2

and on a SCSI drive will be:

/dev/sd0a  /     ufs  rw  1 1
/dev/sd0h  /usr  ufs  rw  1 2";

		for (;;) {
			if ($EDITOR eq "") {
				$EDITOR = "pico";
			}
			$EDITOR = &vquery("Which editor should be used?",
			    $EDITOR, ("pico", "vi", "ed", "jove"));

			&v_reset();
			if (system("$EDITOR $FSTABX") == 0) {
				last;
			}
			$TEXT=
"For some reason $EDITOR failed.  Please try a different editor";
		}
 	}

	$TEXT="";

# run diskdefect (if necessary) -------------------------------------------

	# &run_diskdefect;
	&writesimple($INSTALLDATA, %idata);

# swapon hard disk swap ---------------------------------------------------

	if ($swapdev ne "") {
		&vaddtext("Adding $swapdev swap space...");
		if (&sys("swapon $swapdev")) {
			&vaddtext("Failed -- continuing anyway");
			sleep(5);
		}
		$idata{"SWAPON"} = 1;
		&writesimple($INSTALLDATA, %idata);
	}

# run newfs or fsck -------------------------------------------------------

	$TITLE="BSD/OS $INSTALL File System Initialization";
	$COMMENTS="";
	if (&check_mounted) {
		$TEXT=
"It appears that the filesystems are already mounted from a previous
invocation of the install procedure.  If you would like to start over by
running newfs (to destroy any pre-existing data) or fsck (to check
that there are no inconsistencies in the filesystem structure),
you may do so at this time.";
		$_ = &vquery("Unmount the filesystems to newfs or fsck again?",
			"no", ("yes", "no"));
		if (/yes/) {
			&umount_mounted;
			&mount_dirs;
		}
	} else {
		&mount_dirs;
	}

# fix up /var -------------------------------------------------------------

	if ($filesys{"/var"} == 0 && system("[ -d /a/var ]") != 0) {
		if ($DMETHOD eq "custom") {
			$TITLE="BSD/OS /var Setup";
			$COMMENTS="";
			$TEXT =
"Since you did not install /var on a filesystem of its own, 
BSD/OS will default to placing the /var data in /usr/var.  It is 
generally undesirable to place the /var data in the root filesystem
(due to its small size).  If you don't know where you want to place
/var, accept the default (/usr/var).  You can always move it
in the future.";
			$varlink = &vquery("Where should the /var data be placed?", 
				"/usr/var");
			$varlink =~ s#^/+##;	# make it relative
		} else {
			$varlink = "usr/var";
		}
		$TEXT="";
		if (system("[ -d /a/$varlink ] || mkdir -p /a/$varlink") != 0) {
			$TEXT="Failed to create directory /a/$varlink.\n$!";
		} elsif ($varlink ne "var" && !symlink($varlink, "/a/var")) {
			$TEXT="Failed to create symlink for /a/var.\n$!";
		}
		if ($TEXT ne "") {
			&hang("");
		}
	}

	$TITLE="BSD/OS Configuration File Setup";
	$COMMENTS="";
	$HELP="";
	$TEXT="Copying configuration files to hard-disk root...";
	&vdisplay();

# create /var/db --------------------------------------------------------------
	&vaddtext("\nCreating /var/db");
	system("[ -d /a/var/db ] || mkdir /a/var/db");

# add /etc/license ------------------------------------------------------------

	&vaddtext("\nCopying license to hard-disk root...");
	system("[ -d /a/etc ] || mkdir /a/etc");
	system("[ -f /etc/license ] && cat < /etc/license > /a/etc/license && chmod 400 /a/etc/license");

# setup keyboard.map  ---------------------------------------------------------

	if (-f "$CONSTABD/$idata{\"KEYBOARD\"}") {
	    &vaddtext("\nCopying keyboard settings to /etc/keyboard.map ...");
	    if (&sys("cat $CONSTABD/$idata{\"KEYBOARD\"} > /a/etc/keyboard.map")) {
		&vaddtext("
Failed to copy $CONSTABD/$idata{\"KEYBOARD\"} to /etc/keyboard.map.
$!");
	    }
	    chmod (0644,"/a/etc/keyboard.map");
	}

# copy fstab to hard disk root ------------------------------------------------

	&vaddtext("\nCopying fstab to hard-disk root...");

	if (&sys("cat $FSTABX > /a/etc/fstab")) {
		&hang("\nFailed to copy fstab onto hard-disk\n$!");
	}
	if ($TESTMODE == 0 && chmod (0644,"/a/etc/fstab") == 0) {
		&hang("\nFailed to chmod /a/etc/fstab ($!)");
	}

# setup /etc/boot.default -----------------------------------------------------

	if (open (OUT, ">/a/etc/boot.default")) {
		print OUT <<ETX;
-echooff
# change to -v for more verbose output
# remove this line for traditional output
# the -q option inhibits noisy autoconfiguration details
-autodebug -q 
# Only 1G of memory is supported unless KERNEL_VIRTUAL_MB is 
# adjusted in the kernel config file.
-extendend 1G
ETX
		if ($ENV{'TERM'} eq "vt100") {
			print OUT <<ETX;
-echo booting from serial console $CONSOLE
-pause 2
-console $CONSOLE
ETX
		}
# The /boot program can find sd0 and wd0, but has to be told
# about the other root disk types
		if ( $dirs{"/"} !~ m#/dev/[sw]d0a# ) {
			$dirs{"/"} =~ m#/dev/([a-zA-Z_]+)(\d+)([a-z]{1})#;
			printf OUT "-echo Setting root device to %s%d%s\n",$1,$2,$3;
			printf OUT "-rootdev %s(%d,%d)\n",$1,$2,ord($3) - ord('a');
		}
		if ($bus{"pcic"} ne "") {
			print OUT <<ETX;
# some notebooks need wait states inserted.
-echo setting up pcmcia services
-pccard_wait on
# Uncomment the following two lines to enable apm
#-echo setting up for apm
#-apm
ETX
		}
		if ($ENV{'TERM'} eq "vt100") {
			print OUT <<ETX;
-pause 5
ETX
		}
		close(OUT);
	}

	&vaddtext("\nWriting out network configuration.");
	&netstart_write;

# copy /boot && /bsd  ---------------------------------------------------------

	&vaddtext("\nCopying /boot and /bsd to root filesystem.");
	if ($BSDLOC ne "floppy") {
		if ($idata{"LOC"} eq "remote") {
                	$tmp = &sys("rsh -l $netutil{\"RUSER\"} $netutil{\"RADDR\"} cat $netutil{\"DIRECTORY\"}/boot >/a/boot 2>/dev/null");
		} elsif ($idata{"LOC"} eq "http") {
                	$tmp = &sys("fetch -o /a/boot -q $idata{\"HTTP\"}/boot 2>/dev/null");
		} else {

			$tmp = &sys("cat < /cdrom/boot > /a/boot");
		}
		if ($tmp != 0 || &sys("chmod 555 /a/boot") != 0) {
			$TEXT=
"$INSTALL was unable to copy /boot from the local cdrom drive.
The installation will continue, but you will need to install /boot
manually when the system is rebooted.  To do this you should reboot
from floppy after the installation is complete and press &lt;CTRL-C&gt;
at the first question.  Once you see the '#' prompt you should enter
the following commands:

    # mount /dev/wd0a /a       (or whatever device your root is on)
    # mount /dev/fd0a /mnt
    # cat < /mnt/boot > /a/boot
    # chmod 555 /a/boot
    # umount /a
    # umount /mnt
    # reboot                   (after removing the floppy)
";
			&vquery("Press <ENTER> to continue:");
		}
		if ($idata{"LOC"} eq "remote") {
                	$tmp = &sys("rsh -l $netutil{\"RUSER\"} $netutil{\"RADDR\"} cat $netutil{\"DIRECTORY\"}/bsd >/a/bsd 2>/dev/null");
		} elsif ($idata{"LOC"} eq "http") {
                	$tmp = &sys("fetch -o /a/bsd -q $idata{\"HTTP\"}/bsd 2>/dev/null");
		} else {

			$tmp = &sys("cat < /cdrom/bsd > /a/bsd");
		}
		if ($tmp != 0 || &sys("chmod 555 /a/bsd") != 0) {
			$TEXT=
"$INSTALL was unable to copy /bsd from the local cdrom drive.
The installation will continue, but you will need to install /boot
manually when the system is rebooted.  To do this you should reboot
from floppy after the installation is complete and press &lt;CTRL-C&gt;
at the first question.  Once you see the '#' prompt you should enter
the following commands:

    # mount /dev/wd0a /a       (or whatever device your root is on)
    # mount /dev/fd0a /mnt
    # gzcat < /mnt/bsd.gz > /a/bsd
    # chmod 555 /a/bsd
    # umount /a
    # umount /mnt
    # reboot                   (after removing the floppy)
";
			&vquery("Press <ENTER> to continue:");
		}
		return 0;
	}
	$TEXT =
"The /boot and /bsd programs must be copied from a floppy.  Please make 
sure the appropriate floppy is in the floppy drive.";
	
	while (1) {
		if (&sys("mount -r /dev/fd0a /mnt") != 0) {
			&vquery("Press <ENTER> to continue:");
			next;
		}
		if (&sys("cat < /mnt/boot > /a/boot && chmod 555 /a/boot") != 0) {
			&sys("umount /mnt");
			&vquery("Press <ENTER> to continue:");
			next;
		}
		if (&sys("gzcat /mnt/bsd.gz > /a/bsd && chmod 555 /a/bsd") != 0) {
			&sys("umount /mnt");
			&vquery("Press <ENTER> to continue:");
			next;
		}
		last;
	}
	if (&sys("umount /mnt") != 0) {
		$TEXT=
"/boot and /bsd appear to have been copied successfully, however,
an error occurred trying to umount the floppy.  This will more
that likely cause difficulties if you need to install additional
software from floppy or when you reboot the system.  It is
possible to try and continue, however.";
		&vquery("Press <ENTER> to continue:");
	} else {
		$TEXT=
"/boot and /bsd were copied successfully.  You may now remove the
BSD/OS INSTALL floppy from the floppy drive.";
		sleep(5);
	}
}

sub crack_fstab {
	local ($chkunk) = @_;
	while (<IN>) {
		chop;
		if (/^#/) {
		    ($cmt, $fs, $mpt, $type, $flags) = split;
		    if ($type eq "ufs" && $mpt eq "UNKNOWN") {
			for (;$chkunk;) {
			    $TEXT=
"The filesystem $fs has an unknown mount point.  If you would
like this to be a mounted filesystem you will need to enter the mount
point now.  If you do not want this to be a mounted filesystem then
then leave its mount point as UNKNOWN";
			    $mpt = &vquery("Mount point for $fs:", $mpt);
			    if ($mpt eq "UNKNOWN") {
				last;
			    }
			    if ($mpt =~ /^\// || $mpt eq "swap") {
				s/# //;
				if (length($mpt) < 8) {
				    s/UNKNOWN/$mpt/;
				} else {
				    s/UNKNOWN\t/$mpt/;
				}
				last;
			    }
			    $mpt = "UNKNOWN";
			    $COMMENTS=
"Mount point names must start with a / or be UNKNOWN or swap";
			}
		    }
		} else {
		    ($fs, $mpt, $type, $flags) = split;
		}
		if (! /^#/) {
			if ($fs =~ /.*0a/) {
				if ($mpt eq "/a") {
					$mpt = "/";
					s/\t\/a/\t\//;
					# make sure it's fsck pass 1
					s/\d\s*$/1/;
				}
			} elsif ($mpt =~ m#/a/(.*)#) {
				if (length($mpt) == 8 || length($mpt) == 9) {
					s/\t\/a/\t\t/;
				} else {
					s/\t\/a/\t/;
				}
			}
			if ($fs =~ m#/dev/(wd\d)#) {
			    $wd_devs{$1} = 1;
			}
			if ($mpt eq "swap") {
			    if ($swapdev eq "") {
				$swapdev = $fs;
			    }
			    $devices{$fs}++;
			} else {
			    $filesys{$mpt}++;
			    if ($type ne "mfs") {
				$devices{$fs}++;
			    }
			    $dirs{$mpt} = $fs;
			    if ($flags =~ /na/) {
				$types{$mpt} = $type . "na";
			    } else {
				$types{$mpt} = $type;
			    }
			}
		}
		$FSTAB[$fstabi] = $_."\n";
		$fstabi++;
	}
}

sub sort_fstab {
	for ($i = 0; $i < $fstabi; $i++) {
		if ($i =~ /^#/) {
			next;
		}
		($dev, $mpt, $t) = split(/[ \t]+/, $FSTAB[$i]);
		if ($mpt =~ /^[^\/]/) {
			next;
		}
		for ($j = $fstabi - 1; $j > $i; $j--) {
			if ($i =~ /^#/) {
				next;
			}
			($dev, $mpt2, $t) = split(/[ \t]+/, $FSTAB[$j]);
			if ($mpt =~ /^$mpt2\//) {
				$save = $FSTAB[$i];
				for ($k = $i; $k < $j; $k++) {
					$FSTAB[$k] = $FSTAB[$k+1];
				}
				$FSTAB[$j] = $save;
				$i--;
				last;
			}
		}
	}
}

sub check_fstab_devs {
	local($fs);

	foreach $fs (keys (%devices)) {
		if (! -b "/a${fs}") {
			# Only make /dev fstab entries
			if (${fs} =~ m#^/dev/([a-zA-Z_]+\d+)[a-h]$#) {
				&make_disk_dev("/a/dev",$1);
			}
		}
	}
}

sub make_disk_dev {
	local($dir,$disk) = @_;
	local($drive,$unit,$part,$type,$mode,$devname,$grp);

	%majdevnum = (
		  "fd",  9,
	          "wd",  3,
	          "sd", 18,
	          "sr", 34,
	         "mcd", 23,
	          "cr", 46,
	        "amir", 58,
	         "vnd", 42,
	          "rd", 44,
	          "sp", 37,
	);

	if ( $disk !~ /^([a-zA-Z]+)(\d+)$/ ) {
		return(1);
	}
	$drive = $1;
	$unit = $2;
	unless (defined $majdevnum{$drive}) {
		return(2);
	}
	# Put things in the operator group #5
	$grp = 5;
	$mode = 0640;
	$mode = 0666 if $drive eq "fd";
	#
	# Make special devices for splice and vnd
	#
	if ( $drive eq "vnd" ) {
		unlink("${dir}/vnd");
		&sys("mknod ${dir}/vnd c $majdevnum{$drive} 1023 0");
		chown(0,0, "$dir/vnd");
		chmod(0600, "$dir/vnd");
	}
	if ( $drive eq "sp" ) {
		unlink("${dir}/splice");
		&sys("mknod ${dir}/splice c $majdevnum{$drive} 1023 0");
		chown(0,0, "$dir/splice");
		chmod(0600, "$dir/splice");
	}
	# Make the block and raw devices
	foreach $part (a..h)  {
		foreach $type ('b','c') {
			$n = ord($part) - ord('a');
			$devname = $drive;
			$devname = "r" . $devname if $type eq 'c';
			&sys("mknod ${dir}/${devname}${unit}${part} $type $majdevnum{$drive} ${unit} $n");
			chown(0,$grp,"${dir}/${devname}${unit}${part}");
			chmod($mode,"${dir}/${devname}${unit}${part}");
		}
	}
	if ( $drive eq "fd" ) {
		# make special floppy devices
		unlink("${dir}/floppy${unit}");
		link("${dir}/${drive}${unit}c", "$dir/floppy${unit}");
		unlink("${dir}/rfloppy${unit}");
		link("${dir}/r${drive}${unit}c", "$dir/rfloppy${unit}");
		unlink("${dir}/fd${unit}");
		link("${dir}/$drive${unit}c", "$dir/fd${unit}");
		unlink("${dir}/rfd${unit}");
		link("${dir}/r${drive}${unit}c", "$dir/rfd${unit}");
		&sys("mknod ${dir}/rfd${unit}_1440_3.5 c $majdevnum{$drive} ${unit} 34");
		&sys("mknod ${dir}/rfd${unit}_720_3.5 c $majdevnum{$drive} ${unit} 58");
		&sys("mknod ${dir}/rfd${unit}_1200_5.25 c $majdevnum{$drive} ${unit} 18");
		&sys("mknod ${dir}/rfd${unit}_720_5.25 c $majdevnum{$drive} ${unit} 42");
		&sys("mknod ${dir}/rfd${unit}_360_5.25 c $majdevnum{$drive} ${unit} 50");

		chown (0,$grp, "${dir}/rfd${unit}_1440_3.5",
			    "${dir}/rfd${unit}_720_3.5",
			    "${dir}/rfd${unit}_1200_5.25",
			    "${dir}/rfd${unit}_720_5.25",
			    "${dir}/rfd${unit}_360_5.25" );

		chmod ($mode, "${dir}/rfd${unit}_1440_3.5",
                            "${dir}/rfd${unit}_720_3.5",
                            "${dir}/rfd${unit}_1200_5.25",
                            "${dir}/rfd${unit}_720_5.25",
                            "${dir}/rfd${unit}_360_5.25" );
	}

	return(0);
}
# Setup Network ###############################################################

sub setup_network {
	$TITLE="BSD/OS Network Configuration";
	if (&netstart_init() == 0) {
		$TEXT=
"A network connection is required to continue this installation.
There appears to be no recognized network interface on this machine so the
installation of BSD/OS cannot continue.

To install BSD/OS you must either have a recognized local CD-ROM or
a recognized network interface which is connected to a network on
which there is an accessible CD-ROM.";
		&hang("");
	}

	for (;;) {
		$TITLE="BSD/OS Network Configuration";
		if ($idata{"LOC"} eq "http") {
			if (&netstart_setup("http") == 0) {
				next;
			}
		} else {
			if (&netstart_setup("install") == 0) {
				next;
			}
		}
		if ($idata{"LOC"} eq "nfs") {
			last;
		}
		&check_cd();
		if ($cd_info[0] eq $SYSTEM &&
			$cd_info[1] eq $VERSION &&
			$cd_info[2] eq $TYPE) {
				last;
		}
		$TEXT=
"Remote access worked, but the CD-ROM appears to be missing.
The disk found is: $cd_info[0] $cd_info[1] $cd_info[2]

Please make sure the $SYSTEM $VERSION $TYPE CD-ROM is mounted
on $netutil{\"DIRECTORY\"} on the remote machine.";
		$HELP=
"If you cannot resolve the problem, an installation from a local
CD-ROM may be a better option for you.";
		&vquery("Press <ENTER> to continue");
		&unconfigure_interface($netutil{"IFACE"}, $netutil{"LADDR"},
				       $netutil{"MASK"}, $media,
				       $netutil{"ROUTER"});
	}

	$idata{"LADDR"} = $netutil{"LADDR"};
	$idata{"NETMASK"} = $netutil{"MASK"};
	$idata{"RADDR"} = $netutil{"RADDR"};
	$idata{"ROUTER"} = $netutil{"ROUTER"};
	$idata{"RUSER"} = $netutil{"RUSER"};
	$idata{"DIRECTORY"} = $netutil{"DIRECTORY"};
	$idata{"HTTP"} = $netutil{"HTTP"};

	system("echo REMOTE=$netutil{\"RADDR\"} > /tmp/INSTALL.ENV");
	system("echo RUSER=$netutil{\"RUSER\"} >> /tmp/INSTALL.ENV");
	system("echo CDPATH=$netutil{\"DIRECTORY\"} >> /tmp/INSTALL.ENV");
	if ($idata{"LOC"} eq "http") {
		system("echo HTTP=$netutil{\"HTTP\"} >> /tmp/INSTALL.ENV");
	}
	system("chmod 777 /var/tmp/INSTALL.ENV");
}
sub sys {
	local ($cmd) = @_;
	if ($TESTMODE == 0) {
		return(system($cmd));
	} else {
		return(0);
	}
}
sub umount_dirs {
	# make sure any previously mounted filesystems are unmounted
	@_ = `mount`;
	for $_ (@_) {
		chop;
		!/^\/dev/ && next;
		($dev, $junk, $fs, $junk) = split;
		if ($fs =~ m#/a#) {
			push(@tmp, $fs);
		}
		@tmp = reverse(sort(@tmp));
	}
	if ($#tmp >= 0) {
		print <<ETX;
Cleaning up from previous installation attempt.  It appears that 
some of the filesystems to be installed were already mounted.  
Attempting to unmount them...

ETX
	}
	for $i (@tmp) {
		print "Unmounting $i\n";
		system("umount $i") &&
			die "could not unmount $i -- aborting\n";
	}
	if ($#tmp >= 0) {
		print <<ETX;
Filesystems unmounted successfully -- proceeding with installation.

ETX
	}
	1;
}
# Parse a generic record
sub parserec {
	local ($entry, $field, $val, @lines) = @_;
	@lines = split(/\n/,$entry);
	%fields = ();
	while (defined($_ = pop(@lines))) {
		next if /^$/;
		next if /^#/;
		($field, $val) = split(/:[ 	]+/, $_, 2);
		while ($fields{$field}) { $field .= "+"; }
		$fields{$field} = $val;
	}
	1;
}

# Write out a generic record
sub writerec {
	local($FILE, %fields, @tmp, $field) = @_;
	@tmp=();
	for $_ (keys(%fields)) {
		push (@tmp, $_) if defined($fields{$_});
	}
	@tmp = sort @tmp;
	for $_ (@tmp) {
		/\+$/ && next;
		$field = $_;
		while (defined($fields{$_})) {
			print $FILE "$field:\t$fields{$_}\n";
			$_ .= "+";
		}
	}
	print $FILE "\n";
	1;
}

# Put a generic record into a string
sub sprintrec {
	local(%fields, @tmp, $field, $str) = @_;
	$str = "";
	@tmp = ();
	for $_ (keys(%fields)) {
		push (@tmp, $_) if defined($fields{$_});
	}
	@tmp = sort @tmp;
	for $_ (@tmp) {
		/\+$/ && next;
		$field = $_;
		while (defined($fields{$_})) {
			$str .= "$field:\t$fields{$_}\n";
			$_ .= "+";
		}
	}
	$str .= "\n";
	return $str;
}

# Read a file as a single generic record
sub readsimple {
	local ($file, $fatal, $lines) = @_;
	if (!open (IN, "$file")) {
		die "can't open $file\n" if $fatal;
		print STDERR "Warning: can't open $file\n";
		return 0;
	}
	undef $/;		# Snarf entire file
	$lines=<IN>;
	close(IN);
	$/ = "\n";		# Back to normal
	&parserec($lines);
	1;
}

# Write a file from a single %fields array
sub writesimple {
	local ($file, %fields, $fatal) = @_;
	if (!open (OUT, ">$file")) {
		die "can't open $file\n" if $fatal;
		print STDERR "Warning: can't open $file\n";
		return 0;
	}
	&writerec("OUT", %fields);
	close(OUT);
	1;
}
