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