/*
 * 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/disks.c,v 1.108.2.5 1999/12/17 02:46:33 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 <fcntl.h>
#include <sys/stat.h>
#include <sys/disklabel.h>

/* Where we start displaying chunk information on the screen */
#define CHUNK_START_ROW		5

/* Where we keep track of MBR chunks */
static struct chunk *chunk_info[16];
static int current_chunk;

static void	diskPartitionNonInteractive(Device *dev);

static void
record_chunks(Disk *d)
{
    struct chunk *c1 = NULL;
    int i = 0;
    int last_free = 0;

    if (!d->chunks) {
	KANJI_MESSAGE("%s に対するチャンクのリストが見つかりません!");
	msgFatal(KM("No chunk list found for %s!"), d->name);
    }

    for (c1 = d->chunks->part; c1; c1 = c1->next) {
	if (c1->type == unused && c1->size > last_free) {
	    last_free = c1->size;
	    current_chunk = i;
	}
	chunk_info[i++] = c1;
    }
    chunk_info[i] = NULL;
    if (current_chunk >= i)
	current_chunk = i - 1;
}

static int Total;

static void
print_chunks(Disk *d)
{
    int row;
    int i;

    for (i = Total = 0; chunk_info[i]; i++)
	Total += chunk_info[i]->size;
#ifdef PC98
    if (d->bios_cyl > 65536 || d->bios_hd > 65536 || d->bios_sect >= 65536) {
#else
    if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
#endif
	dialog_clear_norefresh();
	KANJI_MESSAGE(
	"警告: ジオメトリ %d/%d/%d (%s) は不正です.\n"
	"より それらしいジオメトリを使用しています. もしこのジオメトリが正し\n"
	"くない場合, またはこれが正しいのか正しくないのかがわからない場合は,\n"
	" 文書サブメニューのハードウェアガイドを検討するか, G (ジオメトリ)\n"
	"コマンドで正しい値に設定して下さい.\n\n"
	"参考: あなたのマシンの BIOS が使っているジオメトリを入力する必要があ\n"
	"ることを覚えておいて下さい. IDE ついては, BIOSのセットアップで指定し\n"
	"た値, SCSIは, コントローラが使っている変換モードになります. ``物理的\n"
	"なジオメトリ'' を使ってはいけません.");
	msgConfirm(KM("WARNING:  A geometry of %d/%d/%d for %s is incorrect.  Using\n"
		   "a more likely geometry.  If this geometry is incorrect or you\n"
		   "are unsure as to whether or not it's correct, please consult\n"
		   "the Hardware Guide in the Documentation submenu or use the\n"
		   "(G)eometry command to change it now.\n\n"
		   "Remember: you need to enter whatever your BIOS thinks the\n"
		   "geometry is!  For IDE, it's what you were told in the BIOS\n"
		   "setup. For SCSI, it's the translation mode your controller is\n"
		   "using.  Do NOT use a ``physical geometry''."),
	  d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
	Sanitize_Bios_Geom(d);
    }
    attrset(A_NORMAL);
    KANJI_MESSAGE("ディスク名:\t");
    mvaddstr(0, 0, KM("Disk name:\t"));
    clrtobot();
    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
    { int pos = (kanji) ? 51: 55;
    KANJI_MESSAGE("FDISK パーティションエディタ");
    attrset(A_REVERSE); mvaddstr(0, pos, KM("FDISK Partition Editor")); attrset(A_NORMAL);
    }
    KANJI_MESSAGE("ディスクジオメトリ:\t%lu シリンダ / %lu ヘッド / %lu セクタ = %lu セクタ");
    mvprintw(1, 0, KM(
	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %lu sectors"),
	     d->bios_cyl, d->bios_hd, d->bios_sect,
	     d->bios_cyl * d->bios_hd * d->bios_sect);
#ifdef KANJI
    if (kanji) {
    mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s",
	     "開始位置", "大きさ", "終了位置", "名称", "タイプ", "種別",
	     "副タイプ", "フラグ");
    }
    else {
#endif	/* KANJI */
#ifndef KANJI_ONLY
    mvprintw(3, 1, "%10s %10s %10s %8s %6s %10s %8s %8s",
	     "Offset", "Size", "End", "Name", "PType", "Desc",
	     "Subtype", "Flags");
#endif	/* KANJI_ONLY */
#ifdef KANJI
    }
#endif	/* KANJI */
    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
	if (i == current_chunk)
	    attrset(ATTR_SELECTED);
	mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
		 chunk_info[i]->offset, chunk_info[i]->size,
		 chunk_info[i]->end, chunk_info[i]->name,
		 chunk_info[i]->type, 
		 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
	if (i == current_chunk)
	    attrset(A_NORMAL);
    }
}

