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