/*
 * The new sysinstall program.
 *
 * This is probably the last program in the `sysinstall' line - the next
 * generation being essentially a complete rewrite.
 *
 * $FreeBSD: src/release/sysinstall/install.c,v 1.223.2.29 1999/12/19 07:03:02 jkh Exp $
 *
 * Copyright (c) 1995
 *	Jordan Hubbard.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer,
 *    verbatim and that no modifications are made prior to this
 *    point in the file.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include "sysinstall.h"
#include <ctype.h>
#include <sys/disklabel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/wait.h>
#include <sys/param.h>
#define MSDOSFS
#include <sys/mount.h>
#include <ufs/ufs/ufsmount.h>
#include <msdosfs/msdosfsmount.h>
#undef MSDOSFS
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <termios.h>

static void	create_termcap(void);
static void	fixit_common(void);

#define TERMCAP_FILE	"/usr/share/misc/termcap"

static void	installConfigure(void);

Boolean
checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev)
{
    Device **devs;
    Boolean status;
    Disk *disk;
    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
    int i;

    /* Don't allow whinging if noWarn is set */
    if (variable_get(VAR_NO_WARN))
	whinge = FALSE;

    status = TRUE;
    *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;

    /* We don't need to worry about root/usr/swap if we're already multiuser */
    if (!RunningAsInit)
	return status;

    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
    /* First verify that we have a root device */
    for (i = 0; devs[i]; i++) {
	if (!devs[i]->enabled)
	    continue;
	disk = (Disk *)devs[i]->private;
	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
	if (!disk->chunks) {
	    KANJI_MESSAGE("%s のためのチャンクリストが見つかりません!");
	    msgFatal(KM("No chunk list found for %s!"), disk->name);
	}
	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
	    if (c1->type == freebsd) {
		for (c2 = c1->part; c2; c2 = c2->next) {
		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
			if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
			    if (rootdev) {
				if (whinge) {
				    KANJI_MESSAGE(
				    "警告: 複数のルートデバイスを持っていませんか?!\n"
				    "最初に見つかったものを使用します.");
				    msgConfirm(KM("WARNING:  You have more than one root device set?!\n"
					       "Using the first one found."));
				}
				continue;
			    }
			    else {
				rootdev = c2;
				if (isDebug())
				    msgDebug("Found rootdev at %s!\n", rootdev->name);
			    }
			}
			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
			    if (usrdev) {
				if (whinge) {
				    KANJI_MESSAGE(
				    "警告: 複数の /usr ファイルシステムが存在します.\n"
				    "最初に見つかったものを使用します.");
				    msgConfirm(KM("WARNING:  You have more than one /usr filesystem.\n"
					       "Using the first one found."));
				}
				continue;
			    }
			    else {
				usrdev = c2;
				if (isDebug())
				    msgDebug("Found usrdev at %s!\n", usrdev->name);
			    }
			}
			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
			    if (vardev) {
				if (whinge) {
				    KANJI_MESSAGE("警告：複数の /var ファイルシステムが存在します.\n"
				    "最初に見つかったものを使用します.");
				    msgConfirm(KM("WARNING:  You have more than one /var filesystem.\n"
					       "Using the first one found."));
				}
				continue;
			    }
			    else {
				vardev = c2;
				if (isDebug())
				    msgDebug("Found vardev at %s!\n", vardev->name);
			    }
			}
		    }
		}
	    }
	}
    }

    /* Now check for swap devices */
    for (i = 0; devs[i]; i++) {
	if (!devs[i]->enabled)
	    continue;
	disk = (Disk *)devs[i]->private;
	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
	if (!disk->chunks) {
	    KANJI_MESSAGE("%s のチャンクリストが見つかりません!");
	    msgFatal(KM("No chunk list found for %s!"), disk->name);
	}
	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
	    if (c1->type == freebsd) {
		for (c2 = c1->part; c2; c2 = c2->next) {
		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
			swapdev = c2;
			if (isDebug())
			    msgDebug("Found swapdev at %s!\n", swapdev->name);
			break;
		    }
		}
	    }
	}
    }

    /* Copy our values over */
    *rdev = rootdev;
    *sdev = swapdev;
    *udev = usrdev;
    *vdev = vardev;

    if (!rootdev && whinge) {
	/* AT 日本語版のメッセージと異なる */
	KANJI_MESSAGE(
	"ルートデバイスが見つかりません - ラベルエディタで, パーティション\n"
	"を / にラベルして下さい.");
	msgConfirm(KM("No root device found - you must label a partition as /\n"
		   "in the label editor."));
	status = FALSE;
    }
    if (!swapdev && whinge) {
	KANJI_MESSAGE(
	"スワップデバイスが見つかりません. 少なくとも 1 つはスワップパーティ\n"
	"ションを作る必要があります.");
	msgConfirm(KM("No swap devices found - you must create at least one\n"
		   "swap partition."));
	status = FALSE;
    }
#if 0	/* 3.3 で削除された */
    if (!usrdev && whinge && !variable_get(VAR_NO_USR)) {
	KANJI_MESSAGE(
	"警告: /usr ファイルシステムが見つかりません. これは技術的には, ルー\n"
	"トファイルシステムが十分に大きい場合 (あるいはあとで NFS で /usr \n"
	"ファイルシステムをマウントしようとしている場合) には問題ありません\n"
	"が, そうではなく, 現在設定している状態を把握できていない場合には,\n"
	"トラブルが生じることでしょう!");
	msgConfirm(KM("WARNING:  No /usr filesystem found.  This is not technically\n"
		   "an error if your root filesystem is big enough (or you later\n"
		   "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
		   "cause you trouble if you're not exactly sure what you are doing!"));
    }
    if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
	KANJI_MESSAGE(
	"警告: /var ファイルシステムが見つかりません. これは技術的には, ルート\n"
	"ファイルシステムが十分に大きい場合 (あるいはあとで/var をよそへリンク\n"
	"しようとしている場合)には問題ありませんが, そうではなく, 現在設定して\n"
	"いる状態を把握できていない場合には, 大量のメールを受け取ったり, 大き\n"
	"なファイルを編集しようとしたときにルートファイルシステムがあふれるで\n"
	"しょう!");
	msgConfirm(KM("WARNING:  No /var filesystem found.  This is not technically\n"
		   "an error if your root filesystem is big enough (or you later\n"
		   "intend to link /var to someplace else), but it may otherwise\n"
		   "cause your root filesystem to fill up if you receive lots of mail\n"
		   "or edit large temporary files."));
    }
