Home | History | Annotate | Line # | Download | only in disklabel
      1 /*	$NetBSD: interact.c,v 1.40 2021/11/03 14:25:39 nia Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #if !defined(NO_INTERACT)
     28 
     29 #if HAVE_NBTOOL_CONFIG_H
     30 #include "nbtool_config.h"
     31 #endif
     32 
     33 #include <sys/cdefs.h>
     34 #ifndef lint
     35 __RCSID("$NetBSD: interact.c,v 1.40 2021/11/03 14:25:39 nia Exp $");
     36 #endif /* lint */
     37 
     38 #include <sys/param.h>
     39 #define FSTYPENAMES
     40 #define DKTYPENAMES
     41 
     42 #include <err.h>
     43 #include <stdio.h>
     44 #include <string.h>
     45 #include <stdarg.h>
     46 #include <stdlib.h>
     47 #include <sys/ioctl.h>
     48 
     49 #if HAVE_NBTOOL_CONFIG_H
     50 #define	getmaxpartitions()	maxpartitions
     51 #include <nbinclude/sys/disklabel.h>
     52 #else
     53 #include <util.h>
     54 #include <sys/disklabel.h>
     55 #endif /* HAVE_NBTOOL_CONFIG_H */
     56 
     57 #include "extern.h"
     58 
     59 static void	cmd_help(struct disklabel *, char *, int);
     60 static void	cmd_adjust(struct disklabel *, char *, int);
     61 static void	cmd_chain(struct disklabel *, char *, int);
     62 static void	cmd_print(struct disklabel *, char *, int);
     63 static void	cmd_printall(struct disklabel *, char *, int);
     64 static void	cmd_info(struct disklabel *, char *, int);
     65 static void	cmd_part(struct disklabel *, char *, int);
     66 static void	cmd_label(struct disklabel *, char *, int);
     67 static void	cmd_round(struct disklabel *, char *, int);
     68 static void	cmd_name(struct disklabel *, char *, int);
     69 static void	cmd_listfstypes(struct disklabel *, char *, int);
     70 static int	runcmd(struct disklabel *, char *, int);
     71 static int	getinput(char *, const char *, ...) __printflike(2, 3);
     72 static int	alphacmp(const void *, const void *);
     73 static void	defnum(struct disklabel *, char *, uint32_t);
     74 static void	dumpnames(const char *, const char * const *, size_t);
     75 static intmax_t	getnum(struct disklabel *, char *, intmax_t);
     76 
     77 static int rounding = 0;	/* sector rounding */
     78 static int chaining = 0;	/* make partitions contiguous */
     79 
     80 static struct cmds {
     81 	const char *name;
     82 	void (*func)(struct disklabel *, char *, int);
     83 	const char *help;
     84 } cmds[] = {
     85 	{ "?",	cmd_help,	"print this menu" },
     86 	{ "A",	cmd_adjust,	"adjust the label size to the max disk size" },
     87 	{ "C",	cmd_chain,	"make partitions contiguous" },
     88 	{ "E",	cmd_printall,	"print disk label and current partition table"},
     89 	{ "I",	cmd_info,	"change label information" },
     90 	{ "L",	cmd_listfstypes,"list all known file system types" },
     91 	{ "N",	cmd_name,	"name the label" },
     92 	{ "P",	cmd_print,	"print current partition table" },
     93 	{ "Q",	NULL,		"quit" },
     94 	{ "R",	cmd_round,	"rounding (c)ylinders (s)ectors" },
     95 	{ "W",	cmd_label,	"write the current partition table" },
     96 	{ NULL, NULL,		NULL }
     97 };
     98 
     99 
    100 static void
    101 cmd_help(struct disklabel *lp, char *s, int fd)
    102 {
    103 	struct cmds *cmd;
    104 
    105 	for (cmd = cmds; cmd->name != NULL; cmd++)
    106 		printf("%s\t%s\n", cmd->name, cmd->help);
    107 	printf("[a-%c]\tdefine named partition\n",
    108 	    'a' + getmaxpartitions() - 1);
    109 }
    110 
    111 
    112 static void
    113 cmd_adjust(struct disklabel *lp, char *s, int fd)
    114 {
    115 	struct disklabel dl;
    116 
    117 	if (dk_ioctl(fd, DIOCGDEFLABEL, &dl) == -1) {
    118 		warn("Cannot get default label");
    119 		return;
    120 	}
    121 
    122 	if (dl.d_secperunit != lp->d_secperunit) {
    123 		char line[BUFSIZ];
    124 		int i = getinput(line, "Adjust disklabel sector from %" PRIu32
    125 		    " to %" PRIu32 " [n]? ", lp->d_secperunit, dl.d_secperunit);
    126 		if (i <= 0)
    127 			return;
    128 		if (line[0] != 'Y' && line[0] != 'y')
    129 			return;
    130 		lp->d_secperunit = dl.d_secperunit;
    131 		return;
    132 	}
    133 
    134 	printf("Already at %" PRIu32 " sectors\n", dl.d_secperunit);
    135 	return;
    136 }
    137 
    138 static void
    139 cmd_chain(struct disklabel *lp, char *s, int fd)
    140 {
    141 	int	i;
    142 	char	line[BUFSIZ];
    143 
    144 	i = getinput(line, "Automatically adjust partitions [%s]? ",
    145 	    chaining ? "yes" : "no");
    146 	if (i <= 0)
    147 		return;
    148 
    149 	switch (line[0]) {
    150 	case 'y':
    151 		chaining = 1;
    152 		return;
    153 	case 'n':
    154 		chaining = 0;
    155 		return;
    156 	default:
    157 		printf("Invalid answer\n");
    158 		return;
    159 	}
    160 }
    161 
    162 
    163 static void
    164 cmd_printall(struct disklabel *lp, char *s, int fd)
    165 {
    166 
    167 	showinfo(stdout, lp, specname);
    168 	showpartitions(stdout, lp, Cflag);
    169 }
    170 
    171 
    172 static void
    173 cmd_print(struct disklabel *lp, char *s, int fd)
    174 {
    175 
    176 	showpartitions(stdout, lp, Cflag);
    177 }
    178 
    179 
    180 static void
    181 cmd_info(struct disklabel *lp, char *s, int fd)
    182 {
    183 	char	line[BUFSIZ];
    184 	int	v, i;
    185 	u_int32_t u;
    186 
    187 	printf("# Current values:\n");
    188 	showinfo(stdout, lp, specname);
    189 
    190 	/* d_type */
    191 	for (;;) {
    192 		i = lp->d_type;
    193 		if (i < 0 || i >= DKMAXTYPES)
    194 			i = 0;
    195 		i = getinput(line, "Disk type [%s]: ", dktypenames[i]);
    196 		if (i == -1)
    197 			return;
    198 		else if (i == 0)
    199 			break;
    200 		if (!strcmp(line, "?")) {
    201 			dumpnames("Supported disk types", dktypenames,
    202 			    DKMAXTYPES);
    203 			continue;
    204 		}
    205 		for (i = 0; i < DKMAXTYPES; i++) {
    206 			if (!strcasecmp(dktypenames[i], line)) {
    207 				lp->d_type = i;
    208 				goto done_typename;
    209 			}
    210 		}
    211 		v = atoi(line);
    212 		if ((unsigned)v >= DKMAXTYPES) {
    213 			warnx("Unknown disk type: %s", line);
    214 			continue;
    215 		}
    216 		lp->d_type = v;
    217  done_typename:
    218 		break;
    219 	}
    220 
    221 	/* d_typename */
    222 	i = getinput(line, "Disk name [%.*s]: ",
    223 	    (int) sizeof(lp->d_typename), lp->d_typename);
    224 	if (i == -1)
    225 		return;
    226 	else if (i == 1)
    227 		(void) strncpy(lp->d_typename, line, sizeof(lp->d_typename));
    228 
    229 	/* d_packname */
    230 	cmd_name(lp, s, fd);
    231 
    232 	/* d_npartitions */
    233 	for (;;) {
    234 		i = getinput(line, "Number of partitions [%" PRIu16 "]: ",
    235 		    lp->d_npartitions);
    236 		if (i == -1)
    237 			return;
    238 		else if (i == 0)
    239 			break;
    240 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    241 			printf("Invalid number of partitions `%s'\n", line);
    242 			continue;
    243 		}
    244 		lp->d_npartitions = u;
    245 		break;
    246 	}
    247 
    248 	/* d_secsize */
    249 	for (;;) {
    250 		i = getinput(line, "Sector size (bytes) [%" PRIu32 "]: ",
    251 		    lp->d_secsize);
    252 		if (i == -1)
    253 			return;
    254 		else if (i == 0)
    255 			break;
    256 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    257 			printf("Invalid sector size `%s'\n", line);
    258 			continue;
    259 		}
    260 		lp->d_secsize = u;
    261 		break;
    262 	}
    263 
    264 	/* d_nsectors */
    265 	for (;;) {
    266 		i = getinput(line, "Number of sectors per track [%" PRIu32
    267 		    "]: ", lp->d_nsectors);
    268 		if (i == -1)
    269 			return;
    270 		else if (i == 0)
    271 			break;
    272 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    273 			printf("Invalid number of sectors `%s'\n", line);
    274 			continue;
    275 		}
    276 		lp->d_nsectors = u;
    277 		break;
    278 	}
    279 
    280 	/* d_ntracks */
    281 	for (;;) {
    282 		i = getinput(line, "Number of tracks per cylinder [%" PRIu32
    283 		    "]: ", lp->d_ntracks);
    284 		if (i == -1)
    285 			return;
    286 		else if (i == 0)
    287 			break;
    288 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    289 			printf("Invalid number of tracks `%s'\n", line);
    290 			continue;
    291 		}
    292 		lp->d_ntracks = u;
    293 		break;
    294 	}
    295 
    296 	/* d_secpercyl */
    297 	for (;;) {
    298 		i = getinput(line, "Number of sectors/cylinder [%" PRIu32 "]: ",
    299 		    lp->d_secpercyl);
    300 		if (i == -1)
    301 			return;
    302 		else if (i == 0)
    303 			break;
    304 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    305 			printf("Invalid number of sector/cylinder `%s'\n",
    306 			    line);
    307 			continue;
    308 		}
    309 		lp->d_secpercyl = u;
    310 		break;
    311 	}
    312 
    313 	/* d_ncylinders */
    314 	for (;;) {
    315 		i = getinput(line, "Total number of cylinders [%" PRIu32 "]: ",
    316 		    lp->d_ncylinders);
    317 		if (i == -1)
    318 			return;
    319 		else if (i == 0)
    320 			break;
    321 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    322 			printf("Invalid sector size `%s'\n", line);
    323 			continue;
    324 		}
    325 		lp->d_ncylinders = u;
    326 		break;
    327 	}
    328 
    329 	/* d_secperunit */
    330 	for (;;) {
    331 		i = getinput(line, "Total number of sectors [%" PRIu32 "]: ",
    332 		    lp->d_secperunit);
    333 		if (i == -1)
    334 			return;
    335 		else if (i == 0)
    336 			break;
    337 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    338 			printf("Invalid number of sectors `%s'\n", line);
    339 			continue;
    340 		}
    341 		lp->d_secperunit = u;
    342 		break;
    343 	}
    344 
    345 	/* d_rpm */
    346 
    347 	/* d_interleave */
    348 	for (;;) {
    349 		i = getinput(line, "Hardware sectors interleave [%" PRIu16
    350 		    "]: ", lp->d_interleave);
    351 		if (i == -1)
    352 			return;
    353 		else if (i == 0)
    354 			break;
    355 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    356 			printf("Invalid sector interleave `%s'\n", line);
    357 			continue;
    358 		}
    359 		lp->d_interleave = u;
    360 		break;
    361 	}
    362 
    363 	/* d_trackskew */
    364 	for (;;) {
    365 		i = getinput(line, "Sector 0 skew, per track [%" PRIu16 "]: ",
    366 		    lp->d_trackskew);
    367 		if (i == -1)
    368 			return;
    369 		else if (i == 0)
    370 			break;
    371 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    372 			printf("Invalid track sector skew `%s'\n", line);
    373 			continue;
    374 		}
    375 		lp->d_trackskew = u;
    376 		break;
    377 	}
    378 
    379 	/* d_cylskew */
    380 	for (;;) {
    381 		i = getinput(line, "Sector 0 skew, per cylinder [%" PRIu16
    382 		    "]: ", lp->d_cylskew);
    383 		if (i == -1)
    384 			return;
    385 		else if (i == 0)
    386 			break;
    387 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    388 			printf("Invalid cylinder sector `%s'\n", line);
    389 			continue;
    390 		}
    391 		lp->d_cylskew = u;
    392 		break;
    393 	}
    394 
    395 	/* d_headswitch */
    396 	for (;;) {
    397 		i = getinput(line, "Head switch time (usec) [%" PRIu32 "]: ",
    398 		    lp->d_headswitch);
    399 		if (i == -1)
    400 			return;
    401 		else if (i == 0)
    402 			break;
    403 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    404 			printf("Invalid head switch time `%s'\n", line);
    405 			continue;
    406 		}
    407 		lp->d_headswitch = u;
    408 		break;
    409 	}
    410 
    411 	/* d_trkseek */
    412 	for (;;) {
    413 		i = getinput(line, "Track seek time (usec) [%" PRIu32 "]:",
    414 		    lp->d_trkseek);
    415 		if (i == -1)
    416 			return;
    417 		else if (i == 0)
    418 			break;
    419 		if (sscanf(line, "%" SCNu32, &u) != 1) {
    420 			printf("Invalid track seek time `%s'\n", line);
    421 			continue;
    422 		}
    423 		lp->d_trkseek = u;
    424 		break;
    425 	}
    426 }
    427 
    428 
    429 static void
    430 cmd_name(struct disklabel *lp, char *s, int fd)
    431 {
    432 	char	line[BUFSIZ];
    433 	int	i;
    434 
    435 	i = getinput(line, "Label name [%.*s]: ",
    436 	    (int) sizeof(lp->d_packname), lp->d_packname);
    437 	if (i <= 0)
    438 		return;
    439 	(void) strncpy(lp->d_packname, line, sizeof(lp->d_packname));
    440 }
    441 
    442 
    443 static void
    444 cmd_round(struct disklabel *lp, char *s, int fd)
    445 {
    446 	int	i;
    447 	char	line[BUFSIZ];
    448 
    449 	i = getinput(line, "Rounding [%s]: ", rounding ? "cylinders" :
    450 	    "sectors");
    451 	if (i <= 0)
    452 		return;
    453 
    454 	switch (line[0]) {
    455 	case 'c':
    456 	case 'C':
    457 		rounding = 1;
    458 		return;
    459 	case 's':
    460 	case 'S':
    461 		rounding = 0;
    462 		return;
    463 	default:
    464 		printf("Rounding can be (c)ylinders or (s)ectors\n");
    465 		return;
    466 	}
    467 }
    468 
    469 
    470 static void
    471 cmd_part(struct disklabel *lp, char *s, int fd)
    472 {
    473 	int	i;
    474 	intmax_t im;
    475 	char	line[BUFSIZ];
    476 	char	def[BUFSIZ];
    477 	int	part;
    478 	struct partition *p, ps;
    479 
    480 	part = s[0] - 'a';
    481 	p = &lp->d_partitions[part];
    482 	if (part >= lp->d_npartitions)
    483 		lp->d_npartitions = part + 1;
    484 
    485 	(void)memcpy(&ps, p, sizeof(ps));
    486 
    487 	for (;;) {
    488 		i = p->p_fstype;
    489 		if (i < 0 || i >= FSMAXTYPES)
    490 			i = 0;
    491 		i = getinput(line, "Filesystem type [%s]: ", fstypenames[i]);
    492 		if (i == -1)
    493 			return;
    494 		else if (i == 0)
    495 			break;
    496 		if (!strcmp(line, "?")) {
    497 			dumpnames("Supported file system types",
    498 			    fstypenames, FSMAXTYPES);
    499 			continue;
    500 		}
    501 		for (i = 0; i < FSMAXTYPES; i++)
    502 			if (!strcasecmp(line, fstypenames[i])) {
    503 				p->p_fstype = i;
    504 				goto done_typename;
    505 			}
    506 		printf("Invalid file system typename `%s'\n", line);
    507 		continue;
    508  done_typename:
    509 		break;
    510 	}
    511 	for (;;) {
    512 		defnum(lp, def, p->p_offset);
    513 		i = getinput(line, "Start offset ('x' to start after partition"
    514 		" 'x') [%s]: ", def);
    515 		if (i == -1)
    516 			return;
    517 		else if (i == 0)
    518 			break;
    519 		if (line[1] == '\0' &&
    520 	    		line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
    521 			struct partition *cp = lp->d_partitions;
    522 
    523 			if ((cp[line[0] - 'a'].p_offset +
    524 			    cp[line[0] - 'a'].p_size) >= lp->d_secperunit) {
    525 				printf("Bad offset `%s'\n", line);
    526 				continue;
    527 			} else {
    528 				p->p_offset = cp[line[0] - 'a'].p_offset +
    529 				    cp[line[0] - 'a'].p_size;
    530 			}
    531 		} else {
    532 			if ((im = getnum(lp, line, 0)) == -1 || im < 0) {
    533 				printf("Bad offset `%s'\n", line);
    534 				continue;
    535 			} else if (im > 0xffffffffLL ||
    536 				   (uint32_t)im > lp->d_secperunit) {
    537 				printf("Offset `%s' out of range\n", line);
    538 				continue;
    539 			}
    540 			p->p_offset = (uint32_t)im;
    541 		}
    542 		break;
    543 	}
    544 	for (;;) {
    545 		defnum(lp, def, p->p_size);
    546 		i = getinput(line, "Partition size ('$' for all remaining) "
    547 		    "[%s]: ", def);
    548 		if (i == -1)
    549 			return;
    550 		else if (i == 0)
    551 			break;
    552 		if ((im = getnum(lp, line, lp->d_secperunit - p->p_offset))
    553 		    == -1) {
    554 			printf("Bad size `%s'\n", line);
    555 			continue;
    556 		} else if (im > 0xffffffffLL ||
    557 			   (im + p->p_offset) > lp->d_secperunit) {
    558 			printf("Size `%s' out of range\n", line);
    559 			continue;
    560 		}
    561 		p->p_size = im;
    562 		break;
    563 	}
    564 
    565 	if (memcmp(&ps, p, sizeof(ps)))
    566 		showpartition(stdout, lp, part, Cflag);
    567 	if (chaining) {
    568 		int offs = -1;
    569 		struct partition *cp = lp->d_partitions;
    570 		for (i = 0; i < lp->d_npartitions; i++) {
    571 			if (cp[i].p_fstype != FS_UNUSED) {
    572 				if (offs != -1 && cp[i].p_offset != (uint32_t)offs) {
    573 					cp[i].p_offset = offs;
    574 					showpartition(stdout, lp, i, Cflag);
    575 					}
    576 				offs = cp[i].p_offset + cp[i].p_size;
    577 			}
    578 		}
    579 	}
    580 }
    581 
    582 
    583 static void
    584 cmd_label(struct disklabel *lp, char *s, int fd)
    585 {
    586 	char	line[BUFSIZ];
    587 	int	i;
    588 
    589 	i = getinput(line, "Label disk [n]?");
    590 	if (i <= 0 || (*line != 'y' && *line != 'Y') )
    591 		return;
    592 
    593 	if (checklabel(lp) != 0) {
    594 		printf("Label not written\n");
    595 		return;
    596 	}
    597 
    598 	if (writelabel(fd, lp) != 0) {
    599 		printf("Label not written\n");
    600 		return;
    601 	}
    602 	printf("Label written\n");
    603 }
    604 
    605 
    606 static void
    607 cmd_listfstypes(struct disklabel *lp, char *s, int fd)
    608 {
    609 
    610 	(void)list_fs_types();
    611 }
    612 
    613 
    614 static int
    615 runcmd(struct disklabel *lp, char *line, int fd)
    616 {
    617 	struct cmds *cmd;
    618 
    619 	for (cmd = cmds; cmd->name != NULL; cmd++)
    620 		if (strncmp(line, cmd->name, strlen(cmd->name)) == 0) {
    621 			if (cmd->func == NULL)
    622 				return -1;
    623 			(*cmd->func)(lp, line, fd);
    624 			return 0;
    625 		}
    626 
    627 	if (line[1] == '\0' &&
    628 	    line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
    629 		cmd_part(lp, line, fd);
    630 		return 0;
    631 	}
    632 
    633 	printf("Unknown command %s\n", line);
    634 	return 1;
    635 }
    636 
    637 
    638 static int
    639 getinput(char *line, const char *prompt, ...)
    640 {
    641 
    642 	for (;;) {
    643 		va_list ap;
    644 		va_start(ap, prompt);
    645 		vprintf(prompt, ap);
    646 		va_end(ap);
    647 
    648 		if (fgets(line, BUFSIZ, stdin) == NULL)
    649 			return -1;
    650 		if (line[0] == '\n' || line[0] == '\0')
    651 			return 0;
    652 		else {
    653 			char *p;
    654 
    655 			if ((p = strrchr(line, '\n')) != NULL)
    656 				*p = '\0';
    657 			return 1;
    658 		}
    659 	}
    660 }
    661 
    662 static int
    663 alphacmp(const void *a, const void *b)
    664 {
    665 
    666 	return (strcasecmp(*(const char * const*)a, *(const char * const*)b));
    667 }
    668 
    669 
    670 static void
    671 dumpnames(const char *prompt, const char * const *olist, size_t numentries)
    672 {
    673 	int	w;
    674 	size_t	i, entry, lines;
    675 	int	columns, width;
    676 	const char *p;
    677 	const char **list = NULL;
    678 
    679 	if (reallocarr(&list, numentries, sizeof(char *)) != 0)
    680 		err(1, "reallocarr");
    681 	width = 0;
    682 	printf("%s:\n", prompt);
    683 	for (i = 0; i < numentries; i++) {
    684 		list[i] = olist[i];
    685 		w = strlen(list[i]);
    686 		if (w > width)
    687 			width = w;
    688 	}
    689 #if 0
    690 	for (i = 0; i < numentries; i++)
    691 		printf("%s%s", i == 0 ? "" : ", ", list[i]);
    692 	puts("");
    693 #endif
    694 	(void)qsort(list, numentries, sizeof(char *), alphacmp);
    695 	width++;		/* want two spaces between items */
    696 	width = (width + 8) &~ 7;
    697 
    698 #define ttywidth 72
    699 	columns = ttywidth / width;
    700 #undef ttywidth
    701 	if (columns == 0)
    702 		columns = 1;
    703 	lines = (numentries + columns - 1) / columns;
    704 	/* Output sorted by columns */
    705 	for (i = 0; i < lines; i++) {
    706 		putc('\t', stdout);
    707 		entry = i;
    708 		for (;;) {
    709 			p = list[entry];
    710 			fputs(p, stdout);
    711 			entry += lines;
    712 			if (entry >= numentries)
    713 				break;
    714 			w = strlen(p);
    715 			while (w < width) {
    716 				w = (w + 8) & ~7;
    717 				putc('\t', stdout);
    718 			}
    719 		}
    720 		putc('\n', stdout);
    721 	}
    722 	free(list);
    723 }
    724 
    725 
    726 static void
    727 defnum(struct disklabel *lp, char *buf, uint32_t size)
    728 {
    729 
    730 	(void) snprintf(buf, BUFSIZ, "%.40gc, %" PRIu32 "s, %.40gM",
    731 	    size / (float) lp->d_secpercyl,
    732 	    size, size  * (lp->d_secsize / (float) (1024 * 1024)));
    733 }
    734 
    735 
    736 static intmax_t
    737 getnum(struct disklabel *lp, char *buf, intmax_t defaultval)
    738 {
    739 	char	*ep;
    740 	double	 d;
    741 	intmax_t rv;
    742 
    743 	if (defaultval && buf[0] == '$' && buf[1] == 0)
    744 		return defaultval;
    745 
    746 	d = strtod(buf, &ep);
    747 	if (buf == ep)
    748 		return -1;
    749 
    750 #define ROUND(a)	((((a) / lp->d_secpercyl) + \
    751 		 	 (((a) % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl)
    752 
    753 	switch (*ep) {
    754 	case '\0':
    755 	case 's':
    756 	case 'S':
    757 		rv = (intmax_t) d;
    758 		break;
    759 
    760 	case 'c':
    761 	case 'C':
    762 		rv = (intmax_t) (d * lp->d_secpercyl);
    763 		break;
    764 
    765 	case 'k':
    766 	case 'K':
    767 		rv =  (intmax_t) (d * 1024 / lp->d_secsize);
    768 		break;
    769 
    770 	case 'm':
    771 	case 'M':
    772 		rv =  (intmax_t) (d * 1024 * 1024 / lp->d_secsize);
    773 		break;
    774 
    775 	case 'g':
    776 	case 'G':
    777 		rv =  (intmax_t) (d * 1024 * 1024 * 1024 / lp->d_secsize);
    778 		break;
    779 
    780 	case 't':
    781 	case 'T':
    782 		rv =  (intmax_t) (d * 1024 * 1024 * 1024 * 1024 / lp->d_secsize);
    783 		break;
    784 
    785 	default:
    786 		printf("Unit error %c\n", *ep);
    787 		printf("Valid units: (S)ectors, (C)ylinders, (K)ilo, (M)ega, "
    788 		    "(G)iga, (T)era");
    789 		return -1;
    790 	}
    791 
    792 	if (rounding)
    793 		return ROUND(rv);
    794 	else
    795 		return rv;
    796 }
    797 
    798 
    799 void
    800 interact(struct disklabel *lp, int fd)
    801 {
    802 	char	line[BUFSIZ];
    803 
    804 	puts("Enter '?' for help");
    805 	for (;;) {
    806 		int i = getinput(line, "partition>");
    807 		if (i == -1)
    808 			return;
    809 		if (i == 0)
    810 			continue;
    811 		if (runcmd(lp, line, fd) == -1)
    812 			return;
    813 	}
    814 }
    815 
    816 #endif /* !NO_INTERACT */
    817