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