#endif
    return status;
}

static int
installInitial(void)
{
    static Boolean alreadyDone = FALSE;
    int status = DITEM_SUCCESS;

    if (alreadyDone)
	return DITEM_SUCCESS;

    if (!variable_get(DISK_LABELLED)) {
	KANJI_MESSAGE(
	"インストールを続行するには, その前にディスクラベルを割り当てる必\n"
	"要があります.");
	msgConfirm(KM("You need to assign disk labels before you can proceed with\n"
		   "the installation."));
	return DITEM_FAILURE;
    }
    /* If it's labelled, assume it's also partitioned */
    if (!variable_get(DISK_PARTITIONED))
	variable_set2(DISK_PARTITIONED, "yes", 0);

    /* If we refuse to proceed, bail. */
    dialog_clear_norefresh();
    if (!variable_get(VAR_NO_WARN)) {
	KANJI_MESSAGE(
	"これが最後のチャンスです! あなたは「本当に」インストールを継続し\n"
	"たいのですか? もしすでに何らかのデータが存在しているディスクに対し\n"
	"てこれを実行したいのであれば, 「インストール前に正しいバックアップ\n"
	"を作っておくということを我々は強く勧めます!」\n\n"
	"我々はディスクの内容の消失に関しては何ら責任をとることはできません!");
	if (msgYesNo(KM(
	    "Last Chance!  Are you SURE you want continue the installation?\n\n"
	     "If you're running this on a disk with data you wish to save\n"
	     "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
	     "proceeding!\n\n"
	     "We can take no responsibility for lost disk contents!")) != 0)
	return DITEM_FAILURE;
    }

    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
	KANJI_MESSAGE("ファイルシステムを正常に作れませんでした. 処理を中断します.");
	msgConfirm(KM("Couldn't make filesystems properly.  Aborting."));
	return DITEM_FAILURE;
    }

    if (!copySelf()) {
	KANJI_MESSAGE(
	"インストール初期化: ブートフロッピーの内容をルートファイルシステムに\n"
	"コピーできませんでした. 処理を中断します.");
	msgConfirm(KM("installInitial: Couldn't clone the boot floppy onto the\n"
		   "root file system.  Aborting!"));
	return DITEM_FAILURE;
    }

    if (chroot("/mnt") == -1) {
	KANJI_MESSAGE("インストール初期化: %s に chroot できませんでした.\n"
	"異常事態です!");
	msgConfirm(KM("installInitial: Unable to chroot to %s - this is bad!"),
		   "/mnt");
	return DITEM_FAILURE;
    }

    chdir("/");
    variable_set2(RUNNING_ON_ROOT, "yes", 0);

    /* Configure various files in /etc */
    if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
	status = DITEM_FAILURE;
    if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
	status = DITEM_FAILURE;

    /* stick a helpful shell over on the 4th VTY */
    systemCreateHoloshell();

    alreadyDone = TRUE;
    return status;
}

int
installFixitHoloShell(dialogMenuItem *self)
{
    systemCreateHoloshell();
    return DITEM_SUCCESS;
}

int
installFixitCDROM(dialogMenuItem *self)
{
    struct stat sb;

    if (!RunningAsInit)
	return DITEM_SUCCESS;

    variable_set2(SYSTEM_STATE, "fixit", 0);
    (void)unlink("/mnt2");
    (void)rmdir("/mnt2");

    while (1) {
	KANJI_MESSAGE("FreeBSD のファイルシステム CDROM を入れてリターンを押して下さい.");
	msgConfirm(KM("Please insert a FreeBSD live filesystem CDROM and press return"));
	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
	    mediaClose();
	    KANJI_MESSAGE("CDROM をマウントできません - もう一度試しますか?");
	    if (msgYesNo(KM("Unable to mount the CDROM - do you want to try again?")) != 0)
		return DITEM_FAILURE;
	}
	else
	    break;
    }

    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
     * a little kludge dance here..
     */
    if (symlink("/dist", "/mnt2")) {
	KANJI_MESSAGE("/mnt2 から CDROM のマウントポイントへのシンボリックリンクを作成できません.\n"
		      "この予期せぬトラブルを freebsd-bugs@FreeBSD.org に (英語で) 報告して下さい.");
	msgConfirm(KM("Unable to symlink /mnt2 to the CDROM mount point.  Please report this\n"
		   "unexpected failure to freebsd-bugs@FreeBSD.org."));
	return DITEM_FAILURE;
    }

    /*
     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
     * not very good for us if we point it to the CDROM now.  Rather make it
     * a directory in the root MFS then.  Experienced admins will still be
     * able to mount their disk's /tmp over this if they need.
     */
    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
	(void)unlink("/tmp");
    Mkdir("/tmp");

    /*
     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
     */
    if (!file_readable("/var/run/ld.so.hints")) {
	Mkdir("/var/run");
	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
	    KANJI_MESSAGE("警告: ldconfig が ld.so ヒントファイルを作成できません.\n"
			  "CDROM のダイナミックリンクされたファイルは動作しないでしょう.");
	    msgConfirm(KM("Warning: ldconfig could not create the ld.so hints file.\n"
		       "Dynamic executables from the CDROM likely won't work."));
	}
    }

    /* Yet more iggly hardcoded pathnames. */
    Mkdir("/usr/libexec");
    if (!file_readable("/usr/libexec/ld.so")) {
	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so"))
	    msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
    }
    if (!file_readable("/usr/libexec/ld-elf.so.1")) {
	if (symlink("/mnt2/usr/libexec/ld-elf.so.1", "/usr/libexec/ld-elf.so.1")) {
	    KANJI_MESSAGE("警告: ld-elf.so.1 のためのシンボリックリンクを作成できません.\n"
			  "CDROM のダイナミックリンクされたファイルは動作しないでしょう.");
	    msgConfirm(KM("Warning: could not create the symlink for ld-elf.so.1\n"
		       "Dynamic executables from the CDROM likely won't work."));
	}
    }
    /* optional nicety */
    if (!file_readable("/usr/bin/vi"))
	symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
    fixit_common();
    mediaClose();
    KANJI_MESSAGE("ここで, fixit CDROMを抜いて下さい.");
    msgConfirm(KM("Please remove the FreeBSD fixit CDROM now."));
    return DITEM_SUCCESS;
}

