Home | History | Annotate | Line # | Download | only in fdisk
fdisk.c revision 1.9
      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.9 1995/01/13 10:42:02 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 <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 swap"},
    147 	{0x83, "Linux filesystem"},
    148 	{0x93, "Amoeba filesystem"},
    149 	{0x94, "Amoeba bad block table"},
    150 	{0xA5, "NetBSD or 386BSD"},
    151 	{0xB7, "BSDI BSD/386 filesystem"},
    152 	{0xB8, "BSDI BSD/386 swap"},
    153 	{0xDB, "Concurrent CPM or C.DOS or CTOS"},
    154 	{0xE1, "Speed"},
    155 	{0xE3, "Speed"},
    156 	{0xE4, "Speed"},
    157 	{0xF1, "Speed"},
    158 	{0xF2, "DOS 3.3+ Secondary"},
    159 	{0xF4, "Speed"},
    160 	{0xFF, "BBT (Bad Blocks Table)"},
    161 };
    162 
    163 void	usage __P((void));
    164 void	print_s0 __P((int));
    165 void	print_part __P((int));
    166 void	init_sector0 __P((int));
    167 void	change_part __P((int));
    168 void	print_params __P((void));
    169 void	change_active __P((int));
    170 void	get_params_to_use __P((void));
    171 void	dos __P((int, unsigned char *, unsigned char *, unsigned char *));
    172 int	open_disk __P((int));
    173 int	read_disk __P((int, void *));
    174 int	write_disk __P((int, void *));
    175 int	get_params __P((void));
    176 int	read_s0 __P((void));
    177 int	write_s0 __P((void));
    178 int	yesno __P((char *));
    179 void	decimal __P((char *, int *));
    180 int	type_match __P((const void *, const void *));
    181 char	*get_type __P((int));
    182 
    183 int
    184 main(argc, argv)
    185 	int argc;
    186 	char *argv[];
    187 {
    188 	int ch;
    189 	int part;
    190 
    191 	a_flag = i_flag = u_flag = 0;
    192 	while ((ch = getopt(argc, argv, "0123aiu")) != -1)
    193 		switch (ch) {
    194 		case '0':
    195 			partition = 0;
    196 			break;
    197 		case '1':
    198 			partition = 1;
    199 			break;
    200 		case '2':
    201 			partition = 2;
    202 			break;
    203 		case '3':
    204 			partition = 3;
    205 			break;
    206 		case 'a':
    207 			a_flag = 1;
    208 			break;
    209 		case 'i':
    210 			i_flag = 1;
    211 		case 'u':
    212 			u_flag = 1;
    213 			break;
    214 		default:
    215 			usage();
    216 		}
    217 	argc -= optind;
    218 	argv += optind;
    219 
    220 	if (argc > 0)
    221 		disk = argv[0];
    222 
    223 	if (open_disk(a_flag || i_flag || u_flag) < 0)
    224 		exit(1);
    225 
    226 	printf("******* Working on device %s *******\n", disk);
    227 	if (u_flag)
    228 		get_params_to_use();
    229 	else
    230 		print_params();
    231 
    232 	if (read_s0())
    233 		init_sector0(1);
    234 
    235 	printf("Warning: BIOS sector numbering starts with sector 1\n");
    236 	printf("Information from DOS bootblock is:\n");
    237 	if (partition == -1) {
    238 		for (part = 0; part < NDOSPART; part++)
    239 			change_part(part);
    240 	} else
    241 		change_part(partition);
    242 
    243 	if (u_flag || a_flag)
    244 		change_active(partition);
    245 
    246 	if (u_flag || a_flag) {
    247 		printf("\nWe haven't changed the partition table yet.  ");
    248 		printf("This is your last chance.\n");
    249 		print_s0(-1);
    250 		if (yesno("Should we write new partition table?"))
    251 			write_s0();
    252 	}
    253 
    254 	exit(0);
    255 }
    256 
    257 void
    258 usage()
    259 {
    260 
    261 	(void)fprintf(stderr, "usage: fdisk [-aiu] [-0|-1|-2|-3] [device]\n");
    262 	exit(1);
    263 }
    264 
    265 void
    266 print_s0(which)
    267 	int which;
    268 {
    269 	int part;
    270 
    271 	print_params();
    272 	printf("Information from DOS bootblock is:\n");
    273 	if (which == -1) {
    274 		for (part = 0; part < NDOSPART; part++)
    275 			printf("%d: ", part), print_part(part);
    276 	} else
    277 		print_part(which);
    278 }
    279 
    280 static struct dos_partition mtpart = { 0 };
    281 
    282 void
    283 print_part(part)
    284 	int part;
    285 {
    286 	struct dos_partition *partp;
    287 
    288 	partp = &mboot.parts[part];
    289 	if (!memcmp(partp, &mtpart, sizeof(struct dos_partition))) {
    290 		printf("<UNUSED>\n");
    291 		return;
    292 	}
    293 	printf("sysid %d (%s)\n", partp->dp_typ, get_type(partp->dp_typ));
    294 	printf("    start %d, size %d (%d MB), flag %x\n",
    295 	    partp->dp_start, partp->dp_size,
    296 	    partp->dp_size * 512 / (1024 * 1024), partp->dp_flag);
    297 	printf("\tbeg: cylinder %4d, head %3d, sector %2d\n",
    298 	    DPCYL(partp->dp_scyl, partp->dp_ssect),
    299 	    partp->dp_shd, DPSECT(partp->dp_ssect));
    300 	printf("\tend: cylinder %4d, head %3d, sector %2d\n",
    301 	    DPCYL(partp->dp_ecyl, partp->dp_esect),
    302 	    partp->dp_ehd, DPSECT(partp->dp_esect));
    303 }
    304 
    305 void
    306 init_sector0(start)
    307 	int start;
    308 {
    309 	struct dos_partition *partp;
    310 
    311 	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
    312 	mboot.signature = BOOT_MAGIC;
    313 
    314 	partp = &mboot.parts[3];
    315 	partp->dp_typ = DOSPTYP_386BSD;
    316 	partp->dp_flag = ACTIVE;
    317 	partp->dp_start = start;
    318 	partp->dp_size = disksectors - start;
    319 
    320 	dos(partp->dp_start,
    321 	    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
    322 	dos(partp->dp_start + partp->dp_size - 1,
    323 	    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
    324 }
    325 
    326 void
    327 change_part(part)
    328 	int part;
    329 {
    330 	struct dos_partition *partp;
    331 
    332 	partp = &mboot.parts[part];
    333 
    334 	printf("The data for partition %d is:\n", part);
    335 	print_part(part);
    336 
    337 	if (!u_flag || !yesno("Do you want to change it?"))
    338 		return;
    339 
    340 	if (i_flag) {
    341 		memset(partp, 0, sizeof(*partp));
    342 		if (part == 3) {
    343 			init_sector0(1);
    344 			printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
    345 			print_part(part);
    346 		}
    347 	}
    348 
    349 	do {
    350 		{
    351 			int sysid, start, size;
    352 
    353 			sysid = partp->dp_typ,
    354 			start = partp->dp_start,
    355 			size = partp->dp_size;
    356 			decimal("sysid", &sysid);
    357 			decimal("start", &start);
    358 			decimal("size", &size);
    359 			partp->dp_typ = sysid;
    360 			partp->dp_start = start;
    361 			partp->dp_size = size;
    362 		}
    363 
    364 		if (yesno("Explicitly specify beg/end address?")) {
    365 			int tsector, tcylinder, thead;
    366 
    367 			tcylinder = DPCYL(partp->dp_scyl, partp->dp_ssect);
    368 			thead = partp->dp_shd;
    369 			tsector = DPSECT(partp->dp_ssect);
    370 			decimal("beginning cylinder", &tcylinder);
    371 			decimal("beginning head", &thead);
    372 			decimal("beginning sector", &tsector);
    373 			partp->dp_scyl = DOSCYL(tcylinder);
    374 			partp->dp_shd = thead;
    375 			partp->dp_ssect = DOSSECT(tsector, tcylinder);
    376 
    377 			tcylinder = DPCYL(partp->dp_ecyl, partp->dp_esect);
    378 			thead = partp->dp_ehd;
    379 			tsector = DPSECT(partp->dp_esect);
    380 			decimal("ending cylinder", &tcylinder);
    381 			decimal("ending head", &thead);
    382 			decimal("ending sector", &tsector);
    383 			partp->dp_ecyl = DOSCYL(tcylinder);
    384 			partp->dp_ehd = thead;
    385 			partp->dp_esect = DOSSECT(tsector, tcylinder);
    386 		} else {
    387 			dos(partp->dp_start,
    388 			    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
    389 			dos(partp->dp_start + partp->dp_size - 1,
    390 			    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
    391 		}
    392 
    393 		print_part(part);
    394 	} while (!yesno("Is this entry okay?"));
    395 }
    396 
    397 void
    398 print_params()
    399 {
    400 
    401 	printf("parameters extracted from in-core disklabel are:\n");
    402 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
    403 	    cylinders, heads, sectors, cylindersectors);
    404 	if (dos_sectors > 63 || dos_cylinders > 1023 || dos_heads > 255)
    405 		printf("Figures below won't work with BIOS for partitions not in cylinder 1\n");
    406 	printf("parameters to be used for BIOS calculations are:\n");
    407 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
    408 	    dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors);
    409 }
    410 
    411 void
    412 change_active(which)
    413 	int which;
    414 {
    415 	struct dos_partition *partp;
    416 	int part;
    417 	int active = 3;
    418 
    419 	partp = &mboot.parts[0];
    420 
    421 	if (a_flag && which != -1)
    422 		active = which;
    423 	else {
    424 		for (part = 0; part < NDOSPART; part++)
    425 			if (partp[part].dp_flag & ACTIVE)
    426 				active = part;
    427 	}
    428 	if (yesno("Do you want to change the active partition?")) {
    429 		do {
    430 			decimal("active partition", &active);
    431 		} while (!yesno("Are you happy with this choice?"));
    432 	}
    433 	for (part = 0; part < NDOSPART; part++)
    434 		partp[part].dp_flag &= ~ACTIVE;
    435 	partp[active].dp_flag |= ACTIVE;
    436 }
    437 
    438 void
    439 get_params_to_use()
    440 {
    441 
    442 	print_params();
    443 	if (yesno("Do you want to change our idea of what BIOS thinks?")) {
    444 		do {
    445 			decimal("BIOS's idea of #cylinders", &dos_cylinders);
    446 			decimal("BIOS's idea of #heads", &dos_heads);
    447 			decimal("BIOS's idea of #sectors", &dos_sectors);
    448 			dos_cylindersectors = dos_heads * dos_sectors;
    449 			print_params();
    450 		} while (!yesno("Are you happy with this choice?"));
    451 	}
    452 }
    453 
    454 /***********************************************\
    455 * Change real numbers into strange dos numbers	*
    456 \***********************************************/
    457 void
    458 dos(sector, cylinderp, headp, sectorp)
    459 	int sector;
    460 	unsigned char *cylinderp, *headp, *sectorp;
    461 {
    462 	int cylinder, head;
    463 
    464 	cylinder = sector / dos_cylindersectors;
    465 	sector -= cylinder * dos_cylindersectors;
    466 
    467 	head = sector / dos_sectors;
    468 	sector -= head * dos_sectors;
    469 
    470 	*cylinderp = DOSCYL(cylinder);
    471 	*headp = head;
    472 	*sectorp = DOSSECT(sector + 1, cylinder);
    473 }
    474 
    475 int fd;
    476 
    477 int
    478 open_disk(u_flag)
    479 	int u_flag;
    480 {
    481 	struct stat st;
    482 
    483 	if ((fd = open(disk, u_flag ? O_RDWR : O_RDONLY)) == -1) {
    484 		warn("%s", disk);
    485 		return (-1);
    486 	}
    487 	if (fstat(fd, &st) == -1) {
    488 		close(fd);
    489 		warn("%s", disk);
    490 		return (-1);
    491 	}
    492 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
    493 		close(fd);
    494 		warnx("%s is not a character device or regular file", disk);
    495 		return (-1);
    496 	}
    497 	if (get_params() == -1) {
    498 		close(fd);
    499 		return (-1);
    500 	}
    501 	return (0);
    502 }
    503 
    504 int
    505 read_disk(sector, buf)
    506 	int sector;
    507 	void *buf;
    508 {
    509 
    510 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
    511 		return (-1);
    512 	return (read(fd, buf, 512));
    513 }
    514 
    515 int
    516 write_disk(sector, buf)
    517 	int sector;
    518 	void *buf;
    519 {
    520 
    521 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
    522 		return (-1);
    523 	return (write(fd, buf, 512));
    524 }
    525 
    526 int
    527 get_params()
    528 {
    529 
    530 	if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
    531 		warn("DIOCGDINFO");
    532 		return (-1);
    533 	}
    534 
    535 	dos_cylinders = cylinders = disklabel.d_ncylinders;
    536 	dos_heads = heads = disklabel.d_ntracks;
    537 	dos_sectors = sectors = disklabel.d_nsectors;
    538 	dos_cylindersectors = cylindersectors = heads * sectors;
    539 	disksectors = cylinders * heads * sectors;
    540 
    541 	return (0);
    542 }
    543 
    544 int
    545 read_s0()
    546 {
    547 
    548 	if (read_disk(0, mboot.bootinst) == -1) {
    549 		warn("can't read fdisk partition table");
    550 		return (-1);
    551 	}
    552 	if (mboot.signature != BOOT_MAGIC) {
    553 		warn("invalid fdisk partition table found");
    554 		/* So should we initialize things? */
    555 		return (-1);
    556 	}
    557 	return (0);
    558 }
    559 
    560 int
    561 write_s0()
    562 {
    563 	int flag;
    564 
    565 	/*
    566 	 * write enable label sector before write (if necessary),
    567 	 * disable after writing.
    568 	 * needed if the disklabel protected area also protects
    569 	 * sector 0. (e.g. empty disk)
    570 	 */
    571 	flag = 1;
    572 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
    573 		warn("DIOCWLABEL");
    574 	if (write_disk(0, mboot.bootinst) == -1) {
    575 		warn("can't write fdisk partition table");
    576 		return -1;
    577 	}
    578 	flag = 0;
    579 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
    580 		warn("DIOCWLABEL");
    581 }
    582 
    583 int
    584 yesno(str)
    585 	char *str;
    586 {
    587 	int ch, first;
    588 
    589 	printf("%s [n] ", str);
    590 
    591 	first = ch = getchar();
    592 	while (ch != '\n' && ch != EOF)
    593 		ch = getchar();
    594 	return (first == 'y' || first == 'Y');
    595 }
    596 
    597 void
    598 decimal(str, num)
    599 	char *str;
    600 	int *num;
    601 {
    602 	int acc = 0;
    603 	char *cp;
    604 
    605 	for (;; printf("%s is not a valid decimal number.\n", lbuf)) {
    606 		printf("Supply a decimal value for \"%s\" [%d] ", str, *num);
    607 
    608 		fgets(lbuf, LBUF, stdin);
    609 		lbuf[strlen(lbuf)-1] = '\0';
    610 		cp = lbuf;
    611 
    612 		cp += strspn(cp, " \t");
    613 		if (*cp == '\0')
    614 			return;
    615 
    616 		if (!isdigit(*cp))
    617 			continue;
    618 		acc = strtol(lbuf, &cp, 10);
    619 
    620 		cp += strspn(cp, " \t");
    621 		if (*cp != '\0')
    622 			continue;
    623 
    624 		*num = acc;
    625 		return;
    626 	}
    627 
    628 }
    629 
    630 int
    631 type_match(key, item)
    632 	const void *key, *item;
    633 {
    634 	const int *typep = key;
    635 	const struct part_type *ptr = item;
    636 
    637 	if (*typep < ptr->type)
    638 		return (-1);
    639 	if (*typep > ptr->type)
    640 		return (1);
    641 	return (0);
    642 }
    643 
    644 char *
    645 get_type(type)
    646 	int type;
    647 {
    648 	struct part_type *ptr;
    649 
    650 	ptr = bsearch(&type, part_types,
    651 	    sizeof(part_types) / sizeof(struct part_type),
    652 	    sizeof(struct part_type), type_match);
    653 	if (ptr == 0)
    654 		return ("unknown");
    655 	else
    656 		return (ptr->name);
    657 }
    658