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