int
installFixitFloppy(dialogMenuItem *self)
{
    struct ufs_args args;
    extern char *distWanted;

    if (!RunningAsInit)
	return DITEM_SUCCESS;

    /* Try to open the floppy drive */
    if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
	KANJI_MESSAGE("メディアデバイスをフロッピーに設定できません.");
	msgConfirm(KM("Unable to set media device to floppy."));
	mediaClose();
	return DITEM_FAILURE;
    }

    memset(&args, 0, sizeof(args));
    args.fspec = mediaDevice->devname;
    mediaDevice->private = "/mnt2";
    distWanted = NULL;
    Mkdir("/mnt2");

    variable_set2(SYSTEM_STATE, "fixit", 0);

    while (1) {
	if (!mediaDevice->init(mediaDevice)) {
	    KANJI_MESSAGE(
	    "fixit フロッピーのマウントに失敗しました. フロッピーの不良かファイル\n"
	    "システムがクリーンな状態ではないのでしょう. もう一度試みますか?");
	    if (msgYesNo(KM("The attempt to mount the fixit floppy failed, bad floppy\n"
			 "or unclean filesystem.  Do you want to try again?")))
		return DITEM_FAILURE;
	}
	else
		break;
    }

    if (!directory_exists("/tmp"))
	(void)symlink("/mnt2/tmp", "/tmp");

    fixit_common();

    mediaClose();
    KANJI_MESSAGE("fixit フロッピーを抜いて下さい.");
    msgConfirm(KM("Please remove the fixit floppy now."));
    return DITEM_SUCCESS;
}

/*
 * The common code for both fixit variants.
 */
static void
fixit_common(void)
{
    pid_t child;
    int waitstatus;

    if (!directory_exists("/var/tmp/vi.recover")) {
	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
	    KANJI_MESSAGE(
	    "警告: /var/tmp/vi.recover ディレクトリを作ることができません. vi \n"
	    "は文句をいうでしょうが, とりあえず使えるはずです.");
	    msgConfirm(KM("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
		       "vi will kvetch and moan about it as a result but should still\n"
		       "be essentially usable."));
	}
    }
    if (!directory_exists("/bin"))
	(void)Mkdir("/bin");
    (void)symlink("/stand/sh", "/bin/sh");
    /* Link the /etc/ files */
    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS) {
	KANJI_MESSAGE("/etc ディレクトリを作れません!  このフロッピーは変です…");
	msgConfirm(KM("Unable to create an /etc directory!  Things are weird on this floppy.."));
    }
    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST)) {
	KANJI_MESSAGE("/etc/ のファイルにシンボリックリンクを張れません!\n"
	    "何でこうなるのでしょう…");
	msgConfirm(KM("Couldn't symlink the /etc/ files!  I'm not sure I like this.."));
    }
    if (!file_readable(TERMCAP_FILE))
	create_termcap();
    if (!(child = fork())) {
	int i, fd;
	struct termios foo;
	extern int login_tty(int);

	ioctl(0, TIOCNOTTY, NULL);
	for (i = getdtablesize(); i >= 0; --i)
	    close(i);
	fd = open("/dev/ttyv3", O_RDWR);
	ioctl(0, TIOCSCTTY, &fd);
	dup2(0, 1);
	dup2(0, 2);
	DebugFD = 2;
	if (login_tty(fd) == -1)
	    msgDebug("fixit: I can't set the controlling terminal.\n");

	signal(SIGTTOU, SIG_IGN);
	if (tcgetattr(0, &foo) != -1) {
	    foo.c_cc[VERASE] = '\010';
	    if (tcsetattr(0, TCSANOW, &foo) == -1)
		msgDebug("fixit shell: Unable to set erase character.\n");
	}
	else
	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
	/* use the .profile from the fixit medium */
	setenv("HOME", "/mnt2", 1);
	chdir("/mnt2");
	execlp("sh", "-sh", 0);
	msgDebug("fixit shell: Failed to execute shell!\n");
	_exit(1);;
    }
    else {
	dialog_clear_norefresh();
#ifdef PC98
	KANJI_MESSAGE("fixit shell の終了を待っています. GRPH-F4 を打って VTY4\n"
		      "に切替えて下さい. 終了したら fixit shell から抜けるた\n"
		      "めに ``exit'' と打ち込めばここに戻ります.");
	msgNotify(KM("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
		  "typing GRPH-F4.  When you are done, type ``exit'' to exit\n"
		  "the fixit shell and be returned here."));
#else
	KANJI_MESSAGE("fixit shell の終了を待っています. ALT-F4 を打って VTY4\n"
		      "に切替えて下さい. 終了したら fixit shell から抜けるた\n"
		      "めに ``exit'' と打ち込めばここに戻ります.");
	msgNotify(KM("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
		  "typing ALT-F4.  When you are done, type ``exit'' to exit\n"
		  "the fixit shell and be returned here."));
#endif
	(void)waitpid(child, &waitstatus, 0);
    }
    unmount("/mnt2", MNT_FORCE);
    dialog_clear();
}


int
installExpress(dialogMenuItem *self)
{
    int i;

    dialog_clear_norefresh();
    variable_set2(SYSTEM_STATE, "express", 0);
#ifndef __alpha__
    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
	return i;
#endif
    
    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
	return i;

    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
	i |= DITEM_LEAVE_MENU;
	/* Give user the option of one last configuration spree */
	installConfigure();
    }
    return i;
}

/* Novice mode installation */
int
installNovice(dialogMenuItem *self)
{
    int i, tries = 0;
    Device **devs;

    variable_set2(SYSTEM_STATE, "novice", 0);
    dialog_clear_norefresh();
#ifndef __alpha__
    KANJI_MESSAGE(
    "次のメニューでは, DOS スタイル (\"fdisk\") のパーティショニングをディ\n"
    "スクに対して行ないます. もし単純に全てのディスクスペースを FreeBSD\n"
    "に対して割り当てたいのであれば (選択したディスクに存在している全て\n"
    "の内容を上書きしてしまいます), A (ディスク全体を使用) コマンドを使\n"
    "用して, デフォルトパーティショニング法を選択し, Q (終了) コマンド\n"
    "で終了して下さい. もし空いている領域のみを FreeBSD に対して割り当\n"
    "てたいのであれば, (\"unused\") とマークされたパーティションに移動して,\n"
    "C (パーティション作成) コマンドを使用して下さい.");
    msgConfirm(KM("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
	       "then use the (A)ll command to select the default partitioning scheme followed\n"
	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
	       "partition marked \"unused\" and use the (C)reate command."));

nodisks:
    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
	return DITEM_FAILURE;

    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
	KANJI_MESSAGE(
	"操作を行うためにはいくつかのディスクを選択しなければなりません! ディスク選択\n"
	"メニューでディスクを選択するときは RETURN ではなく SPACE(空白) を押したか確か\n"
	"めてください.");
	msgConfirm(KM("You need to select some disks to operate on!  Be sure to use SPACE\n"
		   "instead of RETURN in the disk selection menu when selecting a disk."));
	++tries;
	goto nodisks;
    }
