1/*
2 * Mach Operating System
3 * Copyright (c) 1992 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/disk.h>
31#include <sys/disklabel.h>
32#include <sys/diskpc98.h>
33#include <sys/param.h>
34#include <sys/stat.h>
35#include <sys/mount.h>
36#include <ctype.h>
37#include <fcntl.h>
38#include <err.h>
39#include <errno.h>
40#include <libgeom.h>
41#include <paths.h>
42#include <regex.h>
43#include <stdint.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49int iotest;
50
51#define LBUF 100
52static char lbuf[LBUF];
53
54/*
55 *
56 * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
57 *
58 * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
59 *	Copyright (c) 1989	Robert. V. Baron
60 *	Created.
61 */
62
63#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
64#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
65
66#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
67
68#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
69#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
70static int secsize = 0;		/* the sensed sector size */
71
72static char *disk;
73
74static int cyls, sectors, heads, cylsecs, disksecs;
75
76struct mboot {
77	unsigned char padding[2]; /* force the longs to be long aligned */
78	unsigned char bootinst[510];
79	unsigned short int	signature;
80	struct	pc98_partition parts[8];
81	unsigned char large_sector_overflow[MAX_SEC_SIZE-MIN_SEC_SIZE];
82};
83
84static struct mboot mboot;
85static int fd;
86
87static uint dos_cyls;
88static uint dos_heads;
89static uint dos_sectors;
90static uint dos_cylsecs;
91
92#define MAX_ARGS	10
93
94typedef struct cmd {
95    char		cmd;
96    int			n_args;
97    struct arg {
98	char	argtype;
99	int	arg_val;
100    }			args[MAX_ARGS];
101} CMD;
102
103static int B_flag  = 0;		/* replace boot code */
104static int I_flag  = 0;		/* Inizialize disk to defaults */
105static int a_flag  = 0;		/* set active partition */
106static int i_flag  = 0;		/* replace partition data */
107static int u_flag  = 0;		/* update partition data */
108static int s_flag  = 0;		/* Print a summary and exit */
109static int t_flag  = 0;		/* test only */
110static char *f_flag = NULL;	/* Read config info from file */
111static int v_flag  = 0;		/* Be verbose */
112
113static struct part_type
114{
115	unsigned char type;
116	const char *name;
117} part_types[] = {
118	 {0x00, "unused"}
119	,{0x01, "Primary DOS with 12 bit FAT"}
120	,{0x11, "MSDOS"}
121	,{0x20, "MSDOS"}
122	,{0x21, "MSDOS"}
123	,{0x22, "MSDOS"}
124	,{0x23, "MSDOS"}
125	,{0x02, "XENIX / file system"}
126	,{0x03, "XENIX /usr file system"}
127	,{0x04, "PC-UX"}
128	,{0x05, "Extended DOS"}
129	,{0x06, "Primary 'big' DOS (> 32MB)"}
130	,{0x07, "OS/2 HPFS, QNX or Advanced UNIX"}
131	,{0x08, "AIX file system"}
132	,{0x09, "AIX boot partition or Coherent"}
133	,{0x0A, "OS/2 Boot Manager or OPUS"}
134	,{0x10, "OPUS"}
135	,{0x14, "FreeBSD/NetBSD/386BSD"}
136	,{0x94, "FreeBSD/NetBSD/386BSD"}
137	,{0x40, "VENIX 286"}
138	,{0x50, "DM"}
139	,{0x51, "DM"}
140	,{0x52, "CP/M or Microport SysV/AT"}
141	,{0x56, "GB"}
142	,{0x61, "Speed"}
143	,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}
144	,{0x64, "Novell Netware 2.xx"}
145	,{0x65, "Novell Netware 3.xx"}
146	,{0x75, "PCIX"}
147	,{0x40, "Minix"}
148};
149
150static void print_s0(int which);
151static void print_part(int i);
152static void init_sector0(unsigned long start);
153static void init_boot(void);
154static void change_part(int i, int force);
155static void print_params(void);
156static void change_active(int which);
157static void change_code(void);
158static void get_params_to_use(void);
159static char *get_rootdisk(void);
160static void dos(u_int32_t start, u_int32_t size, struct pc98_partition *partp);
161static int open_disk(int flag);
162static ssize_t read_disk(off_t sector, void *buf);
163static int write_disk(off_t sector, void *buf);
164static int get_params(void);
165static int read_s0(void);
166static int write_s0(void);
167static int ok(const char *str);
168static int decimal(const char *str, int *num, int deflt);
169static const char *get_type(int type);
170static void usage(void);
171static int string(const char *str, char **ans);
172static void reset_boot(void);
173
174int
175main(int argc, char *argv[])
176{
177	struct	stat sb;
178	int	c, i;
179	int	partition = -1;
180	struct	pc98_partition *partp;
181
182	while ((c = getopt(argc, argv, "BIa:f:istuv12345678")) != -1)
183		switch (c) {
184		case 'B':
185			B_flag = 1;
186			break;
187		case 'I':
188			I_flag = 1;
189			break;
190		case 'a':
191			a_flag = 1;
192			break;
193		case 'f':
194			f_flag = optarg;
195			break;
196		case 'i':
197			i_flag = 1;
198			break;
199		case 's':
200			s_flag = 1;
201			break;
202		case 't':
203			t_flag = 1;
204			break;
205		case 'u':
206			u_flag = 1;
207			break;
208		case 'v':
209			v_flag = 1;
210			break;
211		case '1':
212		case '2':
213		case '3':
214		case '4':
215		case '5':
216		case '6':
217		case '7':
218		case '8':
219			partition = c - '0';
220			break;
221		default:
222			usage();
223		}
224	if (f_flag || i_flag)
225		u_flag = 1;
226	if (t_flag)
227		v_flag = 1;
228	argc -= optind;
229	argv += optind;
230
231	if (argc == 0) {
232		disk = get_rootdisk();
233	} else {
234		if (stat(argv[0], &sb) == 0) {
235			/* OK, full pathname given */
236			disk = argv[0];
237		} else if (errno == ENOENT && argv[0][0] != '/') {
238			/* Try prepending "/dev" */
239			asprintf(&disk, "%s%s", _PATH_DEV, argv[0]);
240			if (disk == NULL)
241				errx(1, "out of memory");
242		} else {
243			/* other stat error, let it fail below */
244			disk = argv[0];
245		}
246	}
247	if (open_disk(u_flag) < 0)
248		err(1, "cannot open disk %s", disk);
249
250	if (s_flag) {
251		if (read_s0())
252			err(1, "read_s0");
253		printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
254		    dos_sectors);
255		printf("Part  %11s %11s %4s %4s %-16s\n", "Start", "Size", "MID",
256		    "SID", "Name");
257		for (i = 0; i < PC98_NPARTS; i++) {
258			partp = ((struct pc98_partition *) &mboot.parts) + i;
259			if (partp->dp_sid == 0)
260				continue;
261			printf("%4d: %11u %11u 0x%02x 0x%02x %-16.16s\n", i + 1,
262			    partp->dp_scyl * cylsecs,
263			    (partp->dp_ecyl - partp->dp_scyl + 1) * cylsecs,
264			    partp->dp_mid, partp->dp_sid, partp->dp_name);
265		}
266		exit(0);
267	}
268
269	printf("******* Working on device %s *******\n",disk);
270
271	if (I_flag) {
272		read_s0();
273		reset_boot();
274		partp = (struct pc98_partition *) (&mboot.parts[0]);
275		partp->dp_mid = DOSMID_386BSD;
276		partp->dp_sid = DOSSID_386BSD;
277		strncpy(partp->dp_name, "FreeBSD", sizeof(partp->dp_name));
278		/* Start c/h/s. */
279		partp->dp_scyl = partp->dp_ipl_cyl = 1;
280		partp->dp_shd = partp->dp_ipl_head = 1;
281		partp->dp_ssect = partp->dp_ipl_sct = 0;
282
283		/* End c/h/s. */
284		partp->dp_ecyl = dos_cyls - 1;
285		partp->dp_ehd = dos_cylsecs / dos_sectors;
286		partp->dp_esect = dos_sectors;
287
288		if (v_flag)
289			print_s0(-1);
290		if (!t_flag)
291			write_s0();
292		exit(0);
293	}
294
295	if (f_flag) {
296	    if (v_flag)
297		print_s0(-1);
298	    if (!t_flag)
299		write_s0();
300	} else {
301	    if(u_flag)
302		get_params_to_use();
303	    else
304		print_params();
305
306	    if (read_s0())
307		init_sector0(dos_sectors);
308
309	    printf("Media sector size is %d\n", secsize);
310	    printf("Warning: BIOS sector numbering starts with sector 1\n");
311	    printf("Information from DOS bootblock is:\n");
312	    if (partition == -1)
313		for (i = 1; i <= PC98_NPARTS; i++)
314		    change_part(i, v_flag);
315	    else
316		change_part(partition, 1);
317
318	    if (u_flag || a_flag)
319		change_active(partition);
320
321	    if (B_flag)
322		change_code();
323
324	    if (u_flag || a_flag || B_flag) {
325		if (!t_flag) {
326		    printf("\nWe haven't changed the partition table yet.  ");
327		    printf("This is your last chance.\n");
328		}
329		print_s0(-1);
330		if (!t_flag) {
331		    if (ok("Should we write new partition table?"))
332			write_s0();
333		} else {
334		    printf("\n-t flag specified -- partition table not written.\n");
335		}
336	    }
337	}
338
339	exit(0);
340}
341
342static void
343usage()
344{
345	fprintf(stderr, "%s%s",
346		"usage: fdisk [-BIaistu] [-12345678] [disk]\n",
347 		"       fdisk -f configfile [-itv] [disk]\n");
348        exit(1);
349}
350
351static struct pc98_partition mtpart;
352
353static int
354part_unused(int i)
355{
356	struct	  pc98_partition *partp;
357
358	partp = ((struct pc98_partition *) &mboot.parts) + i - 1;
359	return (bcmp(partp, &mtpart, sizeof (struct pc98_partition)) == 0);
360}
361
362static void
363print_s0(int which)
364{
365	int	i;
366
367	print_params();
368	printf("Information from DOS bootblock is:\n");
369	if (which == -1) {
370		for (i = 1; i <= PC98_NPARTS; i++)
371			if (v_flag || !part_unused(i)) {
372				printf("%d: ", i);
373				print_part(i);
374			}
375	}
376	else
377		print_part(which);
378}
379
380static void
381print_part(int i)
382{
383	struct	  pc98_partition *partp;
384	u_int64_t part_sz, part_mb;
385
386	if (part_unused(i)) {
387		printf("<UNUSED>\n");
388		return;
389	}
390	/*
391	 * Be careful not to overflow.
392	 */
393	partp = ((struct pc98_partition *) &mboot.parts) + i - 1;
394	part_sz = (partp->dp_ecyl - partp->dp_scyl + 1) * cylsecs;
395	part_mb = part_sz * secsize;
396	part_mb /= (1024 * 1024);
397	printf("sysmid %d (%#04x),(%s)\n", partp->dp_mid, partp->dp_mid,
398	    get_type(partp->dp_mid));
399	printf("    start %lu, size %lu (%ju Meg), sid %d\n",
400		(u_long)(partp->dp_scyl * cylsecs), (u_long)part_sz,
401		(uintmax_t)part_mb, partp->dp_sid);
402	printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
403		,partp->dp_scyl
404		,partp->dp_shd
405		,partp->dp_ssect
406		,partp->dp_ecyl
407		,partp->dp_ehd
408		,partp->dp_esect);
409	printf ("\tsystem Name %.16s\n", partp->dp_name);
410}
411
412
413static void
414init_boot(void)
415{
416
417	mboot.signature = PC98_MAGIC;
418}
419
420
421static void
422init_sector0(unsigned long start)
423{
424	struct pc98_partition *partp =
425		(struct pc98_partition *)(&mboot.parts[0]);
426
427	init_boot();
428
429	partp->dp_mid = DOSMID_386BSD;
430	partp->dp_sid = DOSSID_386BSD;
431
432	dos(start, disksecs - start, partp);
433}
434
435static void
436change_part(int i, int force)
437{
438	struct pc98_partition *partp =
439		((struct pc98_partition *) &mboot.parts) + i - 1;
440
441	if (!force && part_unused(i))
442		return;
443
444	printf("The data for partition %d is:\n", i);
445	print_part(i);
446
447	if (u_flag && ok("Do you want to change it?")) {
448		int tmp;
449
450		if (i_flag) {
451			bzero((char *)partp, sizeof (struct pc98_partition));
452			if (i == 1) {
453				init_sector0(1);
454				printf("\nThe static data for the slice 1 has been reinitialized to:\n");
455				print_part(i);
456			}
457		}
458		do {
459			int x_start = partp->dp_scyl * cylsecs ;
460			int x_size  = (partp->dp_ecyl - partp->dp_scyl + 1) * cylsecs;
461			Decimal("sysmid", partp->dp_mid, tmp);
462			Decimal("syssid", partp->dp_sid, tmp);
463			String ("system name", partp->dp_name, 16);
464			Decimal("start", x_start, tmp);
465			Decimal("size", x_size, tmp);
466
467			if (ok("Explicitly specify beg/end address ?"))
468			{
469				int	tsec,tcyl,thd;
470				tcyl = partp->dp_scyl;
471				thd = partp->dp_shd;
472				tsec = partp->dp_ssect;
473				Decimal("beginning cylinder", tcyl, tmp);
474				Decimal("beginning head", thd, tmp);
475				Decimal("beginning sector", tsec, tmp);
476				partp->dp_scyl = tcyl;
477				partp->dp_ssect = tsec;
478				partp->dp_shd = thd;
479				partp->dp_ipl_cyl = partp->dp_scyl;
480				partp->dp_ipl_sct = partp->dp_ssect;
481				partp->dp_ipl_head = partp->dp_shd;
482
483				tcyl = partp->dp_ecyl;
484				thd = partp->dp_ehd;
485				tsec = partp->dp_esect;
486				Decimal("ending cylinder", tcyl, tmp);
487				Decimal("ending head", thd, tmp);
488				Decimal("ending sector", tsec, tmp);
489				partp->dp_ecyl = tcyl;
490				partp->dp_esect = tsec;
491				partp->dp_ehd = thd;
492			} else
493				dos(x_start, x_size, partp);
494
495			print_part(i);
496		} while (!ok("Are we happy with this entry?"));
497	}
498}
499
500static void
501print_params()
502{
503	printf("parameters extracted from in-core disklabel are:\n");
504	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
505			,cyls,heads,sectors,cylsecs);
506	if (dos_cyls > 65535 || dos_heads > 255 || dos_sectors > 255)
507		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
508	printf("parameters to be used for BIOS calculations are:\n");
509	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
510		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
511}
512
513static void
514change_active(int which)
515{
516	struct pc98_partition *partp = &mboot.parts[0];
517	int active, i, new, tmp;
518
519	active = -1;
520	for (i = 0; i < PC98_NPARTS; i++) {
521		if ((partp[i].dp_sid & PC98_SID_ACTIVE) == 0)
522			continue;
523		printf("Partition %d is marked active\n", i + 1);
524		if (active == -1)
525			active = i + 1;
526	}
527	if (a_flag && which != -1)
528		active = which;
529	else if (active == -1)
530		active = 1;
531
532	if (!ok("Do you want to change the active partition?"))
533		return;
534setactive:
535	do {
536		new = active;
537		Decimal("active partition", new, tmp);
538		if (new < 1 || new > 8) {
539			printf("Active partition number must be in range 1-8."
540					"  Try again.\n");
541			goto setactive;
542		}
543		active = new;
544	} while (!ok("Are you happy with this choice"));
545	if (active > 0 && active <= 8)
546		partp[active-1].dp_sid |= PC98_SID_ACTIVE;
547}
548
549static void
550change_code()
551{
552	if (ok("Do you want to change the boot code?"))
553		init_boot();
554}
555
556void
557get_params_to_use()
558{
559	int	tmp;
560	print_params();
561	if (ok("Do you want to change our idea of what BIOS thinks ?"))
562	{
563		do
564		{
565			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
566			Decimal("BIOS's idea of #heads", dos_heads, tmp);
567			Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
568			dos_cylsecs = dos_heads * dos_sectors;
569			print_params();
570		}
571		while(!ok("Are you happy with this choice"));
572	}
573}
574
575
576/***********************************************\
577* Change real numbers into strange dos numbers	*
578\***********************************************/
579static void
580dos(u_int32_t start, u_int32_t size, struct pc98_partition *partp)
581{
582	u_int32_t end;
583
584	if (partp->dp_mid == 0 && partp->dp_sid == 0 &&
585	    start == 0 && size == 0) {
586		memcpy(partp, &mtpart, sizeof(*partp));
587		return;
588	}
589
590	/* Start c/h/s. */
591	partp->dp_scyl = partp->dp_ipl_cyl = start / dos_cylsecs;
592	partp->dp_shd = partp->dp_ipl_head = start % dos_cylsecs / dos_sectors;
593	partp->dp_ssect = partp->dp_ipl_sct = start % dos_sectors;
594
595	/* End c/h/s. */
596	end = start + size - cylsecs;
597	partp->dp_ecyl = end / dos_cylsecs;
598	partp->dp_ehd = end % dos_cylsecs / dos_sectors;
599	partp->dp_esect = end % dos_sectors;
600}
601
602static int
603open_disk(int flag)
604{
605	struct stat 	st;
606	int rwmode;
607
608	if (stat(disk, &st) == -1) {
609		if (errno == ENOENT)
610			return -2;
611		warnx("can't get file status of %s", disk);
612		return -1;
613	}
614	if ( !(st.st_mode & S_IFCHR) )
615		warnx("device %s is not character special", disk);
616	rwmode = I_flag || a_flag || B_flag || flag ? O_RDWR : O_RDONLY;
617	fd = open(disk, rwmode);
618	if (fd == -1 && errno == EPERM && rwmode == O_RDWR)
619		fd = open(disk, O_RDONLY);
620	if (fd == -1 && errno == ENXIO)
621		return -2;
622	if (fd == -1) {
623		warnx("can't open device %s", disk);
624		return -1;
625	}
626	if (get_params() == -1) {
627		warnx("can't get disk parameters on %s", disk);
628		return -1;
629	}
630	return fd;
631}
632
633static ssize_t
634read_disk(off_t sector, void *buf)
635{
636
637	lseek(fd, (sector * 512), 0);
638	return read(fd, buf,
639		    secsize > MIN_SEC_SIZE ? secsize : MIN_SEC_SIZE * 2);
640}
641
642static int
643write_disk(off_t sector, void *buf)
644{
645	int error;
646	struct gctl_req *grq;
647	const char *q;
648	char fbuf[BUFSIZ];
649	int i, fdw, sz;
650
651	sz = secsize > MIN_SEC_SIZE ? secsize : MIN_SEC_SIZE * 2;
652	grq = gctl_get_handle();
653	gctl_ro_param(grq, "verb", -1, "write PC98");
654	gctl_ro_param(grq, "class", -1, "PC98");
655	q = strrchr(disk, '/');
656	if (q == NULL)
657		q = disk;
658	else
659		q++;
660	gctl_ro_param(grq, "geom", -1, q);
661	gctl_ro_param(grq, "data", sz, buf);
662	q = gctl_issue(grq);
663	if (q == NULL) {
664		gctl_free(grq);
665		return(0);
666	}
667	warnx("Geom problem: %s", q);
668	gctl_free(grq);
669
670	warnx("Warning: Partitioning via geom failed, trying raw write");
671	error = pwrite(fd, buf, sz, sector * 512);
672	if (error == sz)
673		return (0);
674
675	for (i = 0; i < PC98_NPARTS; i++) {
676		sprintf(fbuf, "%ss%d", disk, i + 1);
677		fdw = open(fbuf, O_RDWR, 0);
678		if (fdw < 0)
679			continue;
680		error = ioctl(fdw, DIOCSPC98, buf);
681		close(fdw);
682		if (error == 0)
683			return (0);
684	}
685	warnx("Failed to write sector zero");
686	return(EINVAL);
687}
688
689static int
690get_params()
691{
692	int error;
693	u_int u;
694	off_t o;
695
696	error = ioctl(fd, DIOCGFWSECTORS, &u);
697	if (error == 0)
698		sectors = dos_sectors = u;
699	else
700		sectors = dos_sectors = 17;
701
702	error = ioctl(fd, DIOCGFWHEADS, &u);
703	if (error == 0)
704		heads = dos_heads = u;
705	else
706		heads = dos_heads = 8;
707
708	dos_cylsecs = cylsecs = heads * sectors;
709	disksecs = cyls * heads * sectors;
710
711	error = ioctl(fd, DIOCGSECTORSIZE, &u);
712	if (error != 0 || u == 0)
713		u = 512;
714	secsize = u;
715
716	error = ioctl(fd, DIOCGMEDIASIZE, &o);
717	if (error == 0) {
718		disksecs = o / u;
719		cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
720	}
721
722	return (disksecs);
723}
724
725
726static int
727read_s0()
728{
729
730	if (read_disk(0, (char *) mboot.bootinst) == -1) {
731		warnx("can't read fdisk partition table");
732		return -1;
733	}
734	if (mboot.signature != PC98_MAGIC) {
735		warnx("invalid fdisk partition table found");
736		/* So should we initialize things */
737		return -1;
738	}
739
740	return 0;
741}
742
743static int
744write_s0()
745{
746
747	if (iotest) {
748		print_s0(-1);
749		return 0;
750	}
751
752	/*
753	 * write enable label sector before write (if necessary),
754	 * disable after writing.
755	 * needed if the disklabel protected area also protects
756	 * sector 0. (e.g. empty disk)
757	 */
758	if (write_disk(0, (char *) mboot.bootinst) == -1) {
759		warn("can't write fdisk partition table");
760		return -1;
761	}
762
763	return(0);
764}
765
766
767static int
768ok(const char *str)
769{
770	printf("%s [n] ", str);
771	fflush(stdout);
772	if (fgets(lbuf, LBUF, stdin) == NULL)
773		exit(1);
774	lbuf[strlen(lbuf)-1] = 0;
775
776	if (*lbuf &&
777		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
778		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
779		return 1;
780	else
781		return 0;
782}
783
784static int
785decimal(const char *str, int *num, int deflt)
786{
787	int acc = 0, c;
788	char *cp;
789
790	while (1) {
791		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
792		fflush(stdout);
793		if (fgets(lbuf, LBUF, stdin) == NULL)
794			exit(1);
795		lbuf[strlen(lbuf)-1] = 0;
796
797		if (!*lbuf)
798			return 0;
799
800		cp = lbuf;
801		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
802		if (!c)
803			return 0;
804		while ((c = *cp++)) {
805			if (c <= '9' && c >= '0')
806				acc = acc * 10 + c - '0';
807			else
808				break;
809		}
810		if (c == ' ' || c == '\t')
811			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
812		if (!c) {
813			*num = acc;
814			return 1;
815		} else
816			printf("%s is an invalid decimal number.  Try again.\n",
817				lbuf);
818	}
819
820}
821
822static int
823string(const char *str, char **ans)
824{
825	int i, c;
826	char *cp = lbuf;
827
828	while (1) {
829		printf("Supply a string value for \"%s\" [%s] ", str, *ans);
830		fgets(lbuf, LBUF, stdin);
831		lbuf[strlen(lbuf)-1] = 0;
832
833		if (!*lbuf)
834			return 0;
835
836		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
837		if (c == '"') {
838			c = *++cp;
839			*ans = cp;
840			while ((c = *cp) && c != '"') cp++;
841		} else {
842			*ans = cp;
843			while ((c = *cp) && c != ' ' && c != '\t') cp++;
844		}
845
846		for (i = strlen(*ans); i < 16; i++)
847			(*ans)[i] = ' ';
848		(*ans)[16] = 0;
849
850		return 1;
851	}
852}
853
854static const char *
855get_type(int type)
856{
857	int	numentries = (sizeof(part_types)/sizeof(struct part_type));
858	int	counter = 0;
859	struct	part_type *ptr = part_types;
860
861
862	while(counter < numentries) {
863		if(ptr->type == (type & 0x7f))
864			return(ptr->name);
865		ptr++;
866		counter++;
867	}
868	return("unknown");
869}
870
871/*
872 * Try figuring out the root device's canonical disk name.
873 * The following choices are considered:
874 *   /dev/ad0s1a     => /dev/ad0
875 *   /dev/da0a       => /dev/da0
876 *   /dev/vinum/root => /dev/vinum/root
877 */
878static char *
879get_rootdisk(void)
880{
881	struct statfs rootfs;
882	regex_t re;
883#define NMATCHES 2
884	regmatch_t rm[NMATCHES];
885	char *s;
886	int rv;
887
888	if (statfs("/", &rootfs) == -1)
889		err(1, "statfs(\"/\")");
890
891	if ((rv = regcomp(&re, "^(/dev/[a-z]+[0-9]+)([sp][0-9]+)?[a-h]?$",
892		    REG_EXTENDED)) != 0)
893		errx(1, "regcomp() failed (%d)", rv);
894	if ((rv = regexec(&re, rootfs.f_mntfromname, NMATCHES, rm, 0)) != 0)
895		errx(1,
896"mounted root fs resource doesn't match expectations (regexec returned %d)",
897		    rv);
898	if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
899		errx(1, "out of memory");
900	memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
901	    rm[1].rm_eo - rm[1].rm_so);
902	s[rm[1].rm_eo - rm[1].rm_so] = 0;
903
904	return s;
905}
906
907static void
908reset_boot(void)
909{
910	int i;
911	struct pc98_partition *partp;
912
913	init_boot();
914	for (i = 1; i <= PC98_NPARTS; i++) {
915		partp = ((struct pc98_partition *) &mboot.parts) + i - 1;
916		bzero((char *)partp, sizeof (struct pc98_partition));
917	}
918}
919