Home | History | Annotate | Line # | Download | only in fdisk
fdisk.c revision 1.10
      1 /*	$NetBSD: fdisk.c,v 1.10 1995/03/18 14:55:36 cgd Exp $	*/
      2 
      3 /*
      4  * Mach Operating System
      5  * Copyright (c) 1992 Carnegie Mellon University
      6  * All Rights Reserved.
      7  *
      8  * Permission to use, copy, modify and distribute this software and its
      9  * documentation is hereby granted, provided that both the copyright
     10  * notice and this permission notice appear in all copies of the
     11  * software, derivative works or modified versions, and any portions
     12  * thereof, and that both notices appear in supporting documentation.
     13  *
     14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     17  *
     18  * Carnegie Mellon requests users of this software to return to
     19  *
     20  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     21  *  School of Computer Science
     22  *  Carnegie Mellon University
     23  *  Pittsburgh PA 15213-3890
     24  *
     25  * any improvements or extensions that they make and grant Carnegie Mellon
     26  * the rights to redistribute these changes.
     27  */
     28 
     29 #ifndef lint
     30 static char rcsid[] = "$NetBSD: fdisk.c,v 1.10 1995/03/18 14:55:36 cgd Exp $";
     31 #endif /* not lint */
     32 
     33 #include <sys/types.h>
     34 #include <sys/disklabel.h>
     35 #include <sys/ioctl.h>
     36 #include <sys/stat.h>
     37 
     38 #include <ctype.h>
     39 #include <err.h>
     40 #include <fcntl.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <unistd.h>
     45 
     46 #define LBUF 100
     47 static char lbuf[LBUF];
     48 
     49 /*
     50  * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
     51  *	Copyright (c) 1989	Robert. V. Baron
     52  *	Created.
     53  */
     54 
     55 char *disk = "/dev/rwd0d";
     56 
     57 struct disklabel disklabel;		/* disk parameters */
     58 
     59 int cylinders, sectors, heads, cylindersectors, disksectors;
     60 
     61 struct mboot {
     62 	unsigned char padding[2]; /* force the longs to be long alligned */
     63 	unsigned char bootinst[DOSPARTOFF];
     64 	struct	dos_partition parts[4];
     65 	unsigned short int	signature;
     66 };
     67 struct mboot mboot;
     68 
     69 #define ACTIVE 0x80
     70 #define BOOT_MAGIC 0xAA55
     71 
     72 int dos_cylinders;
     73 int dos_heads;
     74 int dos_sectors;
     75 int dos_cylindersectors;
     76 
     77 #define DOSSECT(s,c)	(((s) & 0x3f) | (((c) >> 2) & 0xc0))
     78 #define DOSCYL(c)	((c) & 0xff)
     79 int partition = -1;
     80 
     81 int a_flag;		/* set active partition */
     82 int i_flag;		/* replace partition data */
     83 int u_flag;		/* update partition data */
     84 
     85 unsigned char bootcode[] = {
     86 0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
     87 0x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
     88 0xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
     89 0x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
     90 0x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
     91 0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
     92 0x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
     93 0x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
     94 0xeb, 0xf4, 0xfb, 0xeb, 0xfe,
     95 'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
     96 	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
     97 'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
     98 	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
     99 'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
    100 	'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
    101 'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
    102 	'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
    103 
    104   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    105   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    106   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    107   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    108   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    109   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    110   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    111   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    112   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    113   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    114   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    115   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    116   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
    117 };
    118 
    119 struct part_type {
    121 	int type;
    122 	char *name;
    123 } part_types[] = {
    124 	{0x00, "unused"},
    125 	{0x01, "Primary DOS with 12 bit FAT"},
    126 	{0x02, "XENIX / filesystem"},
    127 	{0x03, "XENIX /usr filesystem"},
    128 	{0x04, "Primary DOS with 16 bit FAT"},
    129 	{0x05, "Extended DOS"},
    130 	{0x06, "Primary 'big' DOS (> 32MB)"},
    131 	{0x07, "OS/2 HPFS, QNX or Advanced UNIX"},
    132 	{0x08, "AIX filesystem"},
    133 	{0x09, "AIX boot partition or Coherent"},
    134 	{0x0A, "OS/2 Boot Manager or OPUS"},
    135 	{0x10, "OPUS"},
    136 	{0x40, "VENIX 286"},
    137 	{0x50, "DM"},
    138 	{0x51, "DM"},
    139 	{0x52, "CP/M or Microport SysV/AT"},
    140 	{0x56, "GB"},
    141 	{0x61, "Speed"},
    142 	{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"},
    143 	{0x64, "Novell Netware 2.xx"},
    144 	{0x65, "Novell Netware 3.xx"},
    145 	{0x75, "PCIX"},
    146 	{0x80, "Minix 1.1 ... 1.4a"},
    147 	{0x81, "Minix 1.4b ... 1.5.10"},
    148 	{0x82, "Linux swap"},
    149 	{0x83, "Linux filesystem"},
    150 	{0x93, "Amoeba filesystem"},
    151 	{0x94, "Amoeba bad block table"},
    152 	{0xA5, "NetBSD or 386BSD"},
    153 	{0xB7, "BSDI BSD/386 filesystem"},
    154 	{0xB8, "BSDI BSD/386 swap"},
    155 	{0xDB, "Concurrent CPM or C.DOS or CTOS"},
    156 	{0xE1, "Speed"},
    157 	{0xE3, "Speed"},
    158 	{0xE4, "Speed"},
    159 	{0xF1, "Speed"},
    160 	{0xF2, "DOS 3.3+ Secondary"},
    161 	{0xF4, "Speed"},
    162 	{0xFF, "BBT (Bad Blocks Table)"},
    163 };
    164 
    165 void	usage __P((void));
    166 void	print_s0 __P((int));
    167 void	print_part __P((int));
    168 void	init_sector0 __P((int));
    169 void	change_part __P((int));
    170 void	print_params __P((void));
    171 void	change_active __P((int));
    172 void	get_params_to_use __P((void));
    173 void	dos __P((int, unsigned char *, unsigned char *, unsigned char *));
    174 int	open_disk __P((int));
    175 int	read_disk __P((int, void *));
    176 int	write_disk __P((int, void *));
    177 int	get_params __P((void));
    178 int	read_s0 __P((void));
    179 int	write_s0 __P((void));
    180 int	yesno __P((char *));
    181 void	decimal __P((char *, int *));
    182 int	type_match __P((const void *, const void *));
    183 char	*get_type __P((int));
    184 
    185 int
    186 main(argc, argv)
    187 	int argc;
    188 	char *argv[];
    189 {
    190 	int ch;
    191 	int part;
    192 
    193 	a_flag = i_flag = u_flag = 0;
    194 	while ((ch = getopt(argc, argv, "0123aiu")) != -1)
    195 		switch (ch) {
    196 		case '0':
    197 			partition = 0;
    198 			break;
    199 		case '1':
    200 			partition = 1;
    201 			break;
    202 		case '2':
    203 			partition = 2;
    204 			break;
    205 		case '3':
    206 			partition = 3;
    207 			break;
    208 		case 'a':
    209 			a_flag = 1;
    210 			break;
    211 		case 'i':
    212 			i_flag = 1;
    213 		case 'u':
    214 			u_flag = 1;
    215 			break;
    216 		default:
    217 			usage();
    218 		}
    219 	argc -= optind;
    220 	argv += optind;
    221 
    222 	if (argc > 0)
    223 		disk = argv[0];
    224 
    225 	if (open_disk(a_flag || i_flag || u_flag) < 0)
    226 		exit(1);
    227 
    228 	printf("******* Working on device %s *******\n", disk);
    229 	if (u_flag)
    230 		get_params_to_use();
    231 	else
    232 		print_params();
    233 
    234 	if (read_s0())
    235 		init_sector0(1);
    236 
    237 	printf("Warning: BIOS sector numbering starts with sector 1\n");
    238 	printf("Information from DOS bootblock is:\n");
    239 	if (partition == -1) {
    240 		for (part = 0; part < NDOSPART; part++)
    241 			change_part(part);
    242 	} else
    243 		change_part(partition);
    244 
    245 	if (u_flag || a_flag)
    246 		change_active(partition);
    247 
    248 	if (u_flag || a_flag) {
    249 		printf("\nWe haven't changed the partition table yet.  ");
    250 		printf("This is your last chance.\n");
    251 		print_s0(-1);
    252 		if (yesno("Should we write new partition table?"))
    253 			write_s0();
    254 	}
    255 
    256 	exit(0);
    257 }
    258 
    259 void
    260 usage()
    261 {
    262 
    263 	(void)fprintf(stderr, "usage: fdisk [-aiu] [-0|-1|-2|-3] [device]\n");
    264 	exit(1);
    265 }
    266 
    267 void
    268 print_s0(which)
    269 	int which;
    270 {
    271 	int part;
    272 
    273 	print_params();
    274 	printf("Information from DOS bootblock is:\n");
    275 	if (which == -1) {
    276 		for (part = 0; part < NDOSPART; part++)
    277 			printf("%d: ", part), print_part(part);
    278 	} else
    279 		print_part(which);
    280 }
    281 
    282 static struct dos_partition mtpart = { 0 };
    283 
    284 void
    285 print_part(part)
    286 	int part;
    287 {
    288 	struct dos_partition *partp;
    289 
    290 	partp = &mboot.parts[part];
    291 	if (!memcmp(partp, &mtpart, sizeof(struct dos_partition))) {
    292 		printf("<UNUSED>\n");
    293 		return;
    294 	}
    295 	printf("sysid %d (%s)\n", partp->dp_typ, get_type(partp->dp_typ));
    296 	printf("    start %d, size %d (%d MB), flag %x\n",
    297 	    partp->dp_start, partp->dp_size,
    298 	    partp->dp_size * 512 / (1024 * 1024), partp->dp_flag);
    299 	printf("\tbeg: cylinder %4d, head %3d, sector %2d\n",
    300 	    DPCYL(partp->dp_scyl, partp->dp_ssect),
    301 	    partp->dp_shd, DPSECT(partp->dp_ssect));
    302 	printf("\tend: cylinder %4d, head %3d, sector %2d\n",
    303 	    DPCYL(partp->dp_ecyl, partp->dp_esect),
    304 	    partp->dp_ehd, DPSECT(partp->dp_esect));
    305 }
    306 
    307 void
    308 init_sector0(start)
    309 	int start;
    310 {
    311 	struct dos_partition *partp;
    312 
    313 	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
    314 	mboot.signature = BOOT_MAGIC;
    315 
    316 	partp = &mboot.parts[3];
    317 	partp->dp_typ = DOSPTYP_386BSD;
    318 	partp->dp_flag = ACTIVE;
    319 	partp->dp_start = start;
    320 	partp->dp_size = disksectors - start;
    321 
    322 	dos(partp->dp_start,
    323 	    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
    324 	dos(partp->dp_start + partp->dp_size - 1,
    325 	    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
    326 }
    327 
    328 void
    329 change_part(part)
    330 	int part;
    331 {
    332 	struct dos_partition *partp;
    333 
    334 	partp = &mboot.parts[part];
    335 
    336 	printf("The data for partition %d is:\n", part);
    337 	print_part(part);
    338 
    339 	if (!u_flag || !yesno("Do you want to change it?"))
    340 		return;
    341 
    342 	if (i_flag) {
    343 		memset(partp, 0, sizeof(*partp));
    344 		if (part == 3) {
    345 			init_sector0(1);
    346 			printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
    347 			print_part(part);
    348 		}
    349 	}
    350 
    351 	do {
    352 		{
    353 			int sysid, start, size;
    354 
    355 			sysid = partp->dp_typ,
    356 			start = partp->dp_start,
    357 			size = partp->dp_size;
    358 			decimal("sysid", &sysid);
    359 			decimal("start", &start);
    360 			decimal("size", &size);
    361 			partp->dp_typ = sysid;
    362 			partp->dp_start = start;
    363 			partp->dp_size = size;
    364 		}
    365 
    366 		if (yesno("Explicitly specify beg/end address?")) {
    367 			int tsector, tcylinder, thead;
    368 
    369 			tcylinder = DPCYL(partp->dp_scyl, partp->dp_ssect);
    370 			thead = partp->dp_shd;
    371 			tsector = DPSECT(partp->dp_ssect);
    372 			decimal("beginning cylinder", &tcylinder);
    373 			decimal("beginning head", &thead);
    374 			decimal("beginning sector", &tsector);
    375 			partp->dp_scyl = DOSCYL(tcylinder);
    376 			partp->dp_shd = thead;
    377 			partp->dp_ssect = DOSSECT(tsector, tcylinder);
    378 
    379 			tcylinder = DPCYL(partp->dp_ecyl, partp->dp_esect);
    380 			thead = partp->dp_ehd;
    381 			tsector = DPSECT(partp->dp_esect);
    382 			decimal("ending cylinder", &tcylinder);
    383 			decimal("ending head", &thead);
    384 			decimal("ending sector", &tsector);
    385 			partp->dp_ecyl = DOSCYL(tcylinder);
    386 			partp->dp_ehd = thead;
    387 			partp->dp_esect = DOSSECT(tsector, tcylinder);
    388 		} else {
    389 			dos(partp->dp_start,
    390 			    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
    391 			dos(partp->dp_start + partp->dp_size - 1,
    392 			    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
    393 		}
    394 
    395 		print_part(part);
    396 	} while (!yesno("Is this entry okay?"));
    397 }
    398 
    399 void
    400 print_params()
    401 {
    402 
    403 	printf("parameters extracted from in-core disklabel are:\n");
    404 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
    405 	    cylinders, heads, sectors, cylindersectors);
    406 	if (dos_sectors > 63 || dos_cylinders > 1023 || dos_heads > 255)
    407 		printf("Figures below won't work with BIOS for partitions not in cylinder 1\n");
    408 	printf("parameters to be used for BIOS calculations are:\n");
    409 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
    410 	    dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors);
    411 }
    412 
    413 void
    414 change_active(which)
    415 	int which;
    416 {
    417 	struct dos_partition *partp;
    418 	int part;
    419 	int active = 3;
    420 
    421 	partp = &mboot.parts[0];
    422 
    423 	if (a_flag && which != -1)
    424 		active = which;
    425 	else {
    426 		for (part = 0; part < NDOSPART; part++)
    427 			if (partp[part].dp_flag & ACTIVE)
    428 				active = part;
    429 	}
    430 	if (yesno("Do you want to change the active partition?")) {
    431 		do {
    432 			decimal("active partition", &active);
    433 		} while (!yesno("Are you happy with this choice?"));
    434 	}
    435 	for (part = 0; part < NDOSPART; part++)
    436 		partp[part].dp_flag &= ~ACTIVE;
    437 	partp[active].dp_flag |= ACTIVE;
    438 }
    439 
    440 void
    441 get_params_to_use()
    442 {
    443 
    444 	print_params();
    445 	if (yesno("Do you want to change our idea of what BIOS thinks?")) {
    446 		do {
    447 			decimal("BIOS's idea of #cylinders", &dos_cylinders);
    448 			decimal("BIOS's idea of #heads", &dos_heads);
    449 			decimal("BIOS's idea of #sectors", &dos_sectors);
    450 			dos_cylindersectors = dos_heads * dos_sectors;
    451 			print_params();
    452 		} while (!yesno("Are you happy with this choice?"));
    453 	}
    454 }
    455 
    456 /***********************************************\
    457 * Change real numbers into strange dos numbers	*
    458 \***********************************************/
    459 void
    460 dos(sector, cylinderp, headp, sectorp)
    461 	int sector;
    462 	unsigned char *cylinderp, *headp, *sectorp;
    463 {
    464 	int cylinder, head;
    465 
    466 	cylinder = sector / dos_cylindersectors;
    467 	sector -= cylinder * dos_cylindersectors;
    468 
    469 	head = sector / dos_sectors;
    470 	sector -= head * dos_sectors;
    471 
    472 	*cylinderp = DOSCYL(cylinder);
    473 	*headp = head;
    474 	*sectorp = DOSSECT(sector + 1, cylinder);
    475 }
    476 
    477 int fd;
    478 
    479 int
    480 open_disk(u_flag)
    481 	int u_flag;
    482 {
    483 	struct stat st;
    484 
    485 	if ((fd = open(disk, u_flag ? O_RDWR : O_RDONLY)) == -1) {
    486 		warn("%s", disk);
    487 		return (-1);
    488 	}
    489 	if (fstat(fd, &st) == -1) {
    490 		close(fd);
    491 		warn("%s", disk);
    492 		return (-1);
    493 	}
    494 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
    495 		close(fd);
    496 		warnx("%s is not a character device or regular file", disk);
    497 		return (-1);
    498 	}
    499 	if (get_params() == -1) {
    500 		close(fd);
    501 		return (-1);
    502 	}
    503 	return (0);
    504 }
    505 
    506 int
    507 read_disk(sector, buf)
    508 	int sector;
    509 	void *buf;
    510 {
    511 
    512 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
    513 		return (-1);
    514 	return (read(fd, buf, 512));
    515 }
    516 
    517 int
    518 write_disk(sector, buf)
    519 	int sector;
    520 	void *buf;
    521 {
    522 
    523 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
    524 		return (-1);
    525 	return (write(fd, buf, 512));
    526 }
    527 
    528 int
    529 get_params()
    530 {
    531 
    532 	if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
    533 		warn("DIOCGDINFO");
    534 		return (-1);
    535 	}
    536 
    537 	dos_cylinders = cylinders = disklabel.d_ncylinders;
    538 	dos_heads = heads = disklabel.d_ntracks;
    539 	dos_sectors = sectors = disklabel.d_nsectors;
    540 	dos_cylindersectors = cylindersectors = heads * sectors;
    541 	disksectors = cylinders * heads * sectors;
    542 
    543 	return (0);
    544 }
    545 
    546 int
    547 read_s0()
    548 {
    549 
    550 	if (read_disk(0, mboot.bootinst) == -1) {
    551 		warn("can't read fdisk partition table");
    552 		return (-1);
    553 	}
    554 	if (mboot.signature != BOOT_MAGIC) {
    555 		warn("invalid fdisk partition table found");
    556 		/* So should we initialize things? */
    557 		return (-1);
    558 	}
    559 	return (0);
    560 }
    561 
    562 int
    563 write_s0()
    564 {
    565 	int flag;
    566 
    567 	/*
    568 	 * write enable label sector before write (if necessary),
    569 	 * disable after writing.
    570 	 * needed if the disklabel protected area also protects
    571 	 * sector 0. (e.g. empty disk)
    572 	 */
    573 	flag = 1;
    574 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
    575 		warn("DIOCWLABEL");
    576 	if (write_disk(0, mboot.bootinst) == -1) {
    577 		warn("can't write fdisk partition table");
    578 		return -1;
    579 	}
    580 	flag = 0;
    581 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
    582 		warn("DIOCWLABEL");
    583 }
    584 
    585 int
    586 yesno(str)
    587 	char *str;
    588 {
    589 	int ch, first;
    590 
    591 	printf("%s [n] ", str);
    592 
    593 	first = ch = getchar();
    594 	while (ch != '\n' && ch != EOF)
    595 		ch = getchar();
    596 	return (first == 'y' || first == 'Y');
    597 }
    598 
    599 void
    600 decimal(str, num)
    601 	char *str;
    602 	int *num;
    603 {
    604 	int acc = 0;
    605 	char *cp;
    606 
    607 	for (;; printf("%s is not a valid decimal number.\n", lbuf)) {
    608 		printf("Supply a decimal value for \"%s\" [%d] ", str, *num);
    609 
    610 		fgets(lbuf, LBUF, stdin);
    611 		lbuf[strlen(lbuf)-1] = '\0';
    612 		cp = lbuf;
    613 
    614 		cp += strspn(cp, " \t");
    615 		if (*cp == '\0')
    616 			return;
    617 
    618 		if (!isdigit(*cp))
    619 			continue;
    620 		acc = strtol(lbuf, &cp, 10);
    621 
    622 		cp += strspn(cp, " \t");
    623 		if (*cp != '\0')
    624 			continue;
    625 
    626 		*num = acc;
    627 		return;
    628 	}
    629 
    630 }
    631 
    632 int
    633 type_match(key, item)
    634 	const void *key, *item;
    635 {
    636 	const int *typep = key;
    637 	const struct part_type *ptr = item;
    638 
    639 	if (*typep < ptr->type)
    640 		return (-1);
    641 	if (*typep > ptr->type)
    642 		return (1);
    643 	return (0);
    644 }
    645 
    646 char *
    647 get_type(type)
    648 	int type;
    649 {
    650 	struct part_type *ptr;
    651 
    652 	ptr = bsearch(&type, part_types,
    653 	    sizeof(part_types) / sizeof(struct part_type),
    654 	    sizeof(struct part_type), type_match);
    655 	if (ptr == 0)
    656 		return ("unknown");
    657 	else
    658 		return (ptr->name);
    659 }
    660