#endif

#ifdef __alpha__
    msgConfirm("Now you need to create BSD partitions on the disk which you are\n"
	       "installing to.  If you have a reasonable amount of disk space (200MB or more)\n"
	       "and don't have any special requirements, simply use the (A)uto command to\n"
	       "allocate space automatically.  If you have more specific needs or just don't\n"
	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
	       "manual layout.");
#else
    KANJI_MESSAGE(
    "これから、今作った fdisk パーティション内に BSD パーティションを作\n"
    "らなければなりません. もしあなたが十分なディスクスペース (200MB ま\n"
    "たはそれ以上) を持っており, かつ特別な要求がないのであれば, 単に A\n"
    "(全てをデフォルトに設定) コマンドを使用してディスクを自動的に割り\n"
    "当てれば大丈夫です. もし A コマンドでは設定し切れない特殊な要求が\n"
    "ある場合には, 手動でレイアウトを行なって下さい.");
    msgConfirm(KM("Now you need to create BSD partitions inside of the fdisk partition(s)\n"
	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
	       "and don't have any special requirements, simply use the (A)uto command to\n"
	       "allocate space automatically.  If you have more specific needs or just don't\n"
	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
	       "manual layout."));
#endif

    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
	return DITEM_FAILURE;

    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
	dialog_clear();
#ifdef PC98
	KANJI_MESSAGE(
	"いくつかエラーがありましたが, インストールは完了しました. VTY1 上\n"
	"のデバッグメッセージを vf・3 の機能を使用して読んで下さい. また,\n"
	"次のプロンプトで \"No\" を選択すれば, インストールメニューに戻って,\n"
	"再度失敗した操作をやり直すことができます.");
	msgConfirm(KM("Installation completed with some errors.  You may wish to\n"
		   "scroll through the debugging messages on VTY1 with the\n"
		   "vf.3 feature.  You can also chose \"No\" at the next\n"
		   "prompt and go back into the installation menus to try and retry\n"
		   "whichever operations have failed."));
#else
	"いくつかエラーがありましたが, インストールは完了しました. VTY1 上\n"
	"のデバッグメッセージを scroll-lock 機能を使用してデバッグメッセー\n"
	"ジを読んで下さい. また, 次のプロンプトで \"No\" を選択すれば, イン\n"
	"ストールメニューに戻って, 再度失敗した操作をやり直すことができます.");
	msgConfirm(KM("Installation completed with some errors.  You may wish to\n"
		   "scroll through the debugging messages on VTY1 with the\n"
		   "scroll-lock feature.  You can also chose \"No\" at the next\n"
		   "prompt and go back into the installation menus to try and retry\n"
		   "whichever operations have failed."));
#endif	/* PC98 */
	return i;

    }
    else {
	dialog_clear();
	KANJI_MESSAGE(
	"おめでとうございます! あなたのシステムには FreeBSD がインストール\n"
	"されました. これから最後の設定に関する質問に移行します. 特別な設定を\n"
	"したくない設定オプションに関しては, 単に No を選択して下さい.\n\n"
	"もしシステムが立ち上がってからこのユーティリティに再度入りたい時は,\n"
	"/stand/sysinstall と打って下さい.");
	msgConfirm(KM("Congratulations!  You now have FreeBSD installed on your system.\n\n"
		   "We will now move on to the final configuration questions.\n"
		   "For any option you do not wish to configure, simply select\n"
		   "No.\n\n"
		   "If you wish to re-enter this utility after the system is up, you\n"
		   "may do so by typing: /stand/sysinstall."));
    }
    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
	KANJI_MESSAGE("イーサネットまたは SLIP/PPP ネットワークデバイスの設定を行いますか?");
	if (!msgYesNo(KM("Would you like to configure any Ethernet or SLIP/PPP network devices?"))) {
	    Device *tmp = tcpDeviceSelect();

	    KANJI_MESSAGE("インターフェース %s をいますぐ使用可能な状態にしますか?");
	    if (tmp && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo(KM("Would you like to bring the %s interface up right now?"), tmp->name))
		if (!tmp->init(tmp)) {
		    KANJI_MESSAGE("デバイス %s の初期化に失敗しました.");
		    msgConfirm(KM("Initialization of %s device failed."), tmp->name);
		}
	}
	dialog_clear_norefresh();
    }

    KANJI_MESSAGE(
    "このマシンを IP ゲートウェイとして設定しますか (つまりインターフェー\n"
    "ス間でパケットを転送しますか)?");
    if (!msgYesNo(KM("Will this machine be an IP gateway (e.g. will it forward packets\n"
		  "between interfaces)?")))
	variable_set2("gateway_enable", "YES", 1);

    KANJI_MESSAGE("このマシンに関する匿名 FTP 接続を許可しますか?");
    if (!msgYesNo(KM("Do you want to allow anonymous FTP connections to this machine?")))
	configAnonFTP(self);

    KANJI_MESSAGE("このマシンを NFS サーバとして設定しますか?");
    if (!msgYesNo(KM("Do you want to configure this machine as an NFS server?")))
	configNFSServer(self);

    dialog_clear_norefresh();
    KANJI_MESSAGE("このマシンを NFS クライアントとして設定しますか?");
    if (!msgYesNo(KM("Do you want to configure this machine as an NFS client?")))
	variable_set2("nfs_client_enable", "YES", 1);

    KANJI_MESSAGE("コンソールの機能をカスタマイズしますか?");
    if (!msgYesNo(KM("Would you like to customize your system console settings?")))
	dmenuOpenSimple(&MenuSyscons, FALSE);

    dialog_clear_norefresh();
    KANJI_MESSAGE("タイムゾーンの設定を今行ないますか?");
    if (!msgYesNo(KM("Would you like to set this machine's time zone now?")))
	systemExecute("tzsetup");

