xref: /netbsd-src/sbin/disklabel/interact.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
1 /*	$NetBSD: interact.c,v 1.37 2013/01/17 18:33:58 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.37 2013/01/17 18:33:58 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 (dk_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