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