#ifdef __i386__
    dialog_clear_norefresh();
    KANJI_MESSAGE("Linux バイナリーコンパチビリティーを可能にしますか?");
    if (!msgYesNo(KM("Would you like to enable Linux binary compatibility?")))
	(void)configLinux(self);
#endif

    dialog_clear_norefresh();
    KANJI_MESSAGE("このマシンにはマウスが接続されていますか?");
    if (!msgYesNo(KM("Does this system have a mouse attached to it?")))
	dmenuOpenSimple(&MenuMouse, FALSE);

    /* Now would be a good time to checkpoint the configuration data */
    configRC_conf();
    sync();

    if (directory_exists("/usr/X11R6")) {
	dialog_clear_norefresh();
	KANJI_MESSAGE("X サーバの設定を今行ないますか?");
	if (!msgYesNo(KM("Would you like to configure your X server at this time?")))
	    (void)configXSetup(self);
    }

    dialog_clear_norefresh();
    KANJI_MESSAGE(
    "FreeBSD パッケージコレクションは, テキストエディタから WEB サーバ,\n"
    "他にも色々な何百ものすぐに使用することのできるアプリケーションのコレ\n"
    "クションです. このコレクションの一覧を今ご覧になりますか?");
    if (!msgYesNo(KM("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
		  "applications, from text editors to games to WEB servers and more.  Would you\n"
		  "like to browse the collection now?"))) {
	(void)configPackages(self);
    }

    KANJI_MESSAGE("システムにユーザ・アカウントの初期設定を行いますか?\n"
		  "少なくともあなた自身のアカウントをここで追加することを勧めます\n"
		  "\"root\"ユーザで作業する事は危険だからです (容易に全システムに\n"
		  "対して災厄を与えます).");
    if (!msgYesNo(KM("Would you like to add any initial user accounts to the system?\n"
		  "Adding at least one account for yourself at this stage is suggested\n"
		  "since working as the \"root\" user is dangerous (it is easy to do\n"
		  "things which adversely affect the entire system).")))
	(void)configUsers(self);

    KANJI_MESSAGE("システム管理者のパスワードを必ず設定して下さい.\n"
		  "これが \"root\"としてログインする場合のパスワードになります.");
    msgConfirm(KM("Now you must set the system manager's password.\n"
	       "This is the password you'll use to log in as \"root\"."));
#if 1
    if (!systemExecute("passwd root"))
#else
    if (!passwdRoot(NULL))
#endif
	variable_set2("root_password", "YES", 0);

    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */

    /* Give user the option of one last configuration spree */
    dialog_clear_norefresh();
    installConfigure();
    return DITEM_LEAVE_MENU;
}

/* The version of commit we call from the Install Custom menu */
int
installCustomCommit(dialogMenuItem *self)
{
    int i;

    i = installCommit(self);
    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
	/* Give user the option of one last configuration spree */
	installConfigure();
	return i;
    }
    else {
	KANJI_MESSAGE(
	"コミット処理実行中にエラーが発生しました. /etc ファイルをアップデー\n"
	"トしません.");
	msgConfirm(KM("The commit operation completed with errors.  Not\n"
		   "updating /etc files."));
    }
    return i;
}

/*
 * What happens when we finally decide to going ahead with the installation.
 *
 * This is broken into multiple stages so that the user can do a full
 * installation but come back here again to load more distributions,
 * perhaps from a different media type.  This would allow, for
 * example, the user to load the majority of the system from CDROM and
 * then use ftp to load just the DES dist.
 */
int
installCommit(dialogMenuItem *self)
{
    int i;
    char *str;

    dialog_clear_norefresh();
    if (!Dists)
	distConfig(NULL);

    if (!Dists)
	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
	    return DITEM_FAILURE;

    if (!mediaVerify())
	return DITEM_FAILURE;

    str = variable_get(SYSTEM_STATE);
    if (isDebug())
	msgDebug("installCommit: System state is `%s'\n", str);

    /* Installation stuff we wouldn't do to a running system */
    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
	return i;

try_media:
    if (!mediaDevice->init(mediaDevice)) {
	KANJI_MESSAGE("選択されたメディアを初期化できません. メディアの設定を\n"
		      "調整して再挑戦しますか?");
	if (!msgYesNo(KM("Unable to initialize selected media. Would you like to\n"
		      "adjust your media configuration and try again?"))) {
	    mediaDevice = NULL;
	    if (!mediaVerify())
		return DITEM_FAILURE;
	    else
		goto try_media;
	}
	else
	    return DITEM_FAILURE;
    }

    /* Now go get it all */
    i = distExtractAll(self);

    /* When running as init, *now* it's safe to grab the rc.foo vars */
    installEnvironment();

    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);

    return i;
}

static void
installConfigure(void)
{
    /* Final menu of last resort */
    KANJI_MESSAGE("未設定のオプション設定のために, 総合設定メニューに移動しますか?");
    if (!msgYesNo(KM("Visit the general configuration menu for a chance to set\n"
		  "any last options?")))
	dmenuOpenSimple(&MenuConfigure, FALSE);
    configRC_conf();
    sync();
}

