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