static void
print_command_summary()
{
#ifdef KANJI
    if (kanji) {
    mvprintw(14, 0, "以下のコマンドがサポートされています (大文字小文字を問いません):");
    mvprintw(16, 0, "A = ディスク全体を使用  B = 不良ブロック調査   C = スライス作成");
    mvprintw(17, 0, "D = スライス削除        G = ジオメトリ設定     S = ブート可能に設定");
    mvprintw(18, 0, "T = タイプを変更        U = 全ての変更を取消   Q = 終了");
    if (!RunningAsInit)
	mvprintw(19, 0, "W = 変更を書き込む");
    mvprintw(21, 0, "F1 または ? でさらに詳しいヘルプ, 矢印キーで選択.");
    }
    else {
#endif	/* KANJI */
#ifndef KANJI_ONLY
    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
    mvprintw(16, 0, "A = Use Entire Disk    B = Bad Block Scan       C = Create Slice");
    mvprintw(17, 0, "D = Delete Slice       G = Set Drive Geometry   S = Set Bootable");
    mvprintw(18, 0, "T = Change Type        U = Undo All Changes     Q = Finish");
    if (!RunningAsInit)
	mvprintw(19, 0, "W = Write Changes");
    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
#endif	/* KANJI_ONLY */
#ifdef KANJI
    }
#endif	/* KANJI */
    move(0, 0);
}

#ifndef PC98
static u_char *
getBootMgr(char *dname)
{
#ifndef __alpha__	/* only meaningful on x86 */
    extern u_char mbr[], boot0[];
    char str[80];
    char *cp;
    int i = 0;

    cp = variable_get(VAR_BOOTMGR);
    if (!cp) {
	/* Figure out what kind of MBR the user wants */
	KANJI_MESSAGE("ドライブ %s にブートマネージャをインストールしますか?");
	sprintf(str, KM("Install Boot Manager for drive %s?"), dname);
	MenuMBRType.title = str;
	i = dmenuOpenSimple(&MenuMBRType, FALSE);
    }
    else {
	if (!strncmp(cp, "boot", 4))
	    BootMgr = 0;
	else if (!strcmp(cp, "standard"))
	    BootMgr = 1;
	else
	    BootMgr = 2;
    }
    if (cp || i) {
	switch (BootMgr) {
	case 0:
	    return boot0;

	case 1:
	    return mbr;

	case 2:
	default:
	    break;
	}
    }
#endif
    return NULL;
}
#endif

int
diskGetSelectCount(Device ***devs)
{
    int i, cnt, enabled;
    char *cp;
    Device **dp;

    cp = variable_get(VAR_DISK);
    dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
    cnt = deviceCount(dp);
    if (!cnt)
	return -1;
    for (i = 0, enabled = 0; i < cnt; i++) {
	if (dp[i]->enabled)
	    ++enabled;
    }
    return enabled;
}