int
installFixupBin(dialogMenuItem *self)
{
    Device **devs;
    char *cp;
    int i;
    FILE *fp;
    struct statfs rootfs;
    int kstat = 1;

    /* All of this is done only as init, just to be safe */
    if (RunningAsInit) {
	/* Fix up kernel first */
	if (!file_readable("/kernel")) {
#ifdef PC98
	    char *generic_kernel = "/kernel.GENERIC98";
	    if (file_readable(generic_kernel)) {
		if (vsystem("cp -p %s /kernel", generic_kernel)) {
		    KANJI_MESSAGE("/kernel をコピーできません!");
		    msgConfirm(KM("Unable to copy /kernel into place!"));
		    return DITEM_FAILURE;
		}
                /* Snapshot any boot -c changes back to the new kernel */
		cp = variable_get(VAR_KGET);
		if (cp && (*cp == 'Y' || *cp == 'y')) {
		    if (kstat = kget("/boot/kernel.conf")) {
			KANJI_MESSAGE(
			"カーネルコピーは OK です, しかし boot -c での変更は保存できま\n"
			"せん. 詳細はデバッグスクリーン (GRPH-F2) を見てください.");
			msgConfirm(KM("Kernel copied OK, but unable to save boot -c changes\n"
				   "to it.  See the debug screen (GRPH-F2) for details."));
		    }
		}
		if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
		    fprintf(fp, "# -- sysinstall generated deltas -- #\n");
		    if (!kstat)
			fprintf(fp, "userconfig_script_load=\"YES\"\n");
		    if (!OnVTY)
			fprintf(fp, "console=\"comconsole\"\n");
		    if (!statfs("/", &rootfs)) {
			char *cp = rootfs.f_mntfromname;

			while (*cp && !isdigit(*cp))
			    ++cp;
			fprintf(fp, "root_disk_unit=\"%d\"\n", *cp - 48);
		    }
		    fclose(fp);
		}
	    }
	    else {
		KANJI_MESSAGE(
		"root ファイルシステム上にリンクすべきカーネルイメージがありません.\n"
		"このシステムをハードディスクからブートできるようにするのは結構大変\n"
		"でしょう. 心配です!");
		msgConfirm(KM("Can't find a kernel image to link to on the root file system!\n"
			   "You're going to have a hard time getting this system to\n"
			   "boot from the hard disk, I'm afraid!"));
		return DITEM_FAILURE;
	    }
#else
	    char *generic_kernel = "/kernel.GENERIC";
	    if (file_readable(generic_kernel)) {
		if (vsystem("cp -p %s /kernel", generic_kernel)) {
		    KANJI_MESSAGE("/kernel をコピーできません!");
		    msgConfirm(KM("Unable to copy /kernel into place!"));
		    return DITEM_FAILURE;
		}
#ifndef __alpha__
                /* Snapshot any boot -c changes back to the new kernel */
		cp = variable_get(VAR_KGET);
		if (cp && (*cp == 'Y' || *cp == 'y')) {
		    if ((kstat = kget("/boot/kernel.conf")) != NULL) {
			KANJI_MESSAGE(
			"カーネルコピーは OK です, しかし boot -c での変更は保存できま\n"
			"せん. 詳細はデバッグスクリーン (ALT-F2) を見てください.");
			msgConfirm(KM("Kernel copied OK, but unable to save boot -c changes\n"
				   "to it.  See the debug screen (ALT-F2) for details."));
		    }
		}
		if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
		    fprintf(fp, "# -- sysinstall generated deltas -- #\n");
		    if (!kstat)
			fprintf(fp, "userconfig_script_load=\"YES\"\n");
		    if (!OnVTY)
			fprintf(fp, "console=\"comconsole\"\n");
		    if (!statfs("/", &rootfs)) {
			char *cp = rootfs.f_mntfromname;

			while (*cp && !isdigit(*cp))
			    ++cp;
			fprintf(fp, "root_disk_unit=\"%d\"\n", *cp - 48);
		    }
		    fclose(fp);
		}
#endif
	    }
	    else {
		KANJI_MESSAGE(
		"root ファイルシステム上にリンクすべきカーネルイメージがありません.\n"
		"このシステムをハードディスクからブートできるようにするのは結構大変\n"
		"でしょう. 心配です!");
		msgConfirm(KM("Can't find a kernel image to link to on the root file system!\n"
			   "You're going to have a hard time getting this system to\n"
			   "boot from the hard disk, I'm afraid!"));
		return DITEM_FAILURE;
	    }
#endif
	}

#ifdef PC98
	if (file_readable("/kernel.GENERIC")) {
	    KANJI_MESSAGE("/kernel.GENERIC 削除");
	    msgNotify(KM("Remove /kernel.GENERIC"));
	    if (vsystem("rm -f /kernel.GENERIC")) {
		KANJI_MESSAGE("kernel.GENERIC が削除できません!");
		msgConfirm(KM("Can't remove kernel.GENERIC!"));
	    }
	}
	if (file_readable("/boot/boot0")) {
	    KANJI_MESSAGE("/boot/boot0 削除");
	    msgNotify(KM("Remove /boot/boot0"));
	    if (vsystem("rm -f /boot/boot0")) {
		KANJI_MESSAGE("/boot/boot0 が削除できません!");
		msgConfirm(KM("Can't remove /boot/boot0!"));
	    }
	}
#endif

	/* BOGON #1: Resurrect /dev after bin distribution screws it up */
	dialog_clear_norefresh();
	KANJI_MESSAGE("全デバイスを作り直しています…しばらくお待ち下さい!");
	msgNotify(KM("Remaking all devices.. Please wait!"));
	if (vsystem("cd /dev; sh MAKEDEV all")) {
	    KANJI_MESSAGE("MAKEDEV が非零のステータスを返しました");
	    msgConfirm(KM("MAKEDEV returned non-zero status"));
	    return DITEM_FAILURE | DITEM_RESTORE;
	}

	dialog_clear_norefresh();
	KANJI_MESSAGE("/dev にスライスエントリを復活させています..");
	msgNotify(KM("Resurrecting /dev entries for slices.."));
	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
	if (!devs) {
	    KANJI_MESSAGE("ディスクデバイスのリストを取得できません!");
	    msgFatal(KM("Couldn't get a disk device list!"));
	}

	/* Resurrect the slices that the former clobbered */
	for (i = 0; devs[i]; i++) {
	    Disk *disk = (Disk *)devs[i]->private;
	    Chunk *c1;
	    
	    if (!devs[i]->enabled)
		continue;
	    if (!disk->chunks) {
		KANJI_MESSAGE("%s のチャンクリストが見つかりません!");
		msgFatal(KM("No chunk list found for %s!"), disk->name);
	    }
	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
		if (c1->type == freebsd) {
		    dialog_clear_norefresh();
		    KANJI_MESSAGE("%s のスライスエントリを作っています");
		    msgNotify(KM("Making slice entries for %s"), c1->name);
		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
			KANJI_MESSAGE("%s のスライスエントリが作れません!");
			msgConfirm(KM("Unable to make slice entries for %s!"), c1->name);
			return DITEM_FAILURE | DITEM_RESTORE;
		    }
		}
	    }
	}
 
	/* BOGON #2: We leave /etc in a bad state */
	chmod("/etc", 0755);

	/* BOGON #3: No /var/db/mountdtab complains */
	Mkdir("/var/db");
	creat("/var/db/mountdtab", 0644);

	/* BOGON #4: /compat created by default in root fs */
	Mkdir("/usr/compat");
	vsystem("ln -s /usr/compat /compat");

	/* BOGON #5: aliases database not build for bin */
#if 1	/* newaliases が実行されていないようだ */
	vsystem("/usr/bin/newaliases");
#else
	vsystem("newaliases");
