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