void
diskPartition(Device *dev)
{
    char *cp, *p;
    int rv, key = 0;
    Boolean chunking;
    char *msg = NULL;
#ifndef PC98
    u_char *mbrContents;
#endif
    WINDOW *w = savescr();
    Disk *d = (Disk *)dev->private;

    chunking = TRUE;
    keypad(stdscr, TRUE);

    /* Flush both the dialog and curses library views of the screen
       since we don't always know who called us */
    dialog_clear_norefresh(), clear();
    current_chunk = 0;

    /* Set up the chunk array */
    record_chunks(d);

    while (chunking) {
	char *val, geometry[80];
	    
	/* Now print our overall state */
	if (d)
	    print_chunks(d);
	print_command_summary();
	if (msg) {
	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
	    beep();
	    msg = NULL;
	}
	else {
	    move(23, 0);
	    clrtoeol();
	}

	/* Get command character */
	key = getch();
	switch (toupper(key)) {
	case '\014':	/* ^L (redraw) */
	    clear();
	    msg = NULL;
	    break;
	    
	case '\020':	/* ^P */
	case KEY_UP:
	case '-':
	    if (current_chunk != 0)
		--current_chunk;
	    break;
	    
	case '\016':	/* ^N */
	case KEY_DOWN:
	case '+':
	case '\r':
	case '\n':
	    if (chunk_info[current_chunk + 1])
		++current_chunk;
	    break;

	case KEY_HOME:
	    current_chunk = 0;
	    break;

	case KEY_END:
	    while (chunk_info[current_chunk + 1])
		++current_chunk;
	    break;

	case KEY_F(1):
	case '?':
	    systemDisplayHelp("slice");
	    clear();
	    break;

	case 'A':
#ifdef __alpha__
	    rv = 1;
#else	    /* The rest is only relevant on x86 */
	    cp = variable_get(VAR_DEDICATE_DISK);
	    if (cp && !strcasecmp(cp, "always"))
		rv = 1;
	    else {
		KANJI_MESSAGE(
		"正しいパーティションエントリを保持して, このドライブには将来的に他の\n"
		"オペレーティングシステムを同居させる余地を残しますか?");
		/*"注意: FreeBSD(98)では余地を残さないとカーネルが立ち上がりませんので,\n"
		"Yes を選択してください. MO(od)装置の場合だけ No を選択してください.");*/
		/*"(FreeBSD FAQ の中の``dangerously (危険な) dedicated''セクションを\n"
		"参照してください)");*/
		rv = msgYesNo(KM("Do you want to do this with a true partition entry\n"
			      "so as to remain cooperative with any future possible\n"
			      "operating systems on the drive(s)?\n"
			      "(See also the section about ``dangerously dedicated''\n"
			      "disks in the FreeBSD FAQ.)"));
		if (rv == -1)
		    rv = 0;
	    }
#endif
	    All_FreeBSD(d, rv);
	    variable_set2(DISK_PARTITIONED, "yes", 0);
	    record_chunks(d);
	    clear();
	    break;
	    
	case 'B':
	    KANJI_MESSAGE(
	    "これは一般に, ESDI, IDE または MFM ドライブのみに意味のある操作で\n"
	    "す. 本当にこれを SCSI ディスクに対して実行したいのですか?");
	    if (chunk_info[current_chunk]->type != freebsd) {
		KANJI_MESSAGE("FreeBSD スライス内にある不良ブロックのみを調査できます.");
		msg = KM("Can only scan for bad blocks in FreeBSD slice.");
	    }
	    else if (strncmp(d->name, "sd", 2) ||
		     strncmp(d->name, "da", 2) ||
		     !msgYesNo(KM("This typically makes sense only for ESDI, IDE or MFM drives.\n"
			       "Are you sure you want to do this on a SCSI disk?"))) {
		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
		else
		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
	    }
	    clear();
	    break;
	    
	case 'C':
	    if (chunk_info[current_chunk]->type != unused) {
		KANJI_MESSAGE(
		"スライスは使用中です. まず削除するか, "
		"未使用領域を選択して下さい.");
		msg = KM("Slice in use, delete it first or move to an unused one.");
	    }
	    else {
		char *val, tmp[20], *cp;
#ifdef PC98
		int size;
#else
		int size, subtype;
		chunk_e partitiontype;
#endif
#ifdef PC98
		char name[16];

		snprintf(name, 16, "%s", "FreeBSD");
		KANJI_MESSAGE("新しい FreeBSD スライスの名前を入力して下さい.");
		val = msgGetInput(name, KM("Please specify the name for new FreeBSD slice."));
		if (val)
		    strncpy(name, val, 16);
#endif
		
		snprintf(tmp, 20, "%lu", chunk_info[current_chunk]->size);
		KANJI_MESSAGE(
		"新しい FreeBSD のスライスの大きさをブロック数単位, あるいは\n"
		"最後に `M' をつけてメガバイト単位で指定して下さい (例: 20M).");
		val = msgGetInput(tmp, KM("Please specify the size for new FreeBSD slice in blocks\n"
				  "or append a trailing `M' for megabytes (e.g. 20M)."));
		if (val && (size = strtol(val, &cp, 0)) > 0) {
		    if (*cp && toupper(*cp) == 'M')
			size *= ONE_MEG;
#ifdef PC98
		    Create_Chunk(d, chunk_info[current_chunk]->offset,
				 size, freebsd, 3,
				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN),
				 name);
		    variable_set2(DISK_PARTITIONED, "yes", 0);
		    record_chunks(d);
#else
		    strcpy(tmp, "165");
		    KANJI_MESSAGE(
		    "生成するパーティションのタイプを指定して下さい. Enter を単に押せば,\n"
		    "ネイティブの FreeBSD スライス (タイプ 165) に設定されます.\n"
		    "この他にも DOS パーティションであれば 6 を, Linux パーティションな\n"
		    "らば 131 を指定して下さい.\n"
		    "注意: FreeBSD 以外の領域を選択した場合は, その領域は単に空間が確保\n"
		    "されるだけであり, フォーマットなどの操作は一切行なわれません. 後で \n"
		    "DOS の FORMAT などのツールでその領域を使用可能として下さい.");
		    val = msgGetInput(tmp, KM("Enter type of partition to create:\n\n"
				      "Pressing Enter will choose the default, a native FreeBSD\n"
				      "slice (type 165).  You can choose other types, 6 for a\n"
				      "DOS partition or 131 for a Linux partition, for example.\n\n"
				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
				      "be formatted or otherwise prepared, it will simply reserve space\n"
				      "for you to use another tool, such as DOS FORMAT, to later format\n"
				      "and use the partition."));
		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
			if (subtype == 165)
			    partitiontype = freebsd;
			else if (subtype == 6)
			    partitiontype = fat;
			else
			    partitiontype = unknown;
#ifdef __alpha__
			if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
			    All_FreeBSD(d, 1);
			else
#endif
			Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
				     (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
			variable_set2(DISK_PARTITIONED, "yes", 0);
			record_chunks(d);
		    }
#endif
		}
		clear();
	    }
	    break;
	    
	case KEY_DC:
	case 'D':
	    if (chunk_info[current_chunk]->type == unused) {
		KANJI_MESSAGE("スライスはすでに未使用状態です!");
		msg = KM("Slice is already unused!");
	    }
	    else {
		Delete_Chunk(d, chunk_info[current_chunk]);
		variable_set2(DISK_PARTITIONED, "yes", 0);
		record_chunks(d);
	    }
	    break;
	    
	case 'T':
	    if (chunk_info[current_chunk]->type == unused) {
		KANJI_MESSAGE("スライスは現在未使用です(代わりに作成します)");
		msg = KM("Slice is currently unused (use create instead)");
	    }
	    else {
		char *val, tmp[20];
		int subtype;
		chunk_e partitiontype;

#ifdef PC98
		strcpy(tmp, "50324");
		KANJI_MESSAGE("ニューパーティションタイプ:\n\n"
		"FreeBSD スライス (タイプ 50324) の省略時解釈を選ぶならリターン\n"
		"を押してください. この他にも DOS パーティションであれば 37218 \n"
		"を, Linux パーティションならば 58016 または Linux スワップパー\n"
		"ティションなら 57888 です.\n\n"
		"注意: FreeBSD 以外の領域を選択した場合は, その領域は単に空間\n"
		"が確保されるだけであり, フォーマットなどの操作は一切行なわれ\n"
		"ません. 後で DOS の FORMAT などのツールでその領域を使用可能と\n"
		"して下さい.");
		val = msgGetInput(tmp, KM("New partition type:\n\n"
				  "Pressing Enter will choose the default, a native FreeBSD\n"
				  "slice (type 50324).  Other popular values are 37218 for\n"
				  "DOS FAT partition.\n\n"
				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
				  "be formatted or otherwise prepared, it will simply reserve space\n"
				  "for you to use another tool, such as DOS format, to later format\n"
				  "and actually use the partition."));
		if (val && (subtype = strtol(val, NULL, 0)) > 0) {
		    if (subtype == 50324)
			partitiontype = freebsd;
		    else if (subtype == 37218)
			partitiontype = fat;
		    else
			partitiontype = unknown;
		    chunk_info[current_chunk]->type = partitiontype;
		    chunk_info[current_chunk]->subtype = subtype;
		}
#else
		strcpy(tmp, "165");
		KANJI_MESSAGE("ニューパーティションタイプ:\n\n"
		"FreeBSD スライス (タイプ 165) の省略時解釈を選ぶならリターン\n"
		"を押してください. この他にも DOS パーティションであれば 6 を,\n"
		"Linux パーティションならば 131 または Linux スワップパーティ\n"
		"ションなら 130 です.\n\n"
		"注意: FreeBSD 以外の領域を選択した場合は, その領域は単に空間\n"
		"が確保されるだけであり, フォーマットなどの操作は一切行なわれ\n"
		"ません. 後で DOS の FORMAT などのツールでその領域を使用可能と\n"
		"して下さい.");
		val = msgGetInput(tmp, KM("New partition type:\n\n"
				  "Pressing Enter will choose the default, a native FreeBSD\n"
				  "slice (type 165).  Other popular values are 6 for\n"
				  "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
				  "130 for a Linux swap partition.\n\n"
				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
				  "be formatted or otherwise prepared, it will simply reserve space\n"
				  "for you to use another tool, such as DOS format, to later format\n"
				  "and actually use the partition."));
		if (val && (subtype = strtol(val, NULL, 0)) > 0) {
		    if (subtype == 165)
			partitiontype = freebsd;
		    else if (subtype == 6)
			partitiontype = fat;
		    else
			partitiontype = unknown;
		    chunk_info[current_chunk]->type = partitiontype;
		    chunk_info[current_chunk]->subtype = subtype;
		}
#endif
	    }
	    break;
	    
	case 'G':
	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
	    KANJI_MESSAGE(
	    "新しいジオメトリを「シリンダ/ヘッダ/セクタ」の形式で指定して下さい.\n"
	    "この際, 2 つのスラッシュ (/) で区切ることを忘れないようにして下さ\n"
	    "い! これがないとそれぞれのフィールドを解釈することができません.");
	    val = msgGetInput(geometry, KM("Please specify the new geometry in cyl/hd/sect format.\n"
			      "Don't forget to use the two slash (/) separator characters!\n"
			      "It's not possible to parse the field without them."));
	    if (val) {
		long nc, nh, ns;
		nc = strtol(val, &val, 0);
		nh = strtol(val + 1, &val, 0);
		ns = strtol(val + 1, 0, 0);
		Set_Bios_Geom(d, nc, nh, ns);
	    }
	    clear();
	    break;
	
	case 'S':
	    /* Set Bootable */
	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
	    break;
	
	case 'U':
	    KANJI_MESSAGE("全てを取り消しても良いですか?");
	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
		KANJI_MESSAGE("すでに この情報を書き込んでしまいました - 取り\n"
			      "消す事はできません.");
		msgConfirm(KM("You've already written this information out - you\n"
			   "can't undo it."));
	    }
	    else if (!msgYesNo(KM("Are you SURE you want to Undo everything?"))) {
		char cp[BUFSIZ];

		sstrncpy(cp, d->name, sizeof cp);
		Free_Disk(dev->private);
		d = Open_Disk(cp);
		if (!d) {
		    KANJI_MESSAGE("%s を再オープンできません! おそらく内部状態が異常になっています");
		    msgConfirm(KM("Can't reopen disk %s! Internal state is probably corrupted"), cp);
		}
		dev->private = d;
		variable_unset(DISK_PARTITIONED);
		variable_unset(DISK_LABELLED);
		if (d)
		    record_chunks(d);
	    }
	    clear();
	    break;

	case 'W':
	    KANJI_MESSAGE(
	    "警告: この操作は「現在存在している」インストール済みのドライブに対\n"
	    "する変更を行なう時にのみ使用して下さい. つまり, 初めて FreeBSD \n"
	    "をインストールしているなら, ここで必要な操作が終った時に単に Q \n"
	    "と打って下さい. あなたの変更は それらの質問の最後に自動的に まと\n"
	    "めて実行されます. もしディスクに追加するなら, この画面で書き込ん\n"
	    "ではいけません, どうしてもというならラベルエディタで行ってくださ\n"
	    "い.\n\n"
	    "それでも本当にこの操作を今行なっても構わないですか?");
	    if (!msgYesNo(KM("WARNING:  This should only be used when modifying an EXISTING\n"
			       "installation.  If you are installing FreeBSD for the first time\n"
			       "then you should simply type Q when you're finished here and your\n"
			       "changes will be committed in one batch automatically at the end of\n"
			       "these questions.  If you're adding a disk, you should NOT write\n"
			       "from this screen, you should do it from the label editor.\n\n"
			       "Are you absolutely sure you want to do this now?"))) {
		variable_set2(DISK_PARTITIONED, "yes", 0);
		
		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
		 * booteasy or a "standard" MBR -- both would be fatal in this case.
		 */
#ifndef PC98
		/*
		 * Don't offer to update the MBR on this disk if the first "real" chunk looks like
		 * a FreeBSD "all disk" partition, or the disk is entirely FreeBSD. 
		 */
		if (((d->chunks->part->type != freebsd) || (d->chunks->part->offset > 1)))
		    mbrContents = getBootMgr(d->name);
		else
		    mbrContents = NULL;
		Set_Boot_Mgr(d, mbrContents);
#endif
		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS) {
		    KANJI_MESSAGE("ディスクパーティションの書き込みがエラーとなりました!");
		    msgConfirm(KM("Disk partition write returned an error status!"));
		}
		else {
		    KANJI_MESSAGE("FDISK パーティション情報は正常に書き込まれました.");
		    msgConfirm(KM("Wrote FDISK partition information out successfully."));
		}
	    }
	    clear();
	    break;
	    
	case '|':
	    KANJI_MESSAGE(
	    "「本当に」ウィザードモードに変更してもいいのですか?\n"
	    "シートベルトはありませんよ!");
	    if (!msgYesNo(KM("Are you SURE you want to go into Wizard mode?\n"
			  "No seat belts whatsoever are provided!"))) {
		clear();
		refresh();
		slice_wizard(d);
		variable_set2(DISK_PARTITIONED, "yes", 0);
		record_chunks(d);
	    }
	    else {
		KANJI_MESSAGE("賢い選択ですね!");
		msg = KM("Wise choice!");
	    }
	    clear();
	    break;

	case '\033':	/* ESC */
	case 'Q':
	    chunking = FALSE;
	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
	     * booteasy or a "standard" MBR -- both would be fatal in this case.
	     */
