Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / sbin / disklabel / interact.c
blob17e62825bf30f87c6fddf50e5e189cc65e12a124
1 /* $NetBSD: interact.c,v 1.32 2009/10/21 01:07:46 snj Exp $ */
3 /*
4 * Copyright (c) 1997 Christos Zoulas. All rights reserved.
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.
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.
27 #if HAVE_NBTOOL_CONFIG_H
28 #include "nbtool_config.h"
29 #endif
31 #include <sys/cdefs.h>
32 #ifndef lint
33 __RCSID("$NetBSD: interact.c,v 1.32 2009/10/21 01:07:46 snj Exp $");
34 #endif /* lint */
36 #include <sys/param.h>
37 #define FSTYPENAMES
38 #define DKTYPENAMES
40 #include <err.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
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 */
53 #include "extern.h"
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);
72 static int rounding = 0; /* sector rounding */
73 static int chaining = 0; /* make partitions contiguous */
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 }
95 static void
96 cmd_help(struct disklabel *lp, char *s, int fd)
98 struct cmds *cmd;
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);
107 static void
108 cmd_chain(struct disklabel *lp, char *s, int fd)
110 int i;
111 char line[BUFSIZ];
113 i = getinput(":", "Automatically adjust partitions",
114 chaining ? "yes" : "no", line);
115 if (i <= 0)
116 return;
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;
132 static void
133 cmd_printall(struct disklabel *lp, char *s, int fd)
136 showinfo(stdout, lp, specname);
137 showpartitions(stdout, lp, Cflag);
141 static void
142 cmd_print(struct disklabel *lp, char *s, int fd)
145 showpartitions(stdout, lp, Cflag);
149 static void
150 cmd_info(struct disklabel *lp, char *s, int fd)
152 char line[BUFSIZ];
153 char def[BUFSIZ];
154 int v, i;
155 u_int32_t u;
157 printf("# Current values:\n");
158 showinfo(stdout, lp, specname);
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;
176 for (i = 0; i < DKMAXTYPES; i++) {
177 if (!strcasecmp(dktypenames[i], line)) {
178 lp->d_type = i;
179 goto done_typename;
182 v = atoi(line);
183 if ((unsigned)v >= DKMAXTYPES) {
184 warnx("Unknown disk type: %s", line);
185 continue;
187 lp->d_type = v;
188 done_typename:
189 break;
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));
201 /* d_packname */
202 cmd_name(lp, s, fd);
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;
216 lp->d_npartitions = u;
217 break;
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;
232 lp->d_secsize = u;
233 break;
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;
248 lp->d_nsectors = u;
249 break;
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;
264 lp->d_ntracks = u;
265 break;
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;
281 lp->d_secpercyl = u;
282 break;
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;
297 lp->d_ncylinders = u;
298 break;
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;
313 lp->d_secperunit = u;
314 break;
317 /* d_rpm */
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;
331 lp->d_interleave = u;
332 break;
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;
347 lp->d_trackskew = u;
348 break;
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;
363 lp->d_cylskew = u;
364 break;
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;
379 lp->d_headswitch = u;
380 break;
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;
395 lp->d_trkseek = u;
396 break;
401 static void
402 cmd_name(struct disklabel *lp, char *s, int fd)
404 char line[BUFSIZ];
405 char def[BUFSIZ];
406 int i;
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));
417 static void
418 cmd_round(struct disklabel *lp, char *s, int fd)
420 int i;
421 char line[BUFSIZ];
423 i = getinput(":", "Rounding", rounding ? "cylinders" : "sectors", line);
424 if (i <= 0)
425 return;
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;
443 static void
444 cmd_part(struct disklabel *lp, char *s, int fd)
446 int i;
447 char line[BUFSIZ];
448 char def[BUFSIZ];
449 int part;
450 struct partition *p, ps;
452 part = s[0] - 'a';
453 p = &lp->d_partitions[part];
454 if (part >= lp->d_npartitions)
455 lp->d_npartitions = part + 1;
457 (void)memcpy(&ps, p, sizeof(ps));
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;
474 for (i = 0; i < FSMAXTYPES; i++)
475 if (!strcasecmp(line, fstypenames[i])) {
476 p->p_fstype = i;
477 goto done_typename;
479 printf("Invalid file system typename `%s'\n", line);
480 continue;
481 done_typename:
482 break;
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;
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;
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;
513 p->p_offset = i;
515 break;
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;
534 p->p_size = i;
535 break;
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);
549 offs = cp[i].p_offset + cp[i].p_size;
556 static void
557 cmd_label(struct disklabel *lp, char *s, int fd)
559 char line[BUFSIZ];
560 int i;
562 i = getinput("?", "Label disk", "n", line);
563 if (i <= 0 || (*line != 'y' && *line != 'Y') )
564 return;
566 if (checklabel(lp) != 0) {
567 printf("Label not written\n");
568 return;
571 if (writelabel(fd, lp) != 0) {
572 printf("Label not written\n");
573 return;
575 printf("Label written\n");
579 static void
580 cmd_listfstypes(struct disklabel *lp, char *s, int fd)
583 (void)list_fs_types();
587 static int
588 runcmd(struct disklabel *lp, char *line, int fd)
590 struct cmds *cmd;
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;
600 if (line[1] == '\0' &&
601 line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
602 cmd_part(lp, line, fd);
603 return 0;
606 printf("Unknown command %s\n", line);
607 return 1;
611 static int
612 getinput(const char *sep, const char *prompt, const char *def, char *line)
615 for (;;) {
616 printf("%s", prompt);
617 if (def)
618 printf(" [%s]", def);
619 printf("%s ", sep);
621 if (fgets(line, BUFSIZ, stdin) == NULL)
622 return -1;
623 if (line[0] == '\n' || line[0] == '\0') {
624 if (def)
625 return 0;
627 else {
628 char *p;
630 if ((p = strrchr(line, '\n')) != NULL)
631 *p = '\0';
632 return 1;
637 static int
638 alphacmp(const void *a, const void *b)
641 return (strcasecmp(*(const char * const*)a, *(const char * const*)b));
645 static void
646 dumpnames(const char *prompt, const char * const *olist, size_t numentries)
648 int w;
649 size_t i, entry, lines;
650 int columns, width;
651 const char *p;
652 const char **list;
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;
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;
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);
695 putc('\n', stdout);
697 free(list);
701 static void
702 defnum(struct disklabel *lp, char *buf, uint32_t size)
705 (void) snprintf(buf, BUFSIZ, "%.40gc, %us, %.40gM",
706 size / (float) lp->d_secpercyl,
707 size, size * (lp->d_secsize / (float) (1024 * 1024)));
711 static int
712 getnum(struct disklabel *lp, char *buf, int max)
714 char *ep;
715 double d;
716 int rv;
718 if (max && buf[0] == '$' && buf[1] == 0)
719 return max;
721 d = strtod(buf, &ep);
722 if (buf == ep)
723 return -1;
725 #define ROUND(a) ((((a) / lp->d_secpercyl) + \
726 (((a) % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl)
728 switch (*ep) {
729 case '\0':
730 case 's':
731 case 'S':
732 rv = (int) d;
733 break;
735 case 'c':
736 case 'C':
737 rv = (int) (d * lp->d_secpercyl);
738 break;
740 case 'k':
741 case 'K':
742 rv = (int) (d * 1024 / lp->d_secsize);
743 break;
745 case 'm':
746 case 'M':
747 rv = (int) (d * 1024 * 1024 / lp->d_secsize);
748 break;
750 case 'g':
751 case 'G':
752 rv = (int) (d * 1024 * 1024 * 1024 / lp->d_secsize);
753 break;
755 case 't':
756 case 'T':
757 rv = (int) (d * 1024 * 1024 * 1024 * 1024 / lp->d_secsize);
758 break;
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;
767 if (rounding)
768 return ROUND(rv);
769 else
770 return rv;
774 void
775 interact(struct disklabel *lp, int fd)
777 char line[BUFSIZ];
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;