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