#ifndef PC98
#if 0
	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
		&& (mbrContents = getBootMgr(d->name)) != NULL)
		Set_Boot_Mgr(d, mbrContents);
#else
	    /*
	     * Don't offer to update the MBR on this disk if the first "real" chunk looks like
	     * a FreeBSD "all disk" partition, or the disk is entirely FreeBSD. 
	     */
	    if (((d->chunks->part->type != freebsd) || (d->chunks->part->offset > 1)) &&
		(mbrContents = getBootMgr(d->name)) != NULL)
		Set_Boot_Mgr(d, mbrContents);
#endif
#endif
	    break;
	    
	default:
	    beep();
	    KANJI_MESSAGE("F1 または ? でヘルプ");
	    msg = KM("Type F1 or ? for help");
	    break;
	}
    }
    p = CheckRules(d);
    if (p) {
	char buf[FILENAME_MAX];
	
	KANJI_MESSAGE("F1 でディスクスライスに関するさらに詳しい解説");
        use_helpline(KM("Press F1 to read more about disk slices."));
	use_helpfile(systemHelpFile("partition", buf));
	if (!variable_get(VAR_NO_WARN)) {
	    KANJI_MESSAGE("ディスクスライスに関する警告:");
	    dialog_mesgbox(KM("Disk slicing warning:"), p, -1, -1);
	}
	free(p);
    }
    restorescr(w);
}

