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