Home | History | Annotate | Line # | Download | only in sysinst
part_edit.c revision 1.11
      1 /*	$NetBSD: part_edit.c,v 1.11 2019/11/12 16:33:14 martin Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2019 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 
     30 /* part_edit.c -- generic partition editing code */
     31 
     32 #include <sys/param.h>
     33 #include <sys/types.h>
     34 #include <assert.h>
     35 #include <stdio.h>
     36 #include <unistd.h>
     37 #include <fcntl.h>
     38 #include <util.h>
     39 #include "defs.h"
     40 #include "md.h"
     41 #include "msg_defs.h"
     42 #include "menu_defs.h"
     43 #include "defsizes.h"
     44 #include "endian.h"
     45 
     46 
     47 /*
     48  * A structure passed to various menu functions for partition editing
     49  */
     50 struct part_edit_info {
     51 	struct disk_partitions *parts;	/* the partitions we edit */
     52 	struct disk_part_info cur;	/* current value (maybe incomplete) */
     53 	part_id cur_id;			/* which partition is it? */
     54 	int first_custom_opt;		/* scheme specific menu options
     55 					 * start here */
     56 	bool cancelled;			/* do not apply changes */
     57 	bool num_changed;		/* number of partitions has changed */
     58 };
     59 
     60 struct single_clone_data {
     61 	struct selected_partitions clone_src;
     62 	part_id *clone_ids;	/* partition IDs in target */
     63 };
     64 struct outer_parts_data {
     65 	struct arg_rv av;
     66 	struct single_clone_data *clones;
     67 	size_t num_clone_entries;
     68 };
     69 
     70 static menu_ent *part_menu_opts;		/* the currently edited partitions */
     71 static menu_ent *outer_fill_part_menu_opts(const struct disk_partitions *parts, size_t *cnt);
     72 static void draw_outer_part_line(menudesc *m, int opt, void *arg);
     73 
     74 static char 	outer_part_sep_line[MENUSTRSIZE],
     75 		outer_part_title[2*MENUSTRSIZE];
     76 
     77 static int
     78 maxline(const char *p, int *count)
     79 {
     80 	int m = 0, i = 0;
     81 
     82 	for (;; p++) {
     83 		if (*p == '\n' || *p == 0) {
     84 			if (i > m)
     85 				m = i;
     86 			(*count)++;
     87 			if (*p == 0)
     88 				return m;
     89 			i = 0;
     90 		} else {
     91 			i++;
     92 		}
     93 	}
     94 }
     95 
     96 int
     97 err_msg_win(const char *errmsg)
     98 {
     99 	const char *cont;
    100 	int l, l1, lines;
    101 
    102 	errmsg = msg_string(errmsg);
    103 	cont = msg_string(MSG_Hit_enter_to_continue);
    104 
    105 	lines = 0;
    106 	l = maxline(errmsg, &lines);
    107 	l1 = maxline(cont, &lines);
    108 	if (l < l1)
    109 		l = l1;
    110 
    111 	msg_fmt_prompt_win("%s.\n%s", -1, 18, l + 5, 2+lines,
    112 			NULL, NULL, 1, "%s%s", errmsg, cont);
    113 	return 0;
    114 }
    115 
    116 static int
    117 set_part_type(menudesc *m, void *arg)
    118 {
    119 	struct part_edit_info *info = arg;
    120 	const struct part_type_desc *desc;
    121 	char buf[STRSIZE];
    122 	const char *err;
    123 
    124 	if (m->cursel == 0)
    125 		return 1;	/* no change */
    126 
    127 	desc = info->parts->pscheme->get_part_type(m->cursel-1);
    128 	if (desc == NULL) {
    129 		/* Create custom type */
    130 		if (info->cur.nat_type != NULL)
    131 			strlcpy(buf, info->cur.nat_type->short_desc,
    132 			    sizeof(buf));
    133 		else
    134 			buf[0] = 0;
    135 		for (;;) {
    136 			msg_prompt_win(info->parts->pscheme->new_type_prompt,
    137 			     -1, 18, 0, 0,
    138 			    buf, buf, sizeof(buf));
    139 			if (buf[0] == 0)
    140 				break;
    141 			desc = info->parts->pscheme->create_custom_part_type(
    142 			    buf, &err);
    143 			if (desc != NULL)
    144 				break;
    145 			err_msg_win(err);
    146 		}
    147 	}
    148 
    149 	info->cur.nat_type = desc;
    150 	return 1;
    151 }
    152 
    153 static void
    154 set_type_label(menudesc *m, int opt, void *arg)
    155 {
    156 	struct part_edit_info *info = arg;
    157 	const struct part_type_desc *desc;
    158 
    159 	if (opt == 0) {
    160 		wprintw(m->mw, "%s", msg_string(MSG_Dont_change));
    161 		return;
    162 	}
    163 
    164 	desc = info->parts->pscheme->get_part_type(opt-1);
    165 	if (desc == NULL) {
    166 		wprintw(m->mw, "%s", msg_string(MSG_Other_kind));
    167 		return;
    168 	}
    169 	wprintw(m->mw, "%s", desc->description);
    170 }
    171 
    172 static int
    173 edit_part_type(menudesc *m, void *arg)
    174 {
    175 	struct part_edit_info *info = arg;
    176 	menu_ent *type_opts;
    177 	int type_menu = -1;
    178 	size_t popt_cnt, i;
    179 
    180 	/*
    181 	 * We add one line at the start of the menu, and one at the
    182 	 * bottom, see "set_type_label" above.
    183 	 */
    184 	popt_cnt =  info->parts->pscheme->get_part_types_count() + 2;
    185 	type_opts = calloc(popt_cnt, sizeof(*type_opts));
    186 	for (i = 0; i < popt_cnt; i++) {
    187 		type_opts[i].opt_action = set_part_type;
    188 	}
    189 	type_menu = new_menu(NULL, type_opts, popt_cnt,
    190 		13, 12, 0, 30,
    191 		MC_SUBMENU | MC_SCROLL | MC_NOEXITOPT | MC_NOCLEAR,
    192 		NULL, set_type_label, NULL,
    193 		NULL, NULL);
    194 
    195 	if (type_menu != -1) {
    196 		process_menu(type_menu, arg);
    197 		info->num_changed = true;	/* force reload of menu */
    198 	}
    199 
    200 	free_menu(type_menu);
    201 	free(type_opts);
    202 
    203 	return -1;
    204 }
    205 
    206 static int
    207 edit_part_start(menudesc *m, void *arg)
    208 {
    209 	struct part_edit_info *marg = arg;
    210 	daddr_t max_size;
    211 
    212 	marg->cur.start = getpartoff(marg->parts, marg->cur.start);
    213 	max_size = marg->parts->pscheme->max_free_space_at(marg->parts,
    214 	    marg->cur.start);
    215 	if (marg->cur.size > max_size)
    216 		marg->cur.size = max_size;
    217 
    218 	return 0;
    219 }
    220 
    221 static int
    222 edit_part_size(menudesc *m, void *arg)
    223 {
    224 	struct part_edit_info *marg = arg;
    225 
    226 	marg->cur.size = getpartsize(marg->parts, marg->cur.start,
    227 	    marg->cur.size);
    228 
    229 	return 0;
    230 }
    231 
    232 static int
    233 edit_part_install(menudesc *m, void *arg)
    234 {
    235 	struct part_edit_info *marg = arg;
    236 
    237 	if (pm->ptstart == marg->cur.start) {
    238 		pm->ptstart = 0;
    239 		pm->ptsize = 0;
    240 	} else {
    241 		pm->ptstart = marg->cur.start;
    242 		pm->ptsize = marg->cur.size;
    243 	}
    244 	return 0;
    245 }
    246 
    247 static void
    248 menu_opts_reload(menudesc *m, const struct disk_partitions *parts)
    249 {
    250 	size_t new_num;
    251 
    252 	free(part_menu_opts);
    253 	part_menu_opts = outer_fill_part_menu_opts(parts, &new_num);
    254 	m->opts = part_menu_opts;
    255 	m->numopts = new_num;
    256 }
    257 
    258 static int
    259 delete_part(menudesc *m, void *arg)
    260 {
    261 	struct part_edit_info *marg = arg;
    262 	const char *err_msg = NULL;
    263 
    264 	if (marg->cur_id == NO_PART)
    265 		return 0;
    266 
    267 	if (!marg->parts->pscheme->delete_partition(marg->parts, marg->cur_id,
    268 	    &err_msg))
    269 		err_msg_win(err_msg);
    270 
    271 	marg->num_changed = true;	/* reload list of partitions */
    272 	marg->cancelled = true;		/* do not write back cur data */
    273 
    274 	return 0;
    275 }
    276 
    277 static void draw_outer_ptn_line(menudesc *m, int line, void *arg);
    278 static void draw_outer_ptn_header(menudesc *m, void *arg);
    279 
    280 static int
    281 part_rollback(menudesc *m, void *arg)
    282 {
    283 	struct part_edit_info *marg = arg;
    284 
    285 	marg->cancelled = true;
    286 	return 0;
    287 }
    288 
    289 static menu_ent common_ptn_edit_opts[] = {
    290 #define PTN_OPT_TYPE		0
    291 	{ .opt_action=edit_part_type },
    292 #define PTN_OPT_START		1
    293 	{ .opt_action=edit_part_start },
    294 #define PTN_OPT_SIZE		2
    295 	{ .opt_action=edit_part_size },
    296 #define PTN_OPT_END		3
    297 	{ .opt_flags=OPT_IGNORE }, /* read only "end" */
    298 
    299 	/*
    300 	 * Only the part upto here will be used when adding a new partition
    301 	 */
    302 
    303 #define PTN_OPT_INSTALL		4
    304 	{ .opt_action=edit_part_install },
    305 
    306 #define	PTN_OPTS_COMMON		PTN_OPT_INSTALL	/* cut off from here for add */
    307 };
    308 
    309 static int
    310 edit_custom_opt(menudesc *m, void *arg)
    311 {
    312 	struct part_edit_info *marg = arg;
    313 	size_t attr_no = m->cursel - marg->first_custom_opt;
    314 	char line[STRSIZE];
    315 
    316 	switch (marg->parts->pscheme->custom_attributes[attr_no].type) {
    317 	case pet_bool:
    318 		marg->parts->pscheme->custom_attribute_toggle(
    319 		    marg->parts, marg->cur_id, attr_no);
    320 		break;
    321 	case pet_cardinal:
    322 	case pet_str:
    323 		marg->parts->pscheme->format_custom_attribute(
    324 		    marg->parts, marg->cur_id, attr_no, &marg->cur,
    325 		    line, sizeof(line));
    326 		msg_prompt_win(
    327 		    marg->parts->pscheme->custom_attributes[attr_no].label,
    328 		    -1, 18, 0, 0, line, line, sizeof(line));
    329 		marg->parts->pscheme->custom_attribute_set_str(
    330 		    marg->parts, marg->cur_id, attr_no, line);
    331 		break;
    332 	}
    333 
    334 	return 0;
    335 }
    336 
    337 static menu_ent ptn_edit_opts[] = {
    338 	{ .opt_name=MSG_askunits, .opt_menu=MENU_sizechoice,
    339 	  .opt_flags=OPT_SUB },
    340 
    341 	{ .opt_name=MSG_Delete_partition,
    342 	  .opt_action = delete_part, .opt_flags = OPT_EXIT },
    343 
    344 	{ .opt_name=MSG_cancel,
    345 	  .opt_action = part_rollback, .opt_flags = OPT_EXIT },
    346 };
    347 
    348 static menu_ent ptn_add_opts[] = {
    349 	{ .opt_name=MSG_askunits, .opt_menu=MENU_sizechoice,
    350 	  .opt_flags=OPT_SUB },
    351 
    352 	{ .opt_name=MSG_cancel,
    353 	  .opt_action = part_rollback, .opt_flags = OPT_EXIT },
    354 };
    355 
    356 /*
    357  * Concatenate common_ptn_edit_opts, the partitioning scheme specific
    358  * custom options and the given suffix to a single menu options array.
    359  */
    360 static menu_ent *
    361 fill_part_edit_menu_opts(struct disk_partitions *parts,
    362     bool with_custom_attrs,
    363     const menu_ent *suffix, size_t suffix_count, size_t *res_cnt)
    364 {
    365 	size_t i;
    366 	menu_ent *opts, *p;
    367 	size_t count, hdr_cnt;
    368 
    369 	if (with_custom_attrs) {
    370 		hdr_cnt = __arraycount(common_ptn_edit_opts);
    371 		count = hdr_cnt + parts->pscheme->custom_attribute_count
    372 		    + suffix_count;
    373 	} else {
    374 		hdr_cnt = PTN_OPTS_COMMON;
    375 		count = hdr_cnt + suffix_count;
    376 	}
    377 
    378 	opts = calloc(count, sizeof(*opts));
    379 	if (opts == NULL) {
    380 		*res_cnt = 0;
    381 		return NULL;
    382 	}
    383 
    384 	memcpy(opts, common_ptn_edit_opts,
    385 	    sizeof(*opts)*hdr_cnt);
    386 	p = opts + hdr_cnt;
    387 	if (with_custom_attrs) {
    388 		for (i = 0; i < parts->pscheme->custom_attribute_count; i++) {
    389 			p->opt_action = edit_custom_opt;
    390 			p++;
    391 		}
    392 	}
    393 	memcpy(p, suffix, sizeof(*opts)*suffix_count);
    394 
    395 	*res_cnt = count;
    396 	return opts;
    397 }
    398 
    399 static int
    400 edit_part_entry(menudesc *m, void *arg)
    401 {
    402 	struct outer_parts_data *pdata = arg;
    403 	struct part_edit_info data = { .parts = pdata->av.arg,
    404 	    .cur_id = m->cursel,
    405 	    .first_custom_opt = __arraycount(common_ptn_edit_opts) };
    406 	int ptn_menu;
    407 	const char *err;
    408 	menu_ent *opts;
    409 	size_t num_opts;
    410 
    411 	opts = fill_part_edit_menu_opts(data.parts, true, ptn_edit_opts,
    412 	    __arraycount(ptn_edit_opts), &num_opts);
    413 	if (opts == NULL)
    414 		return 1;
    415 
    416 	if (data.cur_id < data.parts->num_part)
    417 		data.parts->pscheme->get_part_info(data.parts, data.cur_id,
    418 		    &data.cur);
    419 
    420 	ptn_menu = new_menu(NULL, opts, num_opts,
    421 		15, 2, 0, 54,
    422 		MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
    423 		draw_outer_ptn_header, draw_outer_ptn_line, NULL,
    424 		NULL, MSG_Partition_OK);
    425 	if (ptn_menu == -1) {
    426 		free(opts);
    427 		return 1;
    428 	}
    429 
    430 	process_menu(ptn_menu, &data);
    431 	free_menu(ptn_menu);
    432 	free(opts);
    433 
    434 	if (!data.cancelled && data.cur_id < data.parts->num_part)
    435 		if (!data.parts->pscheme->set_part_info(data.parts,
    436 		    data.cur_id, &data.cur, &err))
    437 			err_msg_win(err);
    438 
    439 	if (data.num_changed) {
    440 		menu_opts_reload(m, data.parts);
    441 		m->cursel = data.parts->num_part > 0 ? 0 : 2;
    442 		return -1;
    443 	}
    444 
    445 	return 0;
    446 }
    447 
    448 static int
    449 add_part_clone(menudesc *menu, void *arg)
    450 {
    451 	struct outer_parts_data *pdata = arg;
    452 	struct disk_partitions *parts = pdata->av.arg;
    453 	struct clone_target_menu_data data;
    454 	menu_ent *men;
    455 	int num_men, i;
    456 	struct disk_part_info sinfo, cinfo;
    457 	struct disk_partitions *csrc;
    458 	struct disk_part_free_space space;
    459 	daddr_t offset, align;
    460 	size_t s, clone_cnt;
    461 	part_id cid;
    462 	struct selected_partitions selected;
    463 	struct single_clone_data *new_clones;
    464 
    465 	if (!select_partitions(&selected, parts))
    466 		return 0;
    467 
    468 	new_clones = realloc(pdata->clones,
    469 	    sizeof(*pdata->clones)*(pdata->num_clone_entries+1));
    470 	if (new_clones == NULL)
    471 		return 0;
    472 	pdata->num_clone_entries++;
    473 	pdata->clones = new_clones;
    474 	new_clones += (pdata->num_clone_entries-1);
    475 	memset(new_clones, 0, sizeof *new_clones);
    476 	new_clones->clone_src = selected;
    477 
    478 	memset(&data, 0, sizeof data);
    479 	data.usage.parts = parts;
    480 
    481 	/* if we already have partitions, ask for the target position */
    482 	if (parts->num_part > 0) {
    483 		data.res = -1;
    484 		num_men = parts->num_part+1;
    485 		men = calloc(num_men, sizeof *men);
    486 		if (men == NULL)
    487 			return 0;
    488 		for (i = 0; i < num_men; i++)
    489 			men[i].opt_action = clone_target_select;
    490 		men[num_men-1].opt_name = MSG_clone_target_end;
    491 
    492 		data.usage.menu = new_menu(MSG_clone_target_hdr,
    493 		    men, num_men, 3, 2, 0, 65, MC_SCROLL,
    494 		    NULL, draw_outer_part_line, NULL, NULL, MSG_cancel);
    495 		process_menu(data.usage.menu, &data);
    496 		free_menu(data.usage.menu);
    497 		free(men);
    498 
    499 		if (data.res < 0)
    500 			goto err;
    501 	} else {
    502 		data.res = 0;
    503 	}
    504 
    505 	/* find selected offset from data.res and insert clones there */
    506 	align = parts->pscheme->get_part_alignment(parts);
    507 	offset = -1;
    508 	if (data.res > 0) {
    509 		for (cid = 0; cid < (size_t)data.res; cid++) {
    510 			if (!parts->pscheme->get_part_info(parts, cid, &sinfo))
    511 			continue;
    512 			offset = sinfo.start + sinfo.size;
    513 		}
    514 	} else {
    515 		offset = 0;
    516 	}
    517 
    518 	new_clones->clone_ids = calloc(selected.num_sel,
    519 	    sizeof(*new_clones->clone_ids));
    520 	if (new_clones->clone_ids == NULL)
    521 		goto err;
    522 	for (s = 0; s < selected.num_sel; s++) {
    523 		csrc = selected.selection[s].parts;
    524 		cid = selected.selection[s].id;
    525 		csrc->pscheme->get_part_info(csrc, cid, &sinfo);
    526 		if (!parts->pscheme->adapt_foreign_part_info(
    527 		    parts, &cinfo, csrc->pscheme, &sinfo))
    528 			continue;
    529 		size_t cnt = parts->pscheme->get_free_spaces(
    530 		    parts, &space, 1, cinfo.size-align, align,
    531 		    offset, -1);
    532 		if (cnt == 0)
    533 			continue;
    534 		cinfo.start = space.start;
    535 		cid = parts->pscheme->add_partition(
    536 		    parts, &cinfo, NULL);
    537 		new_clones->clone_ids[s] = cid;
    538 		if (cid == NO_PART)
    539 			continue;
    540 		parts->pscheme->get_part_info(parts, cid, &cinfo);
    541 			clone_cnt++;
    542 		offset = rounddown(cinfo.start+cinfo.size+align, align);
    543 	}
    544 
    545 	/* reload menu and start again */
    546 	menu_opts_reload(menu, parts);
    547 	menu->cursel = parts->num_part+1;
    548 	if (parts->num_part == 0)
    549 		menu->cursel++;
    550 	return -1;
    551 
    552 err:
    553 	free_selected_partitions(&selected);
    554 	return -1;
    555 }
    556 
    557 
    558 static int
    559 add_part_entry(menudesc *m, void *arg)
    560 {
    561 	struct outer_parts_data *pdata = arg;
    562 	struct part_edit_info data = { .parts = pdata->av.arg,
    563 	    .first_custom_opt = PTN_OPTS_COMMON };
    564 	int ptn_menu;
    565 	daddr_t ptn_alignment;
    566 	menu_ent *opts;
    567 	size_t num_opts;
    568 	struct disk_part_free_space space;
    569 	const char *err;
    570 
    571 	opts = fill_part_edit_menu_opts(data.parts, false, ptn_add_opts,
    572 	    __arraycount(ptn_add_opts), &num_opts);
    573 	if (opts == NULL)
    574 		return 1;
    575 
    576 	ptn_alignment = data.parts->pscheme->get_part_alignment(data.parts);
    577 	data.cur_id = NO_PART;
    578 	memset(&data.cur, 0, sizeof(data.cur));
    579 	data.cur.nat_type = data.parts->pscheme->
    580 	    get_generic_part_type(PT_root);
    581 	if (data.parts->pscheme->get_free_spaces(data.parts, &space, 1,
    582 	    max(sizemult, ptn_alignment), ptn_alignment, -1, -1) > 0) {
    583 		data.cur.start = space.start;
    584 		data.cur.size = space.size;
    585 	} else {
    586 		return 0;
    587 	}
    588 
    589 	ptn_menu = new_menu(NULL, opts, num_opts,
    590 		15, -1, 0, 54,
    591 		MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
    592 		draw_outer_ptn_header, draw_outer_ptn_line, NULL,
    593 		NULL, MSG_Partition_OK);
    594 	if (ptn_menu == -1) {
    595 		free(opts);
    596 		return 1;
    597 	}
    598 
    599 	process_menu(ptn_menu, &data);
    600 	free_menu(ptn_menu);
    601 	free(opts);
    602 
    603 	if (!data.cancelled &&
    604 	    data.parts->pscheme->add_partition(data.parts, &data.cur, &err)
    605 	    == NO_PART)
    606 		err_msg_win(err);
    607 
    608 	menu_opts_reload(m, data.parts);
    609 	m->cursel = data.parts->num_part+1;
    610 	if (data.parts->num_part == 0)
    611 		m->cursel++;
    612 	return -1;
    613 }
    614 
    615 static void
    616 draw_outer_ptn_line(menudesc *m, int line, void *arg)
    617 {
    618 	struct part_edit_info *marg = arg;
    619 	char value[STRSIZE];
    620 	static int col_width;
    621 	static const char *yes, *no, *ptn_type, *ptn_start, *ptn_size,
    622 	    *ptn_end, *ptn_install;
    623 
    624 	if (yes == NULL) {
    625 		int i;
    626 
    627 #define CHECK(str)	i = strlen(str); if (i > col_width) col_width = i;
    628 
    629 		col_width = 0;
    630 		yes = msg_string(MSG_Yes); CHECK(yes);
    631 		no = msg_string(MSG_No); CHECK(no);
    632 		ptn_type = msg_string(MSG_ptn_type); CHECK(ptn_type);
    633 		ptn_start = msg_string(MSG_ptn_start); CHECK(ptn_start);
    634 		ptn_size = msg_string(MSG_ptn_size); CHECK(ptn_size);
    635 		ptn_end = msg_string(MSG_ptn_end); CHECK(ptn_end);
    636 		ptn_install = msg_string(MSG_ptn_install); CHECK(ptn_install);
    637 
    638 #undef CHECK
    639 
    640 		for (size_t n = 0;
    641 		    n < marg->parts->pscheme->custom_attribute_count; n++) {
    642 			i = strlen(msg_string(
    643 			    marg->parts->pscheme->custom_attributes[n].label));
    644 			if (i > col_width)
    645 				col_width = i;
    646 		}
    647 		col_width += 3;
    648 	}
    649 
    650 	if (line >= marg->first_custom_opt) {
    651 		size_t attr_no = line-marg->first_custom_opt;
    652 		marg->parts->pscheme->format_custom_attribute(
    653 		    marg->parts, marg->cur_id, attr_no, &marg->cur,
    654 		    value, sizeof(value));
    655 		wprintw(m->mw, "%*s : %s", col_width,
    656 		    msg_string(
    657 		    marg->parts->pscheme->custom_attributes[attr_no].label),
    658 		    value);
    659 		return;
    660 	}
    661 
    662 	switch (line) {
    663 	case PTN_OPT_TYPE:
    664 		wprintw(m->mw, "%*s : %s", col_width, ptn_type,
    665 		    marg->cur.nat_type != NULL
    666 		    ? marg->cur.nat_type->description
    667 		    : "-");
    668 		break;
    669 	case PTN_OPT_START:
    670 		wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width, ptn_start,
    671 		    marg->cur.start / (daddr_t)sizemult, multname);
    672 		break;
    673 	case PTN_OPT_SIZE:
    674 		wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width, ptn_size,
    675 		    marg->cur.size / (daddr_t)sizemult, multname);
    676 		break;
    677 	case PTN_OPT_END:
    678 		wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width, ptn_end,
    679 		    (marg->cur.start + marg->cur.size - 1) / (daddr_t)sizemult,
    680 		    multname);
    681 		break;
    682 	case PTN_OPT_INSTALL:
    683 		wprintw(m->mw, "%*s : %s", col_width, ptn_install,
    684 		    (marg->cur.nat_type->generic_ptype == PT_root &&
    685 		    marg->cur.start == pm->ptstart) ? yes : no);
    686 		break;
    687 	}
    688 
    689 }
    690 
    691 static void
    692 draw_outer_ptn_header(menudesc *m, void *arg)
    693 {
    694 	struct part_edit_info *marg = arg;
    695 	size_t attr_no;
    696 	bool may_change_type;
    697 
    698 #define DISABLE(opt,cond) \
    699 	if (cond) \
    700 		m->opts[opt].opt_flags |= OPT_IGNORE; \
    701 	else \
    702 		m->opts[opt].opt_flags &= ~OPT_IGNORE;
    703 
    704 	/* e.g. MBR extended partitions can only change if empty */
    705 	may_change_type = marg->cur_id == NO_PART
    706 	     || marg->parts->pscheme->part_type_can_change == NULL
    707 	     || marg->parts->pscheme->part_type_can_change(
    708 		    marg->parts, marg->cur_id);
    709 
    710 	DISABLE(PTN_OPT_TYPE, !may_change_type);
    711 	if (!may_change_type && m->cursel == PTN_OPT_TYPE)
    712 		m->cursel++;
    713 	if (marg->cur_id != NO_PART) {
    714 		for (int i = 0; i < m->numopts; i++) {
    715 			if (m->opts[i].opt_action == delete_part) {
    716 				DISABLE(i, !may_change_type);
    717 			}
    718 		}
    719 	}
    720 
    721 	/* Can only install into NetBSD partition */
    722 	if (marg->cur_id != NO_PART) {
    723 		DISABLE(PTN_OPT_INSTALL, marg->cur.nat_type == NULL
    724 		    || marg->cur.nat_type->generic_ptype != PT_root);
    725 	}
    726 
    727 	if (marg->cur_id == NO_PART)
    728 		return;
    729 
    730 	for (attr_no = 0; attr_no <
    731 	    marg->parts->pscheme->custom_attribute_count; attr_no++) {
    732 		bool writable =
    733 		    marg->parts->pscheme->custom_attribute_writable(
    734 			marg->parts, marg->cur_id, attr_no);
    735 		DISABLE(attr_no+marg->first_custom_opt, !writable);
    736 	}
    737 }
    738 
    739 static void
    740 draw_outer_part_line(menudesc *m, int opt, void *arg)
    741 {
    742 	struct outer_parts_data *pdata = arg;
    743 	struct disk_partitions *parts = pdata->av.arg;
    744 	int len;
    745 	part_id pno = opt;
    746 	struct disk_part_info info;
    747 	char buf[SSTRSIZE], *astr, colval[STRSIZE], line[STRSIZE];
    748 	size_t astr_avail, x;
    749 	static char install_flag = 0;
    750 
    751 #define PART_ROW_USED_FMT	"%13" PRIu64 " %13" PRIu64 " %-4s"
    752 
    753 	len = snprintf(0, 0, PART_ROW_USED_FMT, (daddr_t)0, (daddr_t)0, "");
    754 
    755 	if (pno >= parts->num_part ||
    756 	    !parts->pscheme->get_part_info(parts, pno, &info)) {
    757 		wprintw(m->mw, "%*s", len, "");
    758 		// XXX
    759 		return;
    760 	}
    761 
    762 	if (info.start == pm->ptstart &&
    763 	    info.nat_type->generic_ptype == PT_root) {
    764 		if (install_flag == 0)
    765 			install_flag = msg_string(MSG_install_flag)[0];
    766 		astr_avail = sizeof(buf)-1;
    767 		buf[0] = install_flag;
    768 		buf[1] = 0;
    769 		astr = buf+1;
    770 	} else {
    771 		buf[0] = 0;
    772 		astr = buf;
    773 		astr_avail = sizeof(buf);
    774 	}
    775 	if (parts->pscheme->get_part_attr_str != NULL)
    776 		parts->pscheme->get_part_attr_str(parts, pno, astr,
    777 		    astr_avail);
    778 
    779 	daddr_t start = info.start / sizemult;
    780 	daddr_t size = info.size / sizemult;
    781 	wprintw(m->mw, PART_ROW_USED_FMT,
    782 	    start, size, buf);
    783 
    784 	line[0] = 0; x = 0;
    785 	for (size_t col = 0; col < parts->pscheme->edit_columns_count; col++) {
    786 		if (parts->pscheme->format_partition_table_str(parts, pno,
    787 		    col, colval, sizeof(colval)) && colval[0] != 0
    788 		    && x < sizeof(line)-2) {
    789 			for (size_t i = strlen(line); i < x; i++)
    790 				line[i] = ' ';
    791 			line[x] = ' ';
    792 			strlcpy(line+x+1, colval, sizeof(line)-x-1);
    793 		}
    794 		x += parts->pscheme->edit_columns[col].width + 1;
    795 	}
    796 	wprintw(m->mw, "%s", line);
    797 }
    798 
    799 static int
    800 part_edit_abort(menudesc *m, void *arg)
    801 {
    802 	struct outer_parts_data *pdata = arg;
    803 
    804 	pdata->av.rv = -1;
    805 	return 0;
    806 }
    807 
    808 static menu_ent *
    809 outer_fill_part_menu_opts(const struct disk_partitions *parts, size_t *cnt)
    810 {
    811 	menu_ent *opts, *op;
    812 	size_t num_opts;
    813 	size_t i;
    814 	bool may_add;
    815 
    816 	may_add = parts->pscheme->can_add_partition(parts);
    817 	num_opts = 4 + parts->num_part;
    818 	if (parts->num_part == 0)
    819 		num_opts++;
    820 	if (may_add)
    821 		num_opts++;
    822 	opts = calloc(num_opts, sizeof *opts);
    823 	if (opts == NULL) {
    824 		*cnt = 0;
    825 		return NULL;
    826 	}
    827 
    828 	/* add all exisiting partitions */
    829 	for (op = opts, i = 0; i < parts->num_part && i < (num_opts-2);
    830 	    op++, i++) {
    831 		op->opt_flags = OPT_SUB;
    832 		op->opt_action = edit_part_entry;
    833 	}
    834 
    835 	/* if empty, hint that partitions are missing */
    836 	if (parts->num_part == 0) {
    837 		op->opt_name = MSG_nopart;
    838 		op->opt_flags = OPT_IGNORE|OPT_NOSHORT;
    839 		op++;
    840 	}
    841 
    842 	/* separator line between partitions and actions */
    843 	op->opt_name = outer_part_sep_line;
    844 	op->opt_flags = OPT_IGNORE|OPT_NOSHORT;
    845 	op++;
    846 
    847 	/* followed by new partition adder */
    848 	if (may_add) {
    849 		op->opt_name = MSG_addpart;
    850 		op->opt_flags = OPT_SUB;
    851 		op->opt_action = add_part_entry;
    852 		op++;
    853 	}
    854 
    855 	/* and a partition cloner */
    856 	op->opt_name = MSG_clone_from_elsewhere;
    857 	op->opt_action = add_part_clone;
    858 	op++;
    859 
    860 	/* and unit changer */
    861 	op->opt_name = MSG_askunits;
    862 	op->opt_menu = MENU_sizechoice;
    863 	op->opt_flags = OPT_SUB;
    864 	op->opt_action = NULL;
    865 	op++;
    866 
    867 	/* and abort option */
    868 	op->opt_name = MSG_cancel;
    869 	op->opt_flags = OPT_EXIT;
    870 	op->opt_action = part_edit_abort;
    871 	op++;
    872 
    873 	/* counts are consistent? */
    874 	assert((op - opts) >= 0 && (size_t)(op - opts) == num_opts);
    875 
    876 	*cnt = num_opts;
    877 	return opts;
    878 }
    879 
    880 static void
    881 draw_outer_part_header(menudesc *m, void *arg)
    882 {
    883 	struct outer_parts_data *pdata = arg;
    884 	struct disk_partitions *parts = pdata->av.arg;
    885 	char start[SSTRSIZE], size[SSTRSIZE], col[SSTRSIZE],
    886 	    *disk_info, total[SSTRSIZE], avail[SSTRSIZE];
    887 	size_t sep;
    888 	const char *args[3];
    889 
    890 	msg_display_subst(MSG_editparttable, 4,
    891 	    parts->disk,
    892 	    msg_string(parts->pscheme->name),
    893 	    msg_string(parts->pscheme->short_name),
    894 	    parts->pscheme->part_flag_desc ?
    895 		msg_string(parts->pscheme->part_flag_desc)
    896 		: "");
    897 
    898 	snprintf(total, sizeof(total), "%" PRIu64 " %s",
    899 	    parts->disk_size / sizemult, multname);
    900 	snprintf(avail, sizeof(total), "%" PRIu64 " %s",
    901 	    parts->free_space / sizemult, multname);
    902 	args[0] = parts->disk;
    903 	args[1] = total;
    904 	args[2] = avail;
    905 	disk_info = str_arg_subst(msg_string(MSG_part_header), 3, args);
    906 	msg_table_add(disk_info);
    907 	free(disk_info);
    908 
    909 
    910 	strcpy(outer_part_sep_line, "------------- ------------- ----");
    911 	sep = strlen(outer_part_sep_line);
    912 	snprintf(start, sizeof(start), "%s(%s)",
    913 	    msg_string(MSG_part_header_col_start), multname);
    914 	snprintf(size, sizeof(size), "%s(%s)",
    915 	    msg_string(MSG_part_header_col_size), multname);
    916 	snprintf(outer_part_title, sizeof(outer_part_title),
    917 	    "   %13s %13s %-4s", start, size,
    918 	    msg_string(MSG_part_header_col_flag));
    919 
    920 	for (size_t i = 0; i < parts->pscheme->edit_columns_count; i++) {
    921 		char *np = outer_part_sep_line+sep;
    922 		unsigned int w = parts->pscheme->edit_columns[i].width;
    923 		snprintf(col, sizeof(col), " %*s", -w,
    924 		    msg_string(parts->pscheme->edit_columns[i].title));
    925 		strlcat(outer_part_title, col, sizeof(outer_part_title));
    926 		if (sep < sizeof(outer_part_sep_line)-1) {
    927 			*np++ = ' ';
    928 			sep++;
    929 		}
    930 		for (unsigned int p = 0; p < w &&
    931 		    sep < sizeof(outer_part_sep_line)-1; p++)
    932 			*np++ = '-', sep++;
    933 		*np = 0;
    934 	}
    935 
    936 	strlcat(outer_part_title, "\n   ", sizeof(outer_part_title));
    937 	strlcat(outer_part_title, outer_part_sep_line,
    938 	    sizeof(outer_part_title));
    939 
    940 	msg_table_add("\n\n");
    941 }
    942 
    943 /*
    944  * Use the whole disk for NetBSD, but (if any) create required helper
    945  * partitions (usually for booting and stuff).
    946  */
    947 bool
    948 parts_use_wholedisk(struct disk_partitions *parts,
    949     size_t add_ext_parts, const struct disk_part_info *ext_parts)
    950 {
    951 	part_id nbsd;
    952 	struct disk_part_info info;
    953 	struct disk_part_free_space space;
    954 	daddr_t align;
    955 	size_t i;
    956 
    957 	parts->pscheme->delete_all_partitions(parts);
    958 	align = parts->pscheme->get_part_alignment(parts);
    959 
    960 	if (ext_parts != NULL) {
    961 		for (i = 0; i < add_ext_parts; i++) {
    962 			info = ext_parts[i];
    963 			if (parts->pscheme->get_free_spaces(parts, &space,
    964 			    1, info.size, align, -1, -1) != 1)
    965 				return false;
    966 			info.start = space.start;
    967 			if (parts->pscheme->add_partition(parts, &info, NULL)
    968 			    == NO_PART)
    969 				return false;
    970 		}
    971 	}
    972 
    973 	if (parts->pscheme->get_free_spaces(parts, &space, 1, 3*align,
    974 	    align, -1, -1) != 1)
    975 		return false;
    976 
    977 	memset(&info, 0, sizeof(info));
    978 	info.start = space.start;
    979 	info.size = space.size;
    980 	info.nat_type = parts->pscheme->get_generic_part_type(PT_root);
    981 	nbsd = parts->pscheme->add_partition(parts, &info, NULL);
    982 
    983 	if (nbsd == NO_PART)
    984 		return false;
    985 
    986 	if (!parts->pscheme->get_part_info(parts, nbsd, &info))
    987 		return false;
    988 
    989 	if (parts->pscheme->secondary_scheme != NULL) {
    990 		/* force empty secondary partitions */
    991 		parts->pscheme->secondary_partitions(parts, info.start, true);
    992 	}
    993 
    994 	pm->ptstart = info.start;
    995 	pm->ptsize = info.size;
    996 	return true;
    997 }
    998 
    999 static int
   1000 set_keep_existing(menudesc *m, void *arg)
   1001 {
   1002 	((arg_rep_int*)arg)->rv = LY_KEEPEXISTING;
   1003 	return 0;
   1004 }
   1005 
   1006 static int
   1007 set_use_only_part(menudesc *m, void *arg)
   1008 {
   1009 	((arg_rep_int*)arg)->rv = LY_SETSIZES;
   1010 	return 0;
   1011 }
   1012 
   1013 static int
   1014 set_use_entire_disk(menudesc *m, void *arg)
   1015 {
   1016 	((arg_rep_int*)arg)->rv = LY_USEFULL;
   1017 	return 0;
   1018 }
   1019 
   1020 static enum layout_type
   1021 ask_fullpart(struct disk_partitions *parts)
   1022 {
   1023 	arg_rep_int ai;
   1024 	const char *args[2];
   1025 	int menu;
   1026 	size_t num_opts;
   1027 	menu_ent options[3], *opt;
   1028 	daddr_t start, size;
   1029 
   1030 	args[0] = msg_string(pm->parts->pscheme->name);
   1031 	args[1] = msg_string(pm->parts->pscheme->short_name);
   1032 	ai.args.argv = args;
   1033 	ai.args.argc = 2;
   1034 	ai.rv = LY_SETSIZES;
   1035 
   1036 	memset(options, 0, sizeof(options));
   1037 	num_opts = 0;
   1038 	opt = &options[0];
   1039 	if (parts->pscheme->guess_install_target != NULL &&
   1040 	    parts->pscheme->guess_install_target(parts, &start, &size)) {
   1041 		opt->opt_name = MSG_Keep_existing_partitions;
   1042 		opt->opt_flags = OPT_EXIT;
   1043 		opt->opt_action = set_keep_existing;
   1044 		opt++;
   1045 		num_opts++;
   1046 	}
   1047 	opt->opt_name = MSG_Use_only_part_of_the_disk;
   1048 	opt->opt_flags = OPT_EXIT;
   1049 	opt->opt_action = set_use_only_part;
   1050 	opt++;
   1051 	num_opts++;
   1052 
   1053 	opt->opt_name = MSG_Use_the_entire_disk;
   1054 	opt->opt_flags = OPT_EXIT;
   1055 	opt->opt_action = set_use_entire_disk;
   1056 	opt++;
   1057 	num_opts++;
   1058 
   1059 	menu = new_menu(MSG_Select_your_choice, options, num_opts,
   1060 	    -1, -10, 0, 0, MC_NOEXITOPT, NULL, NULL, NULL, NULL, NULL);
   1061 	if (menu != -1) {
   1062 		get_menudesc(menu)->expand_act = expand_all_option_texts;
   1063 		process_menu(menu, &ai);
   1064 		free_menu(menu);
   1065 	}
   1066 
   1067 	return ai.rv;
   1068 }
   1069 
   1070 /*
   1071  * return (see post_edit_verify):
   1072  *  0 -> abort
   1073  *  1 -> re-edit
   1074  *  2 -> continue installation
   1075  */
   1076 static int
   1077 verify_outer_parts(struct disk_partitions *parts, bool quiet)
   1078 {
   1079 	part_id i;
   1080 	int num_bsdparts;
   1081 	daddr_t first_bsdstart, first_bsdsize, inst_start, inst_size;
   1082 
   1083 	first_bsdstart = first_bsdsize = 0;
   1084 	inst_start = inst_size = 0;
   1085 	num_bsdparts = 0;
   1086 	for (i = 0; i < parts->num_part; i++) {
   1087 		struct disk_part_info info;
   1088 		if (!parts->pscheme->get_part_info(parts, i, &info))
   1089 			continue;
   1090 		if (!(info.flags & PTI_SEC_CONTAINER))
   1091 			continue;
   1092 		if (info.nat_type->generic_ptype != PT_root)
   1093 			continue;
   1094 		num_bsdparts++;
   1095 
   1096 		if (first_bsdstart == 0) {
   1097 			first_bsdstart = info.start;
   1098 			first_bsdsize = info.size;
   1099 		}
   1100 		if (inst_start == 0 && info.start == pm->ptstart) {
   1101 			inst_start = info.start;
   1102 			inst_size = info.size;
   1103 		}
   1104 	}
   1105 
   1106 	if (num_bsdparts == 0 ||
   1107 	    (num_bsdparts > 1 && inst_start == 0)) {
   1108 		if (quiet && num_bsdparts == 0)
   1109 			return 0;
   1110 		if (quiet && parts->pscheme->guess_install_target &&
   1111 		    parts->pscheme->guess_install_target(parts,
   1112 		    &inst_start, &inst_size)) {
   1113 			pm->ptstart = inst_start;
   1114 			pm->ptsize = inst_size;
   1115 		} else {
   1116 			if (num_bsdparts == 0)
   1117 				msg_display_subst(MSG_nobsdpart, 2,
   1118 				    msg_string(parts->pscheme->name),
   1119 				    msg_string(parts->pscheme->short_name));
   1120 			else
   1121 				msg_display_subst(MSG_multbsdpart, 2,
   1122 				    msg_string(parts->pscheme->name),
   1123 				    msg_string(parts->pscheme->short_name));
   1124 
   1125 			return ask_reedit(parts);
   1126 		}
   1127 	}
   1128 
   1129 	if (pm->ptstart == 0) {
   1130 		if (inst_start > 0) {
   1131 			pm->ptstart = inst_start;
   1132 			pm->ptsize = inst_size;
   1133 		} else if (first_bsdstart > 0) {
   1134 			pm->ptstart = first_bsdstart;
   1135 			pm->ptsize = first_bsdsize;
   1136 		} else if (parts->pscheme->guess_install_target &&
   1137 			   parts->pscheme->guess_install_target(
   1138 			   parts, &inst_start, &inst_size)) {
   1139 			pm->ptstart = inst_start;
   1140 			pm->ptsize = inst_size;
   1141 		}
   1142 	}
   1143 
   1144 	/*
   1145 	 * post_edit_verify returns:
   1146 	 *  0 -> abort
   1147 	 *  1 -> re-edit
   1148 	 *  2 -> continue installation
   1149 	 */
   1150 	if (parts->pscheme->post_edit_verify)
   1151 		return parts->pscheme->post_edit_verify(parts, quiet);
   1152 
   1153 	return 2;
   1154 }
   1155 
   1156 static bool
   1157 ask_outer_partsizes(struct disk_partitions *parts)
   1158 {
   1159 	int j;
   1160 	int part_menu;
   1161 	size_t num_opts, i, ci;
   1162 	struct outer_parts_data data;
   1163 
   1164 	part_menu_opts = outer_fill_part_menu_opts(parts, &num_opts);
   1165 	part_menu = new_menu(outer_part_title, part_menu_opts, num_opts,
   1166 			0, -1, 15, 70,
   1167 			MC_NOBOX|MC_ALWAYS_SCROLL|MC_NOCLEAR|MC_CONTINUOUS,
   1168 			draw_outer_part_header, draw_outer_part_line, NULL,
   1169 			NULL, MSG_Partition_table_ok);
   1170 	if (part_menu == -1) {
   1171 		free(part_menu_opts);
   1172 		return false;
   1173 	}
   1174 
   1175 	/* Default to MB, and use bios geometry for cylinder size */
   1176 	set_default_sizemult(MEG/512);
   1177 	if (pm->current_cylsize == 0)
   1178 		pm->current_cylsize = 16065;	/* noone cares nowadays */
   1179 	pm->ptstart = 0;
   1180 	pm->ptsize = 0;
   1181 	memset(&data, 0, sizeof data);
   1182 	data.av.arg = parts;
   1183 
   1184 	for (;;) {
   1185 		data.av.rv = 0;
   1186 		process_menu(part_menu, &data);
   1187 		if (data.av.rv < 0)
   1188 			break;
   1189 
   1190 		j = verify_outer_parts(parts, false);
   1191 		if (j == 0) {
   1192 			data.av.rv = -1;
   1193 			return false;
   1194 		} else if (j == 1) {
   1195 			continue;
   1196 		}
   1197 		break;
   1198 	}
   1199 
   1200 	/* handle cloned partitions content copies now */
   1201 	for (i = 0; i < data.num_clone_entries; i++) {
   1202 		for (ci = 0; ci < data.clones[i].clone_src.num_sel; ci++) {
   1203 			if (data.clones[i].clone_src.with_data)
   1204 				clone_partition_data(parts,
   1205 				    data.clones[i].clone_ids[ci],
   1206 				    data.clones[i].clone_src.selection[ci].
   1207 				    parts,
   1208 				    data.clones[i].clone_src.selection[ci].id);
   1209 		}
   1210 	}
   1211 
   1212 	/* free clone data */
   1213 	if (data.clones) {
   1214 		for (i = 0; i < data.num_clone_entries; i++)
   1215 			free_selected_partitions(&data.clones[i].clone_src);
   1216 		free(data.clones);
   1217 	}
   1218 
   1219 	free_menu(part_menu);
   1220 	free(part_menu_opts);
   1221 
   1222 	return data.av.rv == 0;
   1223 }
   1224 
   1225 bool
   1226 edit_outer_parts(struct disk_partitions *parts)
   1227 {
   1228 	part_id i;
   1229 	enum layout_type layout;
   1230 	int num_foreign_parts;
   1231 
   1232 	/* If targeting a wedge, do not ask for further partitioning */
   1233 	if (pm && (pm->no_part || pm->no_mbr))
   1234 		return true;
   1235 
   1236 	/* Make sure parts has been properly initialized */
   1237 	assert(parts && parts->pscheme);
   1238 
   1239 	if (parts->pscheme->secondary_scheme == NULL)
   1240 		return true;	/* no outer parts */
   1241 
   1242 	if (partman_go) {
   1243 		layout = LY_SETSIZES;
   1244 	} else {
   1245 		/* Ask full/part */
   1246 		const struct disk_partitioning_scheme *sec =
   1247 		    parts->pscheme->secondary_scheme;
   1248 
   1249 		uint64_t m_size =
   1250 		    DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE + XNEEDMB;
   1251 		char min_size[5], build_size[5];
   1252 		const char
   1253 		    *prim_name = msg_string(parts->pscheme->name),
   1254 		    *prim_short = msg_string(parts->pscheme->short_name),
   1255 		    *sec_name = msg_string(sec->name),
   1256 		    *sec_short = msg_string(sec->short_name);
   1257 
   1258 		humanize_number(min_size, sizeof(min_size),
   1259 		    m_size * MEG,
   1260 		    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
   1261 		humanize_number(build_size, sizeof(build_size),
   1262 		     SYSTEM_BUILD_SIZE * MEG, "", HN_AUTOSCALE,
   1263 		     HN_B | HN_NOSPACE | HN_DECIMAL);
   1264 
   1265 		msg_display_subst(MSG_fullpart, 7,
   1266 		    pm->diskdev,
   1267 		    prim_name, sec_name,
   1268 		    prim_short, sec_short,
   1269 		    min_size, build_size);
   1270 		msg_display_add("\n\n");
   1271 
   1272 		layout = ask_fullpart(parts);
   1273 	}
   1274 
   1275 	if (layout == LY_USEFULL) {
   1276 		struct disk_part_info info;
   1277 
   1278 		/* Count nonempty, non-BSD partitions. */
   1279 		num_foreign_parts = 0;
   1280 		for (i = 0; i < parts->num_part; i++) {
   1281 			if (!parts->pscheme->get_part_info(parts, i, &info))
   1282 				continue;
   1283 			if (info.size == 0)
   1284 				continue;
   1285 			if (info.flags & (PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
   1286 				continue;
   1287 			if (info.nat_type != NULL
   1288 			    && info.nat_type->generic_ptype != PT_root
   1289 			    && info.nat_type->generic_ptype != PT_swap)
   1290 				num_foreign_parts++;
   1291 		}
   1292 
   1293 		/* Ask if we really want to blow away non-NetBSD stuff */
   1294 		if (num_foreign_parts > 0) {
   1295 			msg_display(MSG_ovrwrite);
   1296 			if (!ask_noyes(NULL)) {
   1297 				if (logfp)
   1298 					(void)fprintf(logfp,
   1299 					    "User answered no to destroy "
   1300 					    "other data, aborting.\n");
   1301 				return false;
   1302 			}
   1303 		}
   1304 		if (!md_parts_use_wholedisk(parts)) {
   1305 			hit_enter_to_continue(MSG_No_free_space, NULL);
   1306 			return false;
   1307 		}
   1308 		if (parts->pscheme->post_edit_verify) {
   1309 			return
   1310 			    parts->pscheme->post_edit_verify(parts, true) == 2;
   1311 		}
   1312 		return true;
   1313 	} else if (layout == LY_SETSIZES) {
   1314 		return ask_outer_partsizes(parts);
   1315 	} else {
   1316 		return verify_outer_parts(parts, true) == 2;
   1317 	}
   1318 }
   1319 
   1320 static int
   1321 set_part_scheme(menudesc *m, void *arg)
   1322 {
   1323 	size_t *res = arg;
   1324 
   1325 	*res = (size_t)m->cursel;
   1326 	return 1;
   1327 }
   1328 
   1329 const struct disk_partitioning_scheme *
   1330 select_part_scheme(
   1331 	struct pm_devs *dev,
   1332 	const struct disk_partitioning_scheme *skip,
   1333 	bool bootable,
   1334 	const char *hdr)
   1335 {
   1336 	int ps_menu = -1;
   1337 	menu_ent *opt;
   1338 	char **str, *ms = NULL;
   1339 	const struct disk_partitioning_scheme **options, *res;
   1340 	const char *title;
   1341 	size_t ndx, selected = ~0U, used;
   1342 	const struct disk_partitioning_scheme *p;
   1343 	bool showing_limit = false;
   1344 
   1345 	if (hdr == NULL)
   1346 		hdr = MSG_select_part_scheme;
   1347 
   1348 	opt = calloc(num_available_part_schemes, sizeof *opt);
   1349 	if (!opt)
   1350 		return NULL;
   1351 	str = calloc(num_available_part_schemes, sizeof *str);
   1352 	if (!str) {
   1353 		free(opt);
   1354 		return NULL;
   1355 	}
   1356 	options = calloc(num_available_part_schemes, sizeof *options);
   1357 	if (!options) {
   1358 		free(str);
   1359 		free(opt);
   1360 		return NULL;
   1361 	}
   1362 
   1363 	for (used = 0, ndx = 0; ndx < num_available_part_schemes; ndx++) {
   1364 		p = available_part_schemes[ndx];
   1365 		if (skip != NULL && p == skip)
   1366 			continue;
   1367 		if (bootable && p->have_boot_support != NULL &&
   1368 		    !p->have_boot_support(dev->diskdev))
   1369 			continue;
   1370 #ifdef HAVE_MBR
   1371 		if (dev->no_mbr && p->name == MSG_parttype_mbr)
   1372 			continue;
   1373 #endif
   1374 		if (p->size_limit && dev->dlsize > p->size_limit) {
   1375 			char buf[255], hum_lim[5];
   1376 
   1377 			humanize_number(hum_lim, sizeof(hum_lim),
   1378 			    (uint64_t)p->size_limit*512UL,
   1379 			    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
   1380 			sprintf(buf, "%s [%s %s]", msg_string(p->name),
   1381 			    msg_string(MSG_size_limit), hum_lim);
   1382 			str[used] = strdup(buf);
   1383 			showing_limit = true;
   1384 		} else {
   1385 			str[used] = strdup(msg_string(p->name));
   1386 		}
   1387 		if (!str[used])
   1388 			goto out;
   1389 
   1390 		opt[used].opt_name = str[used];
   1391 		opt[used].opt_action = set_part_scheme;
   1392 		options[used] = p;
   1393 		used++;
   1394 	}
   1395 
   1396 	/* do not bother to ask if there are no options */
   1397 	if (used <= 1) {
   1398 		selected = (used == 1) ? 0 : ~0U;
   1399 		goto out;
   1400 	}
   1401 
   1402 	if (showing_limit) {
   1403 		char hum_lim[5], *tmp;
   1404 		size_t total;
   1405 
   1406 		const char *p1 = msg_string(hdr);
   1407 		const char *p2 = msg_string(MSG_select_part_limit);
   1408 
   1409 		humanize_number(hum_lim, sizeof(hum_lim),
   1410 		    (uint64_t)dev->dlsize*512, "",
   1411 		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
   1412 
   1413 		const char *args[] = { dev->diskdev, hum_lim };
   1414 		char *p3 = str_arg_subst(msg_string(MSG_part_limit_disksize),
   1415 		    __arraycount(args), args);
   1416 
   1417 		total = strlen(p1) + strlen(p2) + strlen(p3)
   1418 		    + sizeof(hum_lim) + 5;
   1419 		ms = tmp = malloc(total);
   1420 		title = tmp;
   1421 		strcpy(tmp, p1); tmp += strlen(p1);
   1422 		*tmp++ = '\n'; *tmp++ = '\n';
   1423 		strcpy(tmp, p2); tmp += strlen(p2);
   1424 		*tmp++ = '\n'; *tmp++ = '\n';
   1425 		strcpy(tmp, p3);
   1426 		free(p3);
   1427 		assert(strlen(ms) < total);
   1428 	} else {
   1429 		title = msg_string(hdr);
   1430 	}
   1431 	ps_menu = new_menu(title, opt, used,
   1432 	    5, 5, 0, 0, 0, NULL, NULL, NULL, NULL, MSG_exit_menu_generic);
   1433 	if (ps_menu != -1)
   1434 		process_menu(ps_menu, &selected);
   1435 out:
   1436 	res = selected >= used ? NULL : options[selected];
   1437 	for (ndx = 0; ndx < used; ndx++)
   1438 		free(str[ndx]);
   1439 	if (showing_limit && ms)
   1440 		free(ms);
   1441 	free(str);
   1442 	free(opt);
   1443 	free(options);
   1444 	if (ps_menu != -1)
   1445 		free_menu(ps_menu);
   1446 
   1447 	return res;
   1448 }
   1449