static u_char *
bootalloc(char *name)
{
    char buf[FILENAME_MAX];
    struct stat sb;

    snprintf(buf, sizeof buf, "/boot/%s", name);
    if (stat(buf, &sb) != -1) {
	int fd;

	fd = open(buf, O_RDONLY);
	if (fd != -1) {
	    u_char *cp;

	    cp = malloc(sb.st_size);
	    if (read(fd, cp, sb.st_size) != sb.st_size) {
		free(cp);
		close(fd);
		msgDebug("bootalloc: couldn't read %d bytes from %s\n", sb.st_size, buf);
		return NULL;
	    }
	    close(fd);
	    return cp;
	}
	msgDebug("bootalloc: couldn't open %s\n", buf);
    }
    else
	msgDebug("bootalloc: can't stat %s\n", buf);
    return NULL;
}

static int
partitionHook(dialogMenuItem *selected)
{
    Device **devs = NULL;

    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
    if (!devs) {
	KANJI_MESSAGE("ディスク %s を発見できません!");
	msgConfirm(KM("Unable to find disk %s!"), selected->prompt);
	return DITEM_FAILURE;
    }
    /* Toggle enabled status? */
    if (!devs[0]->enabled) {
	devs[0]->enabled = TRUE;
	diskPartition(devs[0]);
    }
    else
	devs[0]->enabled = FALSE;
    return DITEM_SUCCESS;
}

