Home | History | Annotate | Line # | Download | only in disklabel
interact.c revision 1.30.28.1
      1 /*	$NetBSD: interact.c,v 1.30.28.1 2009/05/13 19:19:00 jym 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.28.1 2009/05/13 19:19:00 jym 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 int	getnum(struct disklabel *, char *, int);
     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 	char	line[BUFSIZ];
    453 	char	def[BUFSIZ];
    454 	int	part;
    455 	struct partition *p, ps;
    456 
    457 	part = s[0] - 'a';
    458 	p = &lp->d_partitions[part];
    459 	if (part >= lp->d_npartitions)
    460 		lp->d_npartitions = part + 1;
    461 
    462 	(void)memcpy(&ps, p, sizeof(ps));
    463 
    464 	for (;;) {
    465 		i = p->p_fstype;
    466 		if (i < 0 || i >= FSMAXTYPES)
    467 			i = 0;
    468 		snprintf(def, sizeof(def), "%s", fstypenames[i]);
    469 		i = getinput(":", "Filesystem type [?]", def, line);
    470 		if (i == -1)
    471 			return;
    472 		else if (i == 0)
    473 			break;
    474 		if (!strcmp(line, "?")) {
    475 			dumpnames("Supported file system types",
    476 			    fstypenames, FSMAXTYPES);
    477 			continue;
    478 		}
    479 		for (i = 0; i < FSMAXTYPES; i++)
    480 			if (!strcasecmp(line, fstypenames[i])) {
    481 				p->p_fstype = i;
    482 				goto done_typename;
    483 			}
    484 		printf("Invalid file system typename `%s'\n", line);
    485 		continue;
    486  done_typename:
    487 		break;
    488 	}
    489 	for (;;) {
    490 		defnum(lp, def, p->p_offset);
    491 		i = getinput(":",
    492 		    "Start offset ('x' to start after partition 'x')",
    493 		    def, line);
    494 		if (i == -1)
    495 			return;
    496 		else if (i == 0)
    497 			break;
    498 		if (line[1] == '\0' &&
    499 	    		line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
    500 			struct partition *cp = lp->d_partitions;
    501 
    502 			if ((cp[line[0] - 'a'].p_offset +
    503 			    cp[line[0] - 'a'].p_size) >= lp->d_secperunit) {
    504 				printf("Bad offset `%s'\n", line);
    505 				continue;
    506 			} else {
    507 				p->p_offset = cp[line[0] - 'a'].p_offset +
    508 				    cp[line[0] - 'a'].p_size;
    509 			}
    510 		} else {
    511 			if ((i = getnum(lp, line, 0)) == -1 || i < 0) {
    512 				printf("Bad offset `%s'\n", line);
    513 				continue;
    514 			} else if ((uint32_t)i > lp->d_secperunit) {
    515 				printf("Offset `%s' out of range\n", line);
    516 				continue;
    517 			}
    518 			p->p_offset = i;
    519 		}
    520 		break;
    521 	}
    522 	for (;;) {
    523 		defnum(lp, def, p->p_size);
    524 		i = getinput(":", "Partition size ('$' for all remaining)",
    525 		    def, line);
    526 		if (i == -1)
    527 			return;
    528 		else if (i == 0)
    529 			break;
    530 		if ((i = getnum(lp, line, lp->d_secperunit - p->p_offset))
    531 		    == -1) {
    532 			printf("Bad size `%s'\n", line);
    533 			continue;
    534 		} else if
    535 		    ((i + p->p_offset) > lp->d_secperunit) {
    536 			printf("Size `%s' out of range\n", line);
    537 			continue;
    538 		}
    539 		p->p_size = i;
    540 		break;
    541 	}
    542 
    543 	if (memcmp(&ps, p, sizeof(ps)))
    544 		showpartition(stdout, lp, part, Cflag);
    545 	if (chaining) {
    546 		int offs = -1;
    547 		struct partition *cp = lp->d_partitions;
    548 		for (i = 0; i < lp->d_npartitions; i++) {
    549 			if (cp[i].p_fstype != FS_UNUSED) {
    550 				if (offs != -1 && cp[i].p_offset != (uint32_t)offs) {
    551 					cp[i].p_offset = offs;
    552 					showpartition(stdout, lp, i, Cflag);
    553 					}
    554 				offs = cp[i].p_offset + cp[i].p_size;
    555 			}
    556 		}
    557 	}
    558 }
    559 
    560 
    561 static void
    562 cmd_label(struct disklabel *lp, char *s, int fd)
    563 {
    564 	char	line[BUFSIZ];
    565 	int	i;
    566 
    567 	i = getinput("?", "Label disk", "n", line);
    568 	if (i <= 0 || (*line != 'y' && *line != 'Y') )
    569 		return;
    570 
    571 	if (checklabel(lp) != 0) {
    572 		printf("Label not written\n");
    573 		return;
    574 	}
    575 
    576 	if (writelabel(fd, lp) != 0) {
    577 		printf("Label not written\n");
    578 		return;
    579 	}
    580 	printf("Label written\n");
    581 }
    582 
    583 
    584 static void
    585 cmd_listfstypes(struct disklabel *lp, char *s, int fd)
    586 {
    587 
    588 	(void)list_fs_types();
    589 }
    590 
    591 
    592 static int
    593 runcmd(struct disklabel *lp, char *line, int fd)
    594 {
    595 	struct cmds *cmd;
    596 
    597 	for (cmd = cmds; cmd->name != NULL; cmd++)
    598 		if (strncmp(line, cmd->name, strlen(cmd->name)) == 0) {
    599 			if (cmd->func == NULL)
    600 				return -1;
    601 			(*cmd->func)(lp, line, fd);
    602 			return 0;
    603 		}
    604 
    605 	if (line[1] == '\0' &&
    606 	    line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
    607 		cmd_part(lp, line, fd);
    608 		return 0;
    609 	}
    610 
    611 	printf("Unknown command %s\n", line);
    612 	return 1;
    613 }
    614 
    615 
    616 static int
    617 getinput(const char *sep, const char *prompt, const char *def, char *line)
    618 {
    619 
    620 	for (;;) {
    621 		printf("%s", prompt);
    622 		if (def)
    623 			printf(" [%s]", def);
    624 		printf("%s ", sep);
    625 
    626 		if (fgets(line, BUFSIZ, stdin) == NULL)
    627 			return -1;
    628 		if (line[0] == '\n' || line[0] == '\0') {
    629 			if (def)
    630 				return 0;
    631 		}
    632 		else {
    633 			char *p;
    634 
    635 			if ((p = strrchr(line, '\n')) != NULL)
    636 				*p = '\0';
    637 			return 1;
    638 		}
    639 	}
    640 }
    641 
    642 static int
    643 alphacmp(const void *a, const void *b)
    644 {
    645 
    646 	return (strcasecmp(*(const char * const*)a, *(const char * const*)b));
    647 }
    648 
    649 
    650 static void
    651 dumpnames(const char *prompt, const char * const *olist, size_t numentries)
    652 {
    653 	int	w;
    654 	size_t	i, entry, lines;
    655 	int	columns, width;
    656 	const char *p;
    657 	const char **list;
    658 
    659 	if ((list = (const char **)malloc(sizeof(char *) * numentries)) == NULL)
    660 		err(1, "malloc");
    661 	width = 0;
    662 	printf("%s:\n", prompt);
    663 	for (i = 0; i < numentries; i++) {
    664 		list[i] = olist[i];
    665 		w = strlen(list[i]);
    666 		if (w > width)
    667 			width = w;
    668 	}
    669 #if 0
    670 	for (i = 0; i < numentries; i++)
    671 		printf("%s%s", i == 0 ? "" : ", ", list[i]);
    672 	puts("");
    673 #endif
    674 	(void)qsort(list, numentries, sizeof(char *), alphacmp);
    675 	width++;		/* want two spaces between items */
    676 	width = (width + 8) &~ 7;
    677 
    678 #define ttywidth 72
    679 	columns = ttywidth / width;
    680 #undef ttywidth
    681 	if (columns == 0)
    682 		columns = 1;
    683 	lines = (numentries + columns - 1) / columns;
    684 	/* Output sorted by columns */
    685 	for (i = 0; i < lines; i++) {
    686 		putc('\t', stdout);
    687 		entry = i;
    688 		for (;;) {
    689 			p = list[entry];
    690 			fputs(p, stdout);
    691 			entry += lines;
    692 			if (entry >= numentries)
    693 				break;
    694 			w = strlen(p);
    695 			while (w < width) {
    696 				w = (w + 8) & ~7;
    697 				putc('\t', stdout);
    698 			}
    699 		}
    700 		putc('\n', stdout);
    701 	}
    702 	free(list);
    703 }
    704 
    705 
    706 static void
    707 defnum(struct disklabel *lp, char *buf, uint32_t size)
    708 {
    709 
    710 	(void) snprintf(buf, BUFSIZ, "%gc, %us, %gM",
    711 	    size / (float) lp->d_secpercyl,
    712 	    size, size  * (lp->d_secsize / (float) (1024 * 1024)));
    713 }
    714 
    715 
    716 static int
    717 getnum(struct disklabel *lp, char *buf, int max)
    718 {
    719 	char	*ep;
    720 	double	 d;
    721 	int	 rv;
    722 
    723 	if (max && buf[0] == '$' && buf[1] == 0)
    724 		return max;
    725 
    726 	d = strtod(buf, &ep);
    727 	if (buf == ep)
    728 		return -1;
    729 
    730 #define ROUND(a)	((((a) / lp->d_secpercyl) + \
    731 		 	 (((a) % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl)
    732 
    733 	switch (*ep) {
    734 	case '\0':
    735 	case 's':
    736 	case 'S':
    737 		rv = (int) d;
    738 		break;
    739 
    740 	case 'c':
    741 	case 'C':
    742 		rv = (int) (d * lp->d_secpercyl);
    743 		break;
    744 
    745 	case 'k':
    746 	case 'K':
    747 		rv =  (int) (d * 1024 / lp->d_secsize);
    748 		break;
    749 
    750 	case 'm':
    751 	case 'M':
    752 		rv =  (int) (d * 1024 * 1024 / lp->d_secsize);
    753 		break;
    754 
    755 	case 'g':
    756 	case 'G':
    757 		rv =  (int) (d * 1024 * 1024 * 1024 / lp->d_secsize);
    758 		break;
    759 
    760 	case 't':
    761 	case 'T':
    762 		rv =  (int) (d * 1024 * 1024 * 1024 * 1024 / lp->d_secsize);
    763 		break;
    764 
    765 	default:
    766 		printf("Unit error %c\n", *ep);
    767 		printf("Valid units: (S)ectors, (C)ylinders, (K)ilo, (M)ega, "
    768 		    "(G)iga, (T)era");
    769 		return -1;
    770 	}
    771 
    772 	if (rounding)
    773 		return ROUND(rv);
    774 	else
    775 		return rv;
    776 }
    777 
    778 
    779 void
    780 interact(struct disklabel *lp, int fd)
    781 {
    782 	char	line[BUFSIZ];
    783 
    784 	for (;;) {
    785 		if (getinput(">", "partition", NULL, line) == -1)
    786 			return;
    787 		if (runcmd(lp, line, fd) == -1)
    788 			return;
    789 	}
    790 }
    791