#endif

	/* Now run all the mtree stuff to fix things up */
        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");

	/* Do all the last ugly work-arounds here */
    }
    return DITEM_SUCCESS | DITEM_RESTORE;
}

/* Fix side-effects from the the XFree86 installation */
int
installFixupXFree(dialogMenuItem *self)
{
    /* BOGON #1:  XFree86 requires various specialized fixups */
    if (directory_exists("/usr/X11R6")) {
	dialog_clear_norefresh();
	KANJI_MESSAGE("XFree86ツリーのパーミッションを直しています…");
	msgNotify(KM("Fixing permissions in XFree86 tree.."));
	vsystem("chmod -R a+r /usr/X11R6");
#if 1	/* /usr/bin コマンドは実行されていないようだ */
	vsystem("/usr/bin/find /usr/X11R6 -type d | /usr/bin/xargs chmod a+x");
#else
	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
#endif

	/* Also do bogus minimal package registration so ports don't whine */
	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
	    dialog_clear_norefresh();
	    KANJI_MESSAGE("パッケージ metainfo をインストールしています…");
	    msgNotify(KM("Installing package metainfo.."));
	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
	}
    }
    return DITEM_SUCCESS | DITEM_RESTORE;
}

/* Go newfs and/or mount all the filesystems we've been asked to */
int
installFilesystems(dialogMenuItem *self)
{
    int i;
    Disk *disk;
    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
    Device **devs;
    PartInfo *root;
    char dname[80];
    extern int MakeDevChunk(Chunk *c, char *n);
    Boolean upgrade = FALSE;

    /* If we've already done this, bail out */
    if (!variable_cmp(DISK_LABELLED, "written"))
	return DITEM_SUCCESS;

    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
	return DITEM_FAILURE;

    if (rootdev)
	root = (PartInfo *)rootdev->private_data;
    else
	root = NULL;

    command_clear();
    if (swapdev && RunningAsInit) {
	/* As the very first thing, try to get ourselves some swap space */
	sprintf(dname, "/dev/%s", swapdev->name);
	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
	    KANJI_MESSAGE("/dev に %s のデバイスノードを作れません!\n"
		       "ファイルシステムの作成は中断するでしょう.");
	    msgConfirm(KM("Unable to make device node for %s in /dev!\n"
		       "The creation of filesystems will be aborted."), dname);
	    return DITEM_FAILURE;
	}

	if (!Fake) {
	    if (!swapon(dname)) {
		dialog_clear_norefresh();
		KANJI_MESSAGE("%s を初期スワップデバイスに追加します");
		msgNotify(KM("Added %s as initial swap device"), dname);
	    }
	    else {
		KANJI_MESSAGE(
		"警告! %s にスワップできません: %s\n"
		"あなたが十分なメモリを持っていないのであれば, これはインストールを\n"
		"どこかの時点で失敗させるでしょう.");
		msgConfirm(KM("WARNING!  Unable to swap to %s: %s\n"
			   "This may cause the installation to fail at some point\n"
			   "if you don't have a lot of memory."), dname, strerror(errno));
	    }
	}
    }

    if (rootdev && RunningAsInit) {
	/* Next, create and/or mount the root device */
	sprintf(dname, "/dev/r%s", rootdev->name);
	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
	    KANJI_MESSAGE("/dev に %s のデバイスノードを作れません!\n"
			"ファイルシステムの作成は中断するでしょう.");
	    msgConfirm(KM("Unable to make device node for %s in /dev!\n"
		       "The creation of filesystems will be aborted."), dname);
	    return DITEM_FAILURE | DITEM_RESTORE;
	}
	if (strcmp(root->mountpoint, "/")) {
	    KANJI_MESSAGE(
	    "警告: %s はルートパーティションと記されていますが,\n"
	    "%s にマウントされています.");
	    msgConfirm(KM("Warning: %s is marked as a root partition but is mounted on %s"), rootdev->name, root->mountpoint);
	}

	KANJI_MESSAGE("アップグレード中 - ルートパーティションを newfs しますか?");
	if (root->newfs && (!upgrade || !msgYesNo(KM("You are upgrading - are you SURE you want to newfs the root partition?")))) {
	    int i;

	    dialog_clear_norefresh();
	    KANJI_MESSAGE("%s に新しいルートファイルシステムを作っています");
	    msgNotify(KM("Making a new root filesystem on %s"), dname);
	    i = vsystem("%s %s", root->newfs_cmd, dname);
	    if (i) {
		KANJI_MESSAGE(
		"%s にルートファイルシステムを作ることができません!\n"
		"コマンドが %d のステータスを返しています.");
		msgConfirm(KM("Unable to make new root filesystem on %s!\n"
			   "Command returned status %d"), dname, i);
		return DITEM_FAILURE | DITEM_RESTORE;
	    }
	}
	else {
	    if (!upgrade) {
		/* AT 日本語版のメッセージと異なる */
		KANJI_MESSAGE(
		"警告: 既存のルートパーティションが使われています. そこで,\n"
		"/dev に, すでに適切なデバイスエントリが全て存在しているということ\n"
		"を仮定します.");
		msgConfirm(KM("Warning:  Using existing root partition.  It will be assumed\n"
			   "that you have the appropriate device entries already in /dev."));
	    }
	    dialog_clear_norefresh();
	    KANJI_MESSAGE("既存の %s ファイルシステムの一貫性をチェックしています.");
	    msgNotify(KM("Checking integrity of existing %s filesystem."), dname);
	    i = vsystem("fsck -y %s", dname);
	    if (i) {
		KANJI_MESSAGE("警告: fsck は %d のステータスを %s に対して返しました.\n"
		"このパーティションを使用することは危険です.");
		msgConfirm(KM("Warning: fsck returned status of %d for %s.\n"
			   "This partition may be unsafe to use."), i, dname);
	    }
	}

	/* Switch to block device */
	sprintf(dname, "/dev/%s", rootdev->name);
	if (Mount("/mnt", dname)) {
	    KANJI_MESSAGE(
	    "ルートファイルシステムを %s にマウントできません.\n"
	    "あきらめます.");
	    msgConfirm(KM("Unable to mount the root file system on %s!  Giving up."), dname);
	    return DITEM_FAILURE | DITEM_RESTORE;
	}
    }

    /* Now buzz through the rest of the partitions and mount them too */
    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
    for (i = 0; devs[i]; i++) {
	if (!devs[i]->enabled)
	    continue;

	disk = (Disk *)devs[i]->private;
	if (!disk->chunks) {
	    KANJI_MESSAGE("%s のチャンクリストが見つかりません!");
	    msgConfirm(KM("No chunk list found for %s!"), disk->name);
	    return DITEM_FAILURE | DITEM_RESTORE;
	}
	if (RunningAsInit && root && (root->newfs || upgrade)) {
	    Mkdir("/mnt/dev");
	    if (!Fake)
		MakeDevDisk(disk, "/mnt/dev");
	}
	else if (!RunningAsInit && !Fake)
	    MakeDevDisk(disk, "/dev");

	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
	    if (c1->type == freebsd) {
		for (c2 = c1->part; c2; c2 = c2->next) {
		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
			PartInfo *tmp = (PartInfo *)c2->private_data;

			/* Already did root */
			if (c2 == rootdev)
			    continue;

			KANJI_MESSAGE("アップグレード中 - /dev/%s を newfs しますか?");
			if (tmp->newfs && (!upgrade || !msgYesNo(KM("You are upgrading - are you SURE you want to newfs /dev/%s?"), c2->name)))
			    command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
			else
			    command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
			command_func_add(tmp->mountpoint, Mount, c2->name);
		    }
		    else if (c2->type == part && c2->subtype == FS_SWAP) {
			char fname[80];
			int i;

			if (c2 == swapdev)
			    continue;
			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
			i = (Fake || swapon(fname));
			if (!i) {
			    dialog_clear_norefresh();
			    KANJI_MESSAGE("%s を付加スワップデバイスに追加します");
			    msgNotify(KM("Added %s as an additional swap device"), fname);
			}
			else {
			    KANJI_MESSAGE("%s をスワップデバイスとして追加できません: %s");
			    msgConfirm(KM("Unable to add %s as a swap device: %s"), fname, strerror(errno));
			}
		    }
		}
	    }
	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
		char name[FILENAME_MAX];

		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
		Mkdir(name);
	    }
	}
    }

    if (RunningAsInit) {
	dialog_clear_norefresh();
	KANJI_MESSAGE("初期デバイスファイルをコピーしています…");
	msgNotify(KM("Copying initial device files.."));
	/* Copy the boot floppy's dev files */
	if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
	    KANJI_MESSAGE("/dev ファイル群を複製できません!");
	    msgConfirm(KM("Couldn't clone the /dev files!"));
	    return DITEM_FAILURE | DITEM_RESTORE;
	}
    }

    command_sort();
    command_execute();
    dialog_clear_norefresh();
    return DITEM_SUCCESS | DITEM_RESTORE;
}

