fdisk.c revision 8871
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
34int iotest;
35
36#define LBUF 100
37static 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
56char *disk = "/dev/rwd0d";
57char *name;
58
59struct disklabel disklabel;		/* disk parameters */
60
61int cyls, sectors, heads, cylsecs, disksecs;
62
63struct 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};
70struct mboot mboot;
71
72#define ACTIVE 0x80
73#define BOOT_MAGIC 0xAA55
74
75int dos_cyls;
76int dos_heads;
77int dos_sectors;
78int dos_cylsecs;
79
80#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
81#define DOSCYL(c)	(c & 0xff)
82static int dos();
83char *get_type();
84static int partition = -1;
85
86
87static int a_flag  = 0;		/* set active partition */
88static int i_flag  = 0;		/* replace partition data */
89static int u_flag  = 0;		/* update partition data */
90
91static unsigned char bootcode[] = {
920x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
930x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
940xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
950x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
960x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
970x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
980x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
990x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
1000xeb, 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
125struct 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 swap"}
156	,{0x83, "Linux filesystem"}
157	,{0x93, "Amoeba filesystem"}
158	,{0x94, "Amoeba bad block table"}
159	,{0xA5, "FreeBSD/NetBSD/386BSD"}
160	,{0xA7, "NEXTSTEP"}
161	,{0xB7, "BSDI BSD/386 filesystem"}
162	,{0xB8, "BSDI BSD/386 swap"}
163	,{0xDB, "Concurrent CPM or C.DOS or CTOS"}
164	,{0xE1, "Speed"}
165	,{0xE3, "Speed"}
166	,{0xE4, "Speed"}
167	,{0xF1, "Speed"}
168	,{0xF2, "DOS 3.3+ Secondary"}
169	,{0xF4, "Speed"}
170	,{0xFF, "BBT (Bad Blocks Table)"}
171};
172
173
174main(argc, argv)
175char **argv;
176{
177int	i;
178
179	name = *argv;
180	{register char *cp = name;
181		while (*cp) if (*cp++ == '/') name = cp;
182	}
183
184	for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
185		if (*token++ != '-' || !*token)
186			break;
187		else { register int flag;
188			for ( ; flag = *token++ ; ) {
189				switch (flag) {
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					goto usage;
212				}
213			}
214		}
215	}
216
217	if (argc > 0)
218		disk = argv[0];
219
220	if (open_disk(u_flag) < 0)
221		exit(1);
222
223	printf("******* Working on device %s *******\n",disk);
224	if(u_flag)
225	{
226		get_params_to_use();
227	}
228	else
229	{
230		print_params();
231	}
232
233	if (read_s0())
234		init_sector0(1);
235
236	printf("Warning: BIOS sector numbering starts with sector 1\n");
237	printf("Information from DOS bootblock is:\n");
238	if (partition == -1)
239		for (i = 0; i < NDOSPART; i++)
240			change_part(i);
241	else
242		change_part(partition);
243
244	if (u_flag || a_flag)
245		change_active(partition);
246
247	if (u_flag || a_flag) {
248		printf("\nWe haven't changed the partition table yet.  ");
249		printf("This is your last chance.\n");
250		print_s0(-1);
251		if (ok("Should we write new partition table?"))
252			write_s0();
253	}
254
255	exit(0);
256
257usage:
258	printf("fdisk {-a|-i|-r} {disk}\n");
259}
260
261print_s0(which)
262{
263int	i;
264
265	print_params();
266	printf("Information from DOS bootblock is:\n");
267	if (which == -1)
268		for (i = 0; i < NDOSPART; i++)
269			printf("%d: ", i), print_part(i);
270	else
271		print_part(which);
272}
273
274static struct dos_partition mtpart = { 0 };
275
276print_part(i)
277{
278struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
279
280
281	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
282		printf("<UNUSED>\n");
283		return;
284	}
285	printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
286	printf("    start %d, size %d (%d Meg), flag %x\n",
287		partp->dp_start,
288		partp->dp_size, partp->dp_size * 512 / (1024 * 1024),
289		partp->dp_flag);
290	printf("\tbeg: cyl %d/ sector %d/ head %d;\n\tend: cyl %d/ sector %d/ head %d\n"
291		,DPCYL(partp->dp_scyl, partp->dp_ssect)
292		,DPSECT(partp->dp_ssect)
293		,partp->dp_shd
294		,DPCYL(partp->dp_ecyl, partp->dp_esect)
295		,DPSECT(partp->dp_esect)
296		,partp->dp_ehd);
297}
298
299init_sector0(start)
300{
301struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
302int size = disksecs - start;
303int rest;
304
305	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
306	mboot.signature = BOOT_MAGIC;
307
308	partp->dp_typ = DOSPTYP_386BSD;
309	partp->dp_flag = ACTIVE;
310	partp->dp_start = start;
311	partp->dp_size = size;
312
313	dos(partp->dp_start, &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
314	dos(partp->dp_start+partp->dp_size, &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
315}
316
317change_part(i)
318{
319struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
320
321    printf("The data for partition %d is:\n", i);
322    print_part(i);
323
324    if (u_flag && ok("Do you want to change it?")) {
325	int tmp;
326
327	if (i_flag) {
328		bzero((char *)partp, sizeof (struct dos_partition));
329		if (i == 3) {
330			init_sector0(1);
331			printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
332			print_part(i);
333		}
334	}
335
336	do {
337		Decimal("sysid", partp->dp_typ, tmp);
338		Decimal("start", partp->dp_start, tmp);
339		Decimal("size", partp->dp_size, tmp);
340
341		if (ok("Explicitly specifiy beg/end address ?"))
342		{
343			int	tsec,tcyl,thd;
344			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
345			thd = partp->dp_shd;
346			tsec = DPSECT(partp->dp_ssect);
347			Decimal("beginning cylinder", tcyl, tmp);
348			Decimal("beginning head", thd, tmp);
349			Decimal("beginning sector", tsec, tmp);
350			partp->dp_scyl = DOSCYL(tcyl);
351			partp->dp_ssect = DOSSECT(tsec,tcyl);
352			partp->dp_shd = thd;
353
354			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
355			thd = partp->dp_ehd;
356			tsec = DPSECT(partp->dp_esect);
357			Decimal("ending cylinder", tcyl, tmp);
358			Decimal("ending head", thd, tmp);
359			Decimal("ending sector", tsec, tmp);
360			partp->dp_ecyl = DOSCYL(tcyl);
361			partp->dp_esect = DOSSECT(tsec,tcyl);
362			partp->dp_ehd = thd;
363		} else {
364			dos(partp->dp_start,
365				&partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
366			dos(partp->dp_start+partp->dp_size - 1,
367				&partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
368		}
369
370		print_part(i);
371	} while (!ok("Are we happy with this entry?"));
372    }
373}
374
375print_params()
376{
377	printf("parameters extracted from in-core disklabel are:\n");
378	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
379			,cyls,heads,sectors,cylsecs);
380	if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
381		printf(" Figures below won't work with BIOS for partitions not in cyl 1\n");
382	printf("parameters to be used for BIOS calculations are:\n");
383	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
384		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
385}
386
387change_active(which)
388{
389int i;
390int active = 3, tmp;
391struct dos_partition *partp = ((struct dos_partition *) &mboot.parts);
392
393	if (a_flag && which != -1)
394		active = which;
395	if (!ok("Do you want to change the active partition?"))
396		return;
397	do
398		Decimal("active partition", active, tmp);
399	while (!ok("Are you happy with this choice"));
400	for (i = 0; i < NDOSPART; i++)
401		partp[i].dp_flag = 0;
402	if (active >= 0 && active < NDOSPART)
403		partp[active].dp_flag = ACTIVE;
404}
405
406get_params_to_use()
407{
408	int	tmp;
409	print_params();
410	if (ok("Do you want to change our idea of what BIOS thinks ?"))
411	{
412		do
413		{
414			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
415			Decimal("BIOS's idea of #heads", dos_heads, tmp);
416			Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
417			dos_cylsecs = dos_heads * dos_sectors;
418			print_params();
419		}
420		while(!ok("Are you happy with this choice"));
421	}
422}
423
424/***********************************************\
425* Change real numbers into strange dos numbers	*
426\***********************************************/
427static
428dos(sec, c, s, h)
429int sec;
430unsigned char *c, *s, *h;
431{
432int cy;
433int hd;
434
435	if (sec == 0) {
436		*s = *c = *h = 0;
437		return;
438	}
439
440	cy = sec / ( dos_cylsecs );
441	sec = sec - cy * ( dos_cylsecs );
442
443	hd = sec / dos_sectors;
444	sec = (sec - hd * dos_sectors) + 1;
445
446	*h = hd;
447	*c = cy & 0xff;
448	*s = (sec & 0x3f) | ( (cy & 0x300) >> 2);
449}
450
451int fd;
452
453	/* Getting device status */
454
455open_disk(u_flag)
456{
457struct stat 	st;
458
459	if (stat(disk, &st) == -1) {
460		fprintf(stderr, "%s: Can't get file status of %s\n",
461			name, disk);
462		return -1;
463	}
464	if ( !(st.st_mode & S_IFCHR) )
465		fprintf(stderr,"%s: Device %s is not character special\n",
466			name, disk);
467	if ((fd = open(disk, a_flag || u_flag ? O_RDWR : O_RDONLY)) == -1) {
468		fprintf(stderr,"%s: Can't open device %s\n", name, disk);
469		return -1;
470	}
471	if (get_params(0) == -1) {
472		fprintf(stderr, "%s: Can't get disk parameters on %s\n",
473			name, disk);
474		return -1;
475	}
476	return fd;
477}
478
479
480read_disk(sector, buf)
481{
482	lseek(fd,(sector * 512), 0);
483	return read(fd, buf, 512);
484}
485
486write_disk(sector, buf)
487{
488	lseek(fd,(sector * 512), 0);
489	return write(fd, buf, 512);
490}
491
492get_params(verbose)
493{
494
495    if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
496	fprintf(stderr,
497		"%s: Can't get disk parameters on %s; supplying dummy ones\n",
498		name, disk);
499	dos_cyls = cyls = 1;
500	dos_heads = heads = 1;
501	dos_sectors = sectors = 1;
502	dos_cylsecs = cylsecs = heads * sectors;
503	disksecs = cyls * heads * sectors;
504	return disksecs;
505    }
506
507    dos_cyls = cyls = disklabel.d_ncylinders;
508    dos_heads = heads = disklabel.d_ntracks;
509    dos_sectors = sectors = disklabel.d_nsectors;
510    dos_cylsecs = cylsecs = heads * sectors;
511    disksecs = cyls * heads * sectors;
512
513    return (disksecs);
514}
515
516
517read_s0()
518{
519	if (read_disk(0, (char *) mboot.bootinst) == -1) {
520		fprintf(stderr, "%s: Can't read fdisk partition table\n", name);
521		return -1;
522	}
523	if (mboot.signature != BOOT_MAGIC) {
524		fprintf(stderr, "%s: Invalid fdisk partition table found\n",
525			name);
526		/* So should we initialize things */
527		return -1;
528	}
529	return 0;
530}
531
532write_s0()
533{
534	int	flag;
535	if (iotest) {
536		print_s0(-1);
537		return 0;
538	}
539	/*
540	 * write enable label sector before write (if necessary),
541	 * disable after writing.
542	 * needed if the disklabel protected area also protects
543	 * sector 0. (e.g. empty disk)
544	 */
545	flag = 1;
546	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
547		perror("ioctl DIOCWLABEL");
548	if (write_disk(0, (char *) mboot.bootinst) == -1) {
549		fprintf(stderr, "%s: Can't write fdisk partition table\n",
550			name);
551		return -1;
552	flag = 0;
553	(void) ioctl(fd, DIOCWLABEL, &flag);
554	}
555}
556
557
558
559ok(str)
560char *str;
561{
562	printf("%s [n] ", str);
563	fgets(lbuf, LBUF, stdin);
564	lbuf[strlen(lbuf)-1] = 0;
565
566	if (*lbuf &&
567		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
568		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
569		return 1;
570	else
571		return 0;
572}
573
574decimal(str, num, deflt)
575char *str;
576int *num;
577{
578int acc = 0, c;
579char *cp;
580
581	while (1) {
582		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
583		fgets(lbuf, LBUF, stdin);
584		lbuf[strlen(lbuf)-1] = 0;
585
586		if (!*lbuf)
587			return 0;
588
589		cp = lbuf;
590		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
591		if (!c)
592			return 0;
593		while (c = *cp++) {
594			if (c <= '9' && c >= '0')
595				acc = acc * 10 + c - '0';
596			else
597				break;
598		}
599		if (c == ' ' || c == '\t')
600			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
601		if (!c) {
602			*num = acc;
603			return 1;
604		} else
605			printf("%s is an invalid decimal number.  Try again\n",
606				lbuf);
607	}
608
609}
610
611hex(str, num, deflt)
612char *str;
613int *num;
614{
615int acc = 0, c;
616char *cp;
617
618	while (1) {
619		printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
620		fgets(lbuf, LBUF, stdin);
621		lbuf[strlen(lbuf)-1] = 0;
622
623		if (!*lbuf)
624			return 0;
625
626		cp = lbuf;
627		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
628		if (!c)
629			return 0;
630		while (c = *cp++) {
631			if (c <= '9' && c >= '0')
632				acc = (acc << 4) + c - '0';
633			else if (c <= 'f' && c >= 'a')
634				acc = (acc << 4) + c - 'a' + 10;
635			else if (c <= 'F' && c >= 'A')
636				acc = (acc << 4) + c - 'A' + 10;
637			else
638				break;
639		}
640		if (c == ' ' || c == '\t')
641			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
642		if (!c) {
643			*num = acc;
644			return 1;
645		} else
646			printf("%s is an invalid hex number.  Try again\n",
647				lbuf);
648	}
649
650}
651
652string(str, ans)
653char *str;
654char **ans;
655{
656int c;
657char *cp = lbuf;
658
659	while (1) {
660		printf("Supply a string value for \"%s\" [%s] ", str, *ans);
661		fgets(lbuf, LBUF, stdin);
662		lbuf[strlen(lbuf)-1] = 0;
663
664		if (!*lbuf)
665			return 0;
666
667		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
668		if (c == '"') {
669			c = *++cp;
670			*ans = cp;
671			while ((c = *cp) && c != '"') cp++;
672		} else {
673			*ans = cp;
674			while ((c = *cp) && c != ' ' && c != '\t') cp++;
675		}
676
677		if (c)
678			*cp = 0;
679		return 1;
680	}
681}
682
683char *get_type(type)
684int	type;
685{
686	int	numentries = (sizeof(part_types)/sizeof(struct part_type));
687	int	counter = 0;
688	struct	part_type *ptr = part_types;
689
690
691	while(counter < numentries)
692	{
693		if(ptr->type == type)
694		{
695			return(ptr->name);
696		}
697		ptr++;
698		counter++;
699	}
700	return("unknown");
701}
702