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