static int
partitionCheck(dialogMenuItem *selected)
{
    Device **devs = NULL;

    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
    if (!devs || devs[0]->enabled == FALSE)
	return FALSE;
    return TRUE;
}

int
diskPartitionEditor(dialogMenuItem *self)
{
    DMenu *menu;
    Device **devs;
    int i, cnt, devcnt;

    cnt = diskGetSelectCount(&devs);
    devcnt = deviceCount(devs);
    if (cnt == -1) {
	KANJI_MESSAGE(
	"ディスクが 1 つも見つかりません! ディスクコントローラがブート時に\n"
	"正常に認識されているかどうか確認して下さい. ドキュメンテーションメ\n"
	"ニューのハードウェアガイドもこのタイプのトラブル解決の参考となるで\n"
	"しょう.");
	msgConfirm(KM("No disks found!  Please verify that your disk controller is being\n"
		   "properly probed at boot time.  See the Hardware Guide on the\n"
		   "Documentation menu for clues on diagnosing this type of problem."));
	return DITEM_FAILURE;
    }
    else if (cnt) {
	/* Some are already selected */
	for (i = 0; i < devcnt; i++) {
	    if (devs[i]->enabled) {
		if (variable_get(VAR_NONINTERACTIVE))
		    diskPartitionNonInteractive(devs[i]);
		else
		    diskPartition(devs[i]);
	    }
	}
    }
    else {
	/* No disks are selected, fall-back case now */
	if (devcnt == 1) {
	    devs[0]->enabled = TRUE;
	    if (variable_get(VAR_NONINTERACTIVE))
		diskPartitionNonInteractive(devs[0]);
	    else
		diskPartition(devs[0]);
	    return DITEM_SUCCESS;
	}
	else {
	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
	    if (!menu) {
#ifdef PC98
		KANJI_MESSAGE(
		"インストール可能なデバイスが見つかりません!\n\n"
		"ディスクコントローラ (と接続されているドライブ) がきちんと認識され\n"
		"ているかどうかを確認して下さい. これは [vf・3] キーを押した後で矢\n"
		"印キーを押すことでブートメッセージを読むことができるので, この機能\n"
		"で確認ができます. もう一度 [vf・3] を押すと元の画面に戻ります.");
		msgConfirm(KM("No devices suitable for installation found!\n\n"
			   "Please verify that your disk controller (and attached drives)\n"
			   "were detected properly.  This can be done by pressing the\n"
			   "[vf.3] key and using the Arrow keys to move back to\n"
			   "the boot messages.  Press [vf.3] again to return."));
#else
		KANJI_MESSAGE(
		"インストール可能なデバイスが見つかりません!\n"
		"ディスクコントローラ (と接続されているドライブ) がきちんと認識され\n"
		"ているかどうかを確認して下さい. これは [Scroll Lock] キーを押した\n"
		"後で矢印キーを押すことでブートメッセージを読むことができるので, こ\n"
		"の機能で確認ができます. もう一度 [Scroll Lock] を押すと元の画面に\n"
		"戻ります.\n"
		"(現在, 日本語版ブートフロッピーではこの機能を使用できません. VT1\n"
		"上にインストーラによって検出されたデバイス群が出力されていますので\n"
		"参考にして下さい. また, fixit フロッピーを使用することで dmesg コ\n"
		"マンドによるブートメッセージの確認が可能です.)");
		msgConfirm(KM("No devices suitable for installation found!\n\n"
			   "Please verify that your disk controller (and attached drives)\n"
			   "were detected properly.  This can be done by pressing the\n"
			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
			   "the boot messages.  Press [Scroll Lock] again to return."));
#endif	/* PC98 */
		return DITEM_FAILURE;
	    }
	    else {
		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
		free(menu);
	    }
	    return i;
	}
    }
    return DITEM_SUCCESS;
}

