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