xref: /netbsd-src/sbin/disklabel/interact.c (revision ccf8c4a2ce1b9f70d6a07d7aede7488b9e1f1e85)
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