static char *
getRelname(void)
{
    static char buf[64];
    int sz = (sizeof buf) - 1;

    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
	buf[sz] = '\0';
	return buf;
    }
    else
	return "<unknown>";
}

/* Initialize various user-settable values to their defaults */
int
installVarDefaults(dialogMenuItem *self)
{
    char *cp;

    /* Set default startup options */
    variable_set2(VAR_RELNAME,			getRelname(), 0);
    variable_set2(VAR_CPIO_VERBOSITY,		"high", 0);
    variable_set2(VAR_KGET,			"YES", 0);
    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE, 0);
    variable_set2(VAR_INSTALL_ROOT,		"/", 0);
    variable_set2(VAR_INSTALL_CFG,		"install.cfg", 0);
    variable_set2(VAR_TRY_DHCP,			"NO", 0);	/* For now */
    cp = getenv("EDITOR");
    if (!cp)
	cp = "/usr/bin/ee";
    variable_set2(VAR_EDITOR,			cp, 0);
    variable_set2(VAR_FTP_USER,			"ftp", 0);
    variable_set2(VAR_BROWSER_PACKAGE,		"lynx", 0);
    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/lynx", 0);
    variable_set2(VAR_FTP_STATE,		"passive", 0);
    variable_set2(VAR_NFS_SECURE,		"NO", -1);
    variable_set2(VAR_PKG_TMPDIR,		"/usr/tmp", 0);
#ifdef	DOCJP
    variable_set2(VAR_JPHB_PKG,			"jp-handbook-2.2", 0);
    variable_set2(VAR_JPHB_PATH,		"/usr/share/doc/ja_JP.EUC/handbook", 0);
#endif	/* DOCJP */
    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT), 0);
    if (getpid() != 1)
	variable_set2(SYSTEM_STATE,		"update", 0);
    else
	variable_set2(SYSTEM_STATE,		"init", 0);
    variable_set2(VAR_NEWFS_ARGS,		"-b 8192 -f 1024", 0);
    return DITEM_SUCCESS;
}

/* Load the environment up from various system configuration files */
void
installEnvironment(void)
{
    configEnvironmentRC_conf();
    if (file_readable("/etc/resolv.conf"))
	configEnvironmentResolv("/etc/resolv.conf");
}

/* Copy the boot floppy contents into /stand */
Boolean
copySelf(void)
{
    int i;

    if (file_readable("/boot.help"))
	vsystem("cp /boot.help /mnt");
    KANJI_MESSAGE(
    "ブートフロッピーをルートファイルシステムの\n"
    "/stand にコピーしています");
    msgWeHaveOutput(KM("Copying the boot floppy to /stand on root filesystem"));
    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
    if (i) {
	KANJI_MESSAGE("コピーが %d のエラーステータスを返しました!");
	msgConfirm(KM("Copy returned error status of %d!"), i);
	return FALSE;
    }

    /* Copy the /etc files into their rightful place */
    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
	KANJI_MESSAGE("/etc ファイルをコピーできません!");
	msgConfirm(KM("Couldn't copy up the /etc files!"));
	return TRUE;
    }
    return TRUE;
}

static void
create_termcap(void)
{
    FILE *fp;

    const char *caps[] = {
	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
#ifdef AT_KANJI
	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
	termcap_kons24jx, termcap_kons24jx_m, NULL,
#else
	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
#endif
    };
    const char **cp;

    if (!file_readable(TERMCAP_FILE)) {
	Mkdir("/usr/share/misc");
	fp = fopen(TERMCAP_FILE, "w");
	if (!fp) {
	    KANJI_MESSAGE(
	    "termcap ファイルを初期化できません. いくつかのスクリーン指向のユー\n"
	    "ティリテイは使用できません.");
	    msgConfirm(KM("Unable to initialize termcap file. Some screen-oriented\nutilities may not work."));
	    return;
	}
	cp = caps;
	while (*cp)
	    fprintf(fp, "%s\n", *(cp++));
	fclose(fp);
    }
}