int
diskPartitionWrite(dialogMenuItem *self)
{
    Device **devs;
    int i;
    char *cp;

    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
    if (!devs) {
	KANJI_MESSAGE("書き込みを行なうべきディスクが見つかりません??");
	msgConfirm(KM("Unable to find any disks to write to??"));
	return DITEM_FAILURE;
    }
    if (isDebug())
	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
    cp = variable_get(DISK_PARTITIONED);
    if (cp && !strcmp(cp, "written"))
	return DITEM_SUCCESS;

    for (i = 0; devs[i]; i++) {
	Chunk *c1;
	Disk *d = (Disk *)devs[i]->private;
	static u_char *boot1;
#ifndef __alpha__
	static u_char *boot2;
#endif

	if (!devs[i]->enabled)
	    continue;

#ifdef __alpha__
	if (!boot1) boot1 = bootalloc("boot1");
	Set_Boot_Blocks(d, boot1, NULL);
#else
	if (!boot1) boot1 = bootalloc("boot1");
	if (!boot2) boot2 = bootalloc("boot2");
	Set_Boot_Blocks(d, boot1, boot2);
#endif

	KANJI_MESSAGE("ドライブ %s にパーティション情報を書き込んでいます");
	msgNotify(KM("Writing partition information to drive %s"), d->name);
	if (!Fake && Write_Disk(d)) {
	    KANJI_MESSAGE("エラー: ディスク %s にデータの書き込みができません!");
	    msgConfirm(KM("ERROR: Unable to write data to disk %s!"), d->name);
	    return DITEM_FAILURE;
	}

	/* If we've been through here before, we don't need to do the rest */
	if (cp && !strcmp(cp, "written"))
	    return DITEM_SUCCESS;

	/* Now scan for bad blocks, if necessary */
	for (c1 = d->chunks->part; c1; c1 = c1->next) {
	    if (c1->flags & CHUNK_BAD144) {
		int ret;

		KANJI_MESSAGE("スライス %s の不良ブロックの検査をしています");
		msgNotify(KM("Running bad block scan on slice %s"), c1->name);
		if (!Fake) {
		    ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
		    if (ret) {
			KANJI_MESSAGE("%s に対する bad144 の初期化は, ステータス %d を返しました!");
			msgConfirm(KM("Bad144 init on %s returned status of %d!"), c1->name, ret);
		    }
		    ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
		    if (ret) {
			KANJI_MESSAGE("%s に対する bad144 のスキャンは, ステータス %d を返しました!");
			msgConfirm(KM("Bad144 scan on %s returned status of %d!"), c1->name, ret);
		    }
		}
	    }
	}
    }
    /* Now it's not "yes", but "written" */
    variable_set2(DISK_PARTITIONED, "written", 0);
    return DITEM_SUCCESS | DITEM_RESTORE;
}

/* Partition a disk based wholly on which variables are set */
static void
diskPartitionNonInteractive(Device *dev)
{
    char *cp;
    int i, sz, all_disk = 0;
#ifndef PC98
    u_char *mbrContents;
#endif
    Disk *d = (Disk *)dev->private;

    record_chunks(d);
    cp = variable_get(VAR_GEOMETRY);
    if (cp) {
	msgDebug("Setting geometry from script to: %s\n", cp);
	d->bios_cyl = strtol(cp, &cp, 0);
	d->bios_hd = strtol(cp + 1, &cp, 0);
	d->bios_sect = strtol(cp + 1, 0, 0);
    }

    cp = variable_get(VAR_PARTITION);
    if (cp) {
	if (!strcmp(cp, "free")) {
	    /* Do free disk space case */
	    for (i = 0; chunk_info[i]; i++) {
		/* If a chunk is at least 10MB in size, use it. */
		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
#ifdef PC98
		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3,
				 (chunk_info[i]->flags & CHUNK_ALIGN),
				 "FreeBSD");
#else
		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3,
				 (chunk_info[i]->flags & CHUNK_ALIGN));
#endif
		    variable_set2(DISK_PARTITIONED, "yes", 0);
		    break;
		}
	    }
	    if (!chunk_info[i]) {
		KANJI_MESSAGE("このディスク中に空きが見つかりません!");
		msgConfirm(KM("Unable to find any free space on this disk!"));
		return;
	    }
	}
	else if (!strcmp(cp, "all")) {
	    /* Do all disk space case */
	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);

	    All_FreeBSD(d, FALSE);
	}
	else if (!strcmp(cp, "exclusive")) {
	    /* Do really-all-the-disk-space case */
	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);

	    All_FreeBSD(d, all_disk = TRUE);
	}
	else if ((sz = strtol(cp, &cp, 0))) {
	    /* Look for sz bytes free */
	    if (*cp && toupper(*cp) == 'M')
		sz *= ONE_MEG;
	    for (i = 0; chunk_info[i]; i++) {
		/* If a chunk is at least sz MB, use it. */
		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
#ifdef PC98
		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN),
		    "FreeBSD");
#else
		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN));
#endif
		    variable_set2(DISK_PARTITIONED, "yes", 0);
		    break;
		}
	    }
	    if (!chunk_info[i]) {
		KANJI_MESSAGE("このディスク中に %d ブロックの空きが見つかりません!");
		msgConfirm(KM("Unable to find %d free blocks on this disk!"), sz);
		return;
	    }
	}
	else if (!strcmp(cp, "existing")) {
	    /* Do existing FreeBSD case */
	    for (i = 0; chunk_info[i]; i++) {
		if (chunk_info[i]->type == freebsd)
		    break;
	    }
	    if (!chunk_info[i]) {
		KANJI_MESSAGE("このディスク中に FreeBSD のパーティションが見つかりません!");
		msgConfirm(KM("Unable to find any existing FreeBSD partitions on this disk!"));
		return;
	    }
	}
	else {
	    KANJI_MESSAGE("`%s' は不正な値です, %s - config ファイルは正しいですか?");
	    msgConfirm(KM("`%s' is an invalid value for %s - is config file valid?"), cp, VAR_PARTITION);
	    return;
	}
#ifndef PC98
	if (!all_disk) {
	    mbrContents = getBootMgr(d->name);
	    Set_Boot_Mgr(d, mbrContents);
	}
#endif
	variable_set2(DISK_PARTITIONED, "yes", 0);
    }
}
