xref: /original-bsd/sbin/fsdb/fsdb.c (revision 2ce9ec30)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Computer Consoles Inc.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20 
21 #ifndef lint
22 char copyright[] =
23 "@(#) Copyright (c) 1988 Regents of the University of California.\n\
24  All rights reserved.\n";
25 #endif /* not lint */
26 
27 #ifndef lint
28 static char sccsid[] = "@(#)fsdb.c	5.3 (Berkeley) 06/18/88";
29 #endif /* not lint */
30 
31 /*
32  *  fsdb - file system debugger
33  *
34  *  usage: fsdb [options] special
35  *  options:
36  *	-?		display usage
37  *	-o		override some error conditions
38  *	-p"string"	set prompt to string
39  *	-w		open for write
40  */
41 
42 #include <sys/param.h>
43 #include <signal.h>
44 #include <sys/fs.h>
45 #include <sys/inode.h>
46 #include <sys/dir.h>
47 #include <sys/file.h>
48 #include <stdio.h>
49 #include <setjmp.h>
50 
51 /*
52  * Never changing defines.
53  */
54 #define	OCTAL		8		/* octal base */
55 #define	DECIMAL		10		/* decimal base */
56 #define	HEX		16		/* hexadecimal base */
57 
58 /*
59  * Adjustable defines.
60  */
61 #define	NBUF		10		/* number of cache buffers */
62 #define PROMPTSIZE	80		/* size of user definable prompt */
63 #define	MAXFILES	40000		/* max number of files ls can handle */
64 #define FIRST_DEPTH	10		/* default depth for find and ls */
65 #define SECOND_DEPTH	100		/* second try at depth (maximum) */
66 #define INPUTBUFFER	1040		/* size of input buffer */
67 #define BYTESPERLINE	16		/* bytes per line of /dxo output */
68 #define	NREG		36		/* number of save registers */
69 
70 /*
71  * Values dependent on sizes of structs and such.
72  */
73 #define NUMB		3			/* these three are arbitrary, */
74 #define	BLOCK		5			/* but must be different from */
75 #define	FRAGMENT	7			/* the rest (hence odd).      */
76 #define	BITSPERCHAR	8			/* couldn't find it anywhere  */
77 #define	CHAR		(sizeof (char))
78 #define	SHORT		(sizeof (short))
79 #define	LONG		(sizeof (long))
80 #define	INODE		(sizeof (struct dinode))
81 #define	DIRECTORY	(sizeof (struct direct))
82 #define	CGRP		(sizeof (struct cg))
83 #define SB		(sizeof (struct fs))
84 #define	BLKSIZE		(fs->fs_bsize)		/* for clarity */
85 #define	FRGSIZE		(fs->fs_fsize)
86 #define	BLKSHIFT	(fs->fs_bshift)
87 #define	FRGSHIFT	(fs->fs_fshift)
88 
89 /*
90  * Messy macros that would otherwise clutter up such glamorous code.
91  */
92 #define itob(i)		((itod(fs, (i)) << FRGSHIFT) + itoo(fs, (i)) * INODE)
93 #define min(x, y)	((x) < (y) ? (x) : (y))
94 #define	STRINGSIZE(d)	((long)d->d_reclen - \
95 				((long)&d->d_name[0] - (long)&d->d_ino))
96 #define	letter(c)	((((c) >= 'a')&&((c) <= 'z')) ||\
97 				(((c) >= 'A')&&((c) <= 'Z')))
98 #define	digit(c)	(((c) >= '0') && ((c) <= '9'))
99 #define HEXLETTER(c)	(((c) >= 'A') && ((c) <= 'F'))
100 #define hexletter(c)	(((c) >= 'a') && ((c) <= 'f'))
101 #define octaldigit(c)	(((c) >= '0') && ((c) <= '7'))
102 #define uppertolower(c)	((c) - 'A' + 'a')
103 #define hextodigit(c)	((c) - 'a' + 10)
104 #define	numtodigit(c)	((c) - '0')
105 #define loword(X)	(((ushort *)&X)[1])
106 #define lobyte(X)	(((unsigned char *)&X)[1])
107 
108 /*
109  * buffer cache structure.
110  */
111 struct buf {
112 	struct	buf  *fwd;
113 	struct	buf  *back;
114 	char	*blkaddr;
115 	short	valid;
116 	long	blkno;
117 } buf[NBUF], bhdr;
118 
119 /*
120  * used to hold save registers (see '<' and '>').
121  */
122 struct	save_registers {
123 	long	sv_addr;
124 	long	sv_value;
125 	long	sv_objsz;
126 } regs[NREG];
127 
128 /*
129  * cd, find, and ls use this to hold filenames.  Each filename is broken
130  * up by a slash.  In other words, /usr/src/adm would have a len field
131  * of 2 (starting from 0), and filenames->fname[0-2] would hold usr,
132  * src, and adm components of the pathname.
133  */
134 struct filenames {
135 	long	ino;		/* inode */
136 	long	len;		/* number of components */
137 	char	flag;		/* flag if using SECOND_DEPTH allocator */
138 	char	find;		/* flag if found by find */
139 	char	**fname;	/* hold components of pathname */
140 } *filenames, *top;
141 
142 struct fs filesystem, *fs;	/* super block */
143 
144 /*
145  * Global data.
146  */
147 char		*input_path[MAXPATHLEN];
148 char		*stack_path[MAXPATHLEN];
149 char		*current_path[MAXPATHLEN];
150 char		input_buffer[INPUTBUFFER];
151 char		*prompt;
152 char		*buffers;
153 char		scratch[64];
154 char		BASE[] = "o u     x";
155 char		PROMPT[PROMPTSIZE] = "> ";
156 char		laststyle = '/';
157 char		lastpo = 'x';
158 short		input_pointer;
159 short		current_pathp;
160 short		stack_pathp;
161 short		input_pathp;
162 short		cmp_level;
163 short		nfiles;
164 short		type = NUMB;
165 short		dirslot;
166 short		fd;
167 short		c_count;
168 short		error;
169 short		paren;
170 short		trapped;
171 short		doing_cd;
172 short		doing_find;
173 short		find_by_name;
174 short		find_by_inode;
175 short		long_list;
176 short		recursive;
177 short		objsz = SHORT;
178 short		override = 0;
179 short		wrtflag;
180 short		base = HEX;
181 short		acting_on_inode;
182 short		acting_on_directory;
183 short		should_print = 1;
184 short		clear;
185 short		star;
186 long		addr;
187 long		bod_addr;
188 long		value;
189 long		erraddr;
190 long		errcur_bytes;
191 long		errino;
192 long		errinum;
193 long		cur_cgrp;
194 long		cur_ino;
195 long		cur_inum;
196 long		cur_dir;
197 long		cur_block;
198 long		cur_bytes;
199 long		find_ino;
200 long		filesize;
201 long		blocksize;
202 long		stringsize;
203 long		count = 1;
204 long		commands;
205 long		read_requests;
206 long		actual_disk_reads;
207 jmp_buf		env;
208 
209 extern char	*malloc(), *calloc();
210 char		getachar();
211 char		*getblk(), *fmtentry();
212 int		err();
213 long		get(), bmap(), expr(), term(), getnumb();
214 unsigned long	*print_check();
215 
216 /*
217  * main - lines are read up to the unprotected ('\') newline and
218  *	held in an input buffer.  Characters may be read from the
219  *	input buffer using getachar() and unread using ungetachar().
220  *	Reading the whole line ahead allows the use of debuggers
221  *	which would otherwise be impossible since the debugger
222  *	and fsdb could not share stdin.
223  */
224 
225 main(argc,argv)
226 	short			argc;
227 	char			**argv;
228 {
229 
230 	register char		c, *cptr;
231 	register short		i, j, *iptr;
232 	register struct direct	*dirp;
233 	register struct buf	*bp;
234 	struct filenames	*fn;
235 	char			*progname;
236 	short			colon, mode;
237 	long			temp;
238 	unsigned		block;
239 	int			ffcmp();
240 
241 	setbuf(stdin, NULL);
242 
243 	progname = argv[0];
244 	prompt = &PROMPT[0];
245 	/*
246 	 * Parse options.
247 	 */
248 	while (argc>1 && argv[1][0] == '-') {
249 		if (strcmp("-?", argv[1]) == 0)
250 			goto usage;
251 		if (strcmp("-o", argv[1]) == 0) {
252 			printf("error checking off\n");
253 			override = 1;
254 			argc--; argv++;
255 			continue;
256 		}
257 		if (strncmp("-p", argv[1],2) == 0) {
258 			prompt = &argv[1][2];
259 			argc--; argv++;
260 			continue;
261 		}
262 		if (strcmp("-w", argv[1]) == 0) {
263 			wrtflag = 2;		/* suitable for open */
264 			argc--; argv++;
265 			continue;
266 		}
267 	}
268 	if (argc!=2) {
269 usage:
270 		printf("usage:   %s [options] special\n", progname);
271 		printf("options:\n");
272 		printf("\t-?		display usage\n");
273 		printf("\t-o		override some error conditions\n");
274 		printf("\t-p\"string\"	set prompt to string\n");
275 		printf("\t-w		open for write\n");
276 		exit(1);
277 	}
278 	/*
279 	 * Attempt to open the special file.
280 	 */
281 	if ((fd = open(argv[1],wrtflag)) < 0) {
282 		perror(argv[1]);
283 		exit(1);
284 	}
285 	/*
286 	 * Read in the super block and validate (not too picky).
287 	 */
288 	if (lseek(fd, SBLOCK * DEV_BSIZE, 0) == -1) {
289 		perror(argv[1]);
290 		exit(1);
291 	}
292 	if (read(fd, &filesystem, sizeof filesystem) != sizeof filesystem) {
293 		printf("%s: cannot read superblock\n", argv[1]);
294 		exit(1);
295 	}
296 	fs = &filesystem;
297 	if (fs->fs_magic != FS_MAGIC) {
298 		printf("%s: Bad magic number in file system\n", argv[1]);
299 		exit(1);
300 	}
301 	if (fs->fs_postblformat == FS_42POSTBLFMT)
302 		fs->fs_nrpos = 8;
303 	printf("fsdb of %s %s -- last mounted on %s\n",
304 		argv[1], wrtflag ? "(Opened for write)" : "(Read only)",
305 		&fs->fs_fsmnt[0]);
306 	/*
307 	 * Malloc buffers and set up cache.
308 	 */
309 	buffers = malloc(NBUF * BLKSIZE);
310 	bhdr.fwd = bhdr.back = &bhdr;
311 	for (i=0; i<NBUF; i++) {
312 		bp = &buf[i];
313 		bp->blkaddr = buffers + (i * BLKSIZE);
314 		bp->valid = 0;
315 		insert(bp);
316 	}
317 	/*
318 	 * Malloc filenames structure.  The space for the actual filenames
319 	 * is allocated as it needs it.
320 	 */
321 	filenames = (struct filenames *)calloc(MAXFILES,
322 						sizeof (struct filenames));
323 	if (filenames == NULL) {
324 		printf("out of memory\n");
325 		exit(1);
326 	}
327 
328 	fn = filenames;
329 
330 	restore_inode(2);
331 	/*
332 	 * Malloc a few filenames (needed by pwd for example).
333 	 */
334 	for (i = 0; i < MAXPATHLEN; i++) {
335 		input_path[i] = calloc(1, MAXNAMLEN);
336 		stack_path[i] = calloc(1, MAXNAMLEN);
337 		current_path[i] = calloc(1, MAXNAMLEN);
338 		if (current_path[i] == NULL) {
339 			printf("out of memory\n");
340 			exit(1);
341 		}
342 	}
343 	current_pathp = -1;
344 
345 	signal(2,err);
346 	setjmp(env);
347 
348 	getnextinput();
349 	/*
350 	 * Main loop and case statement.  If an error condition occurs
351 	 * initialization and recovery is attempted.
352 	 */
353 	for (;;) {
354 		if (error) {
355 			freemem(filenames, nfiles);
356 			nfiles = 0;
357 			c_count = 0;
358 			count = 1;
359 			star = 0;
360 			error = 0;
361 			paren = 0;
362 			acting_on_inode = 0;
363 			acting_on_directory = 0;
364 			should_print = 1;
365 			addr = erraddr;
366 			cur_ino = errino;
367 			cur_inum = errinum;
368 			cur_bytes = errcur_bytes;
369 			printf("?\n");
370 			getnextinput();
371 			if (error)
372 				continue;
373 		}
374 		c_count++;
375 
376 		switch (c = getachar()) {
377 
378 		case '\n': /* command end */
379 			freemem(filenames, nfiles);
380 			nfiles = 0;
381 			if (should_print && laststyle == '=') {
382 				ungetachar(c);
383 				goto calc;
384 			}
385 			if (c_count == 1) {
386 				clear = 0;
387 				should_print = 1;
388 				erraddr = addr;
389 				errino = cur_ino;
390 				errinum = cur_inum;
391 				errcur_bytes = cur_bytes;
392 				switch (objsz) {
393 				case DIRECTORY:
394 					if ((addr =
395 					     getdirslot(dirslot+1)) == 0)
396 						should_print = 0;
397 					if (error) {
398 						ungetachar(c);
399 						continue;
400 					}
401 					break;
402 				case INODE:
403 					cur_inum++;
404 					addr = itob(cur_inum);
405 					if (!icheck(addr)) {
406 						cur_inum--;
407 						should_print = 0;
408 					}
409 					break;
410 				case CGRP:
411 				case SB:
412 					cur_cgrp++;
413 					if ((addr=cgrp_check(cur_cgrp)) == 0) {
414 					     cur_cgrp--;
415 					     continue;
416 					}
417 					break;
418 				default:
419 					addr += objsz;
420 					cur_bytes += objsz;
421 					if (valid_addr() == 0)
422 						continue;
423 				}
424 			}
425 			if (type == NUMB)
426 				trapped = 0;
427 			if (should_print)
428 				switch (objsz) {
429 				case DIRECTORY:
430 					fprnt('?', 'd');
431 					break;
432 				case INODE:
433 					fprnt('?', 'i');
434 					if (!error)
435 						cur_ino = addr;
436 					break;
437 				case CGRP:
438 					fprnt('?', 'c');
439 					break;
440 				case SB:
441 					fprnt('?', 's');
442 					break;
443 				case CHAR:
444 				case SHORT:
445 				case LONG:
446 					fprnt(laststyle, lastpo);
447 				}
448 			if (error) {
449 				ungetachar(c);
450 				continue;
451 			}
452 			c_count = colon = acting_on_inode = 0;
453 			acting_on_directory = 0;
454 			should_print = 1;
455 			getnextinput();
456 			if (error)
457 				continue;
458 			erraddr = addr;
459 			errino = cur_ino;
460 			errinum = cur_inum;
461 			errcur_bytes = cur_bytes;
462 			continue;
463 
464 		case '(': /* numeric expression or unknown command */
465 		default:
466 			colon = 0;
467 			if (digit(c) || c == '(') {
468 				ungetachar(c);
469 				addr = expr();
470 				type = NUMB;
471 				value = addr;
472 				continue;
473 			}
474 			printf("unknown command or bad syntax\n");
475 			error++;
476 			continue;
477 
478 		case '?': /* general print facilities */
479 		case '/':
480 			fprnt(c, getachar());
481 			continue;
482 
483 		case ';': /* command separator and . */
484 		case '\t':
485 		case ' ':
486 		case '.':
487 			continue;
488 
489 		case ':': /* command indicator */
490 			colon++;
491 			commands++;
492 			should_print = 0;
493 			stringsize = 0;
494 			trapped = 0;
495 			continue;
496 
497 		case ',': /* count indicator */
498 			colon = star = 0;
499 			if ((c = getachar()) == '*') {
500 				star = 1;
501 				count = BLKSIZE;
502 			} else {
503 				ungetachar(c);
504 				count = expr();
505 				if (error)
506 					continue;
507 				if (!count)
508 					count = 1;
509 			}
510 			clear = 0;
511 			continue;
512 
513 		case '+': /* address addition */
514 			colon = 0;
515 			c = getachar();
516 			ungetachar(c);
517 			if (c == '\n')
518 				temp = 1;
519 			else {
520 				temp = expr();
521 				if (error)
522 					continue;
523 			}
524 			erraddr = addr;
525 			errcur_bytes = cur_bytes;
526 			switch (objsz) {
527 			case DIRECTORY:
528 				addr = getdirslot(dirslot + temp);
529 				if (error)
530 					continue;
531 				break;
532 			case INODE:
533 				cur_inum += temp;
534 				addr = itob(cur_inum);
535 				if (!icheck(addr)) {
536 					cur_inum -= temp;
537 					continue;
538 				}
539 				break;
540 			case CGRP:
541 			case SB:
542 				cur_cgrp += temp;
543 				if ((addr = cgrp_check(cur_cgrp)) == 0) {
544 					cur_cgrp -= temp;
545 					continue;
546 				}
547 				break;
548 			default:
549 				laststyle = '/';
550 				addr += temp * objsz;
551 				cur_bytes += temp * objsz;
552 				if (valid_addr() == 0)
553 					continue;
554 			}
555 			value = get(objsz);
556 			continue;
557 
558 		case '-': /* address subtraction */
559 			colon = 0;
560 			c = getachar();
561 			ungetachar(c);
562 			if (c == '\n')
563 				temp = 1;
564 			else {
565 				temp = expr();
566 				if (error)
567 					continue;
568 			}
569 			erraddr = addr;
570 			errcur_bytes = cur_bytes;
571 			switch (objsz) {
572 			case DIRECTORY:
573 				addr = getdirslot(dirslot - temp);
574 				if (error)
575 					continue;
576 				break;
577 			case INODE:
578 				cur_inum -= temp;
579 				addr = itob(cur_inum);
580 				if (!icheck(addr)) {
581 					cur_inum += temp;
582 					continue;
583 				}
584 				break;
585 			case CGRP:
586 			case SB:
587 				cur_cgrp -= temp;
588 				if ((addr = cgrp_check(cur_cgrp)) == 0) {
589 					cur_cgrp += temp;
590 					continue;
591 				}
592 				break;
593 			default:
594 				laststyle = '/';
595 				addr -= temp * objsz;
596 				cur_bytes -= temp * objsz;
597 				if (valid_addr() == 0)
598 					continue;
599 			}
600 			value = get(objsz);
601 			continue;
602 
603 		case '*': /* address multiplication */
604 			colon = 0;
605 			temp = expr();
606 			if (error)
607 				continue;
608 			if (objsz != INODE && objsz != DIRECTORY)
609 				laststyle = '/';
610 			addr *= temp;
611 			value = get(objsz);
612 			continue;
613 
614 		case '%': /* address division */
615 			colon = 0;
616 			temp = expr();
617 			if (error)
618 				continue;
619 			if (!temp) {
620 				printf("divide by zero\n");
621 				error++;
622 				continue;
623 			}
624 			if (objsz != INODE && objsz != DIRECTORY)
625 				laststyle = '/';
626 			addr /= temp;
627 			value = get(objsz);
628 			continue;
629 
630 		case '=': { /* assignment operation */
631 			short tbase = base;
632 
633 calc:
634 			c = getachar();
635 			if (c == '\n') {
636 				ungetachar(c);
637 				c = lastpo;
638 				if (acting_on_inode == 1) {
639 					if (c != 'o' && c != 'd' && c != 'x' &&
640 					    c != 'O' && c != 'D' && c != 'X') {
641 						switch (objsz) {
642 						case LONG:
643 							c = lastpo = 'X';
644 							break;
645 						case SHORT:
646 							c = lastpo = 'x';
647 							break;
648 						case CHAR:
649 							c = lastpo = 'c';
650 						}
651 					}
652 				} else {
653 					if (acting_on_inode == 2)
654 						c = lastpo = 't';
655 				}
656 			} else if (acting_on_inode)
657 				lastpo = c;
658 			should_print = star = 0;
659 			count = 1;
660 			erraddr = addr;
661 			errcur_bytes = cur_bytes;
662 			switch (c) {
663 			case '"': /* character string */
664 				if (type == NUMB) {
665 					blocksize = BLKSIZE;
666 					filesize = BLKSIZE * 2;
667 					cur_bytes = blkoff(fs, addr);
668 					if (objsz==DIRECTORY || objsz==INODE)
669 						lastpo = 'X';
670 				}
671 				puta();
672 				continue;
673 			case '+': /* =+ operator */
674 				temp = expr();
675 				value = get(objsz);
676 				if (!error)
677 					put(value+temp,objsz);
678 				continue;
679 			case '-': /* =- operator */
680 				temp = expr();
681 				value = get(objsz);
682 				if (!error)
683 					put(value-temp,objsz);
684 				continue;
685 			case 'b':
686 			case 'c':
687 				if (objsz == CGRP)
688 					fprnt('?', c);
689 				else
690 					fprnt('/', c);
691 				continue;
692 			case 'i':
693 				addr = cur_ino;
694 				fprnt('?', 'i');
695 				continue;
696 			case 's':
697 				fprnt('?', 's');
698 				continue;
699 			case 't':
700 			case 'T':
701 				laststyle = '=';
702 				printf("\t\t");
703 				printf("%s", ctime(&value));
704 				continue;
705 			case 'o':
706 				base = OCTAL;
707 				goto otx;
708 			case 'd':
709 				if (objsz == DIRECTORY) {
710 					addr = cur_dir;
711 					fprnt('?', 'd');
712 					continue;
713 				}
714 				base = DECIMAL;
715 				goto otx;
716 			case 'x':
717 				base = HEX;
718 otx:
719 				laststyle = '=';
720 				printf("\t\t");
721 				if (acting_on_inode)
722 					print(value & 0177777L, 12, -8, 0);
723 				else
724 					print(addr & 0177777L, 12, -8, 0);
725 				printf("\n");
726 				base = tbase;
727 				continue;
728 			case 'O':
729 				base = OCTAL;
730 				goto OTX;
731 			case 'D':
732 				base = DECIMAL;
733 				goto OTX;
734 			case 'X':
735 				base = HEX;
736 OTX:
737 				laststyle = '=';
738 			 	printf("\t\t");
739 				if (acting_on_inode)
740 					print(value, 12, -8, 0);
741 				else
742 					print(addr, 12, -8, 0);
743 				printf("\n");
744 				base = tbase;
745 				continue;
746 			default: /* regular assignment */
747 				ungetachar(c);
748 				value = expr();
749 				if (error)
750 					printf("syntax error\n");
751 				else
752 					put(value,objsz);
753 				continue;
754 			}
755 		}
756 
757 		case '>': /* save current address */
758 			colon = 0;
759 			should_print = 0;
760 			c = getachar();
761 			if (!letter(c) && !digit(c)) {
762 				printf("invalid register specification, ");
763 				printf("must be letter or digit\n");
764 				error++;
765 				continue;
766 			}
767 			if (letter(c)) {
768 				if (c < 'a')
769 					c = uppertolower(c);
770 				c = hextodigit(c);
771 			} else
772 				c = numtodigit(c);
773 			regs[c].sv_addr = addr;
774 			regs[c].sv_value = value;
775 			regs[c].sv_objsz = objsz;
776 			continue;
777 
778 		case '<': /* restore saved address */
779 			colon = 0;
780 			should_print = 0;
781 			c = getachar();
782 			if (!letter(c) && !digit(c)) {
783 				printf("invalid register specification, ");
784 				printf("must be letter or digit\n");
785 				error++;
786 				continue;
787 			}
788 			if (letter(c)) {
789 				if (c < 'a')
790 					c = uppertolower(c);
791 				c = hextodigit(c);
792 			} else
793 				c = numtodigit(c);
794 			addr = regs[c].sv_addr;
795 			value = regs[c].sv_value;
796 			objsz = regs[c].sv_objsz;
797 			continue;
798 
799 		case 'a':
800 			if (colon)
801 				colon = 0;
802 			else
803 				goto no_colon;
804 			if (match("at", 2)) { 		/* access time */
805 				acting_on_inode = 2;
806 				should_print = 1;
807 				addr = (long)
808 					&((struct dinode *)cur_ino)->di_atime;
809 				value = get(LONG);
810 				type = NULL;
811 				continue;
812 			}
813 			goto bad_syntax;
814 
815 		case 'b':
816 			if (colon)
817 				colon = 0;
818 			else
819 				goto no_colon;
820 			if (match("block", 2)) { 	/* block conversion */
821 				if (type == NUMB) {
822 					value = addr;
823 					cur_bytes = 0;
824 					blocksize = BLKSIZE;
825 					filesize = BLKSIZE * 2;
826 				}
827 				addr = value << FRGSHIFT;
828 				bod_addr = addr;
829 				value = get(LONG);
830 				type = BLOCK;
831 				dirslot = 0;
832 				trapped++;
833 				continue;
834 			}
835 			if (match("bs", 2)) {		/* block size */
836 				acting_on_inode = 1;
837 				should_print = 1;
838 				if (icheck(cur_ino) == 0)
839 					continue;
840 				addr = (long)
841 					&((struct dinode *)cur_ino)->di_blocks;
842 				value = get(LONG);
843 				type = NULL;
844 				continue;
845 			}
846 			if (match("base", 2)) {		/* change/show base */
847 showbase:
848 				if ((c = getachar()) == '\n') {
849 					ungetachar(c);
850 					printf("base =\t\t");
851 					switch (base) {
852 					case OCTAL:
853 						printf("OCTAL\n");
854 						continue;
855 					case DECIMAL:
856 						printf("DECIMAL\n");
857 						continue;
858 					case HEX:
859 						printf("HEX\n");
860 						continue;
861 					}
862 				}
863 				if (c != '=') {
864 					printf("missing '='\n");
865 					error++;
866 					continue;
867 				}
868 				value = expr();
869 				switch (value) {
870 				default:
871 					printf("invalid base\n");
872 					error++;
873 					break;
874 				case OCTAL:
875 				case DECIMAL:
876 				case HEX:
877 					base = value;
878 				}
879 				goto showbase;
880 			}
881 			goto bad_syntax;
882 
883 		case 'c':
884 			if (colon)
885 				colon = 0;
886 			else
887 				goto no_colon;
888 			if (match("cd", 2)) {		/* change directory */
889 				top = filenames - 1;
890 				eat_spaces();
891 				if ((c = getachar()) == '\n') {
892 					ungetachar(c);
893 					current_pathp = -1;
894 					restore_inode(2);
895 					continue;
896 				}
897 				ungetachar(c);
898 				temp = cur_inum;
899 				doing_cd = 1;
900 				parse();
901 				doing_cd = 0;
902 				if (nfiles != 1) {
903 					restore_inode(temp);
904 					if (!error) {
905 						print_path(input_path,
906 								input_pathp);
907 						if (nfiles == 0)
908 							printf(" not found\n");
909 						else
910 							printf(" ambiguous\n");
911 						error++;
912 					}
913 					continue;
914 				}
915 				restore_inode(filenames->ino);
916 				if ((mode = icheck(addr)) == 0)
917 					continue;
918 				if ((mode & IFMT) != IFDIR) {
919 					restore_inode(temp);
920 					print_path(input_path, input_pathp);
921 					printf(" not a directory\n");
922 					error++;
923 					continue;
924 				}
925 				for (i = 0; i <= top->len; i++)
926 					strcpy(current_path[i],
927 						top->fname[i]);
928 				current_pathp = top->len;
929 				continue;
930 			}
931 			if (match("cg", 2)) {		/* cylinder group */
932 				if (type == NUMB)
933 					value = addr;
934 				if (value > fs->fs_ncg - 1) {
935 					printf("maximum cylinder group is ");
936 					print(fs->fs_ncg - 1, 8, -8, 0);
937 					printf("\n");
938 					error++;
939 					continue;
940 				}
941 				type = objsz = CGRP;
942 				cur_cgrp = value;
943 				addr = cgtod(fs, cur_cgrp) << FRGSHIFT;
944 				continue;
945 			}
946 			if (match("ct", 2)) {		/* creation time */
947 				acting_on_inode = 2;
948 				should_print = 1;
949 				addr = (long)
950 					&((struct dinode *)cur_ino)->di_ctime;
951 				value = get(LONG);
952 				type = NULL;
953 				continue;
954 			}
955 			goto bad_syntax;
956 
957 		case 'd':
958 			if (colon)
959 				colon = 0;
960 			else
961 				goto no_colon;
962 			if (match("directory", 2)) { 	/* directory offsets */
963 				if (type == NUMB)
964 					value = addr;
965 				objsz = DIRECTORY;
966 				type = DIRECTORY;
967 				addr = getdirslot(value);
968 				continue;
969 			}
970 			if (match("db", 2)) {		/* direct block */
971 				acting_on_inode = 1;
972 				should_print = 1;
973 				if (type == NUMB)
974 					value = addr;
975 				if (value >= NDADDR) {
976 					printf("direct blocks are 0 to ");
977 					print(NDADDR - 1, 0, 0, 0);
978 					printf("\n");
979 					error++;
980 					continue;
981 				}
982 				addr = cur_ino;
983 				if (!icheck(addr))
984 					continue;
985 				addr = (long)
986 				      &((struct dinode *)cur_ino)->di_db[value];
987 				bod_addr = addr;
988 				cur_bytes = (value) * BLKSIZE;
989 				cur_block = value;
990 				type = BLOCK;
991 				dirslot = 0;
992 				value = get(LONG);
993 				if (!value && !override) {
994 					printf("non existent block\n");
995 					error++;
996 				}
997 				continue;
998 			}
999 			goto bad_syntax;
1000 
1001 		case 'f':
1002 			if (colon)
1003 				colon = 0;
1004 			else
1005 				goto no_colon;
1006 			if (match("find", 3)) {		/* find command */
1007 				find();
1008 				continue;
1009 			}
1010 			if (match("fragment", 2)) {	/* fragment conv. */
1011 				if (type == NUMB) {
1012 					value = addr;
1013 					cur_bytes = 0;
1014 					blocksize = FRGSIZE;
1015 					filesize = FRGSIZE * 2;
1016 				}
1017 				if (min(blocksize, filesize) - cur_bytes >
1018 							FRGSIZE) {
1019 					blocksize = cur_bytes + FRGSIZE;
1020 					filesize = blocksize * 2;
1021 				}
1022 				addr = value << FRGSHIFT;
1023 				bod_addr = addr;
1024 				value = get(LONG);
1025 				type = FRAGMENT;
1026 				dirslot = 0;
1027 				trapped++;
1028 				continue;
1029 			}
1030 			if (match("file", 4)) {		/* access as file */
1031 				acting_on_inode = 1;
1032 				should_print = 1;
1033 				if (type == NUMB)
1034 					value = addr;
1035 				addr = cur_ino;
1036 				if ((mode = icheck(addr)) == 0)
1037 					continue;
1038 				if ((mode & IFCHR) && !override) {
1039 					printf("special device\n");
1040 					error++;
1041 					continue;
1042 				}
1043 				if ((addr = (bmap(value) << FRGSHIFT)) == 0)
1044 					continue;
1045 				cur_block = value;
1046 				bod_addr = addr;
1047 				type = BLOCK;
1048 				dirslot = 0;
1049 				continue;
1050 			}
1051 			if (match("fill", 4)) {		/* fill */
1052 				if (getachar() != '=') {
1053 					printf("missing '='\n");
1054 					error++;
1055 					continue;
1056 				}
1057 				if (objsz == INODE || objsz == DIRECTORY) {
1058 				      printf("can't fill inode or directory\n");
1059 				      error++;
1060 				      continue;
1061 				}
1062 				fill();
1063 				continue;
1064 			}
1065 			goto bad_syntax;
1066 
1067 		case 'g':
1068 			if (colon)
1069 				colon = 0;
1070 			else
1071 				goto no_colon;
1072 			if (match("gid", 1)) {		/* group id */
1073 				acting_on_inode = 1;
1074 				should_print = 1;
1075 				addr = (long)
1076 					&((struct dinode *)cur_ino)->di_gid;
1077 				value = get(SHORT);
1078 				type = NULL;
1079 				continue;
1080 			}
1081 			goto bad_syntax;
1082 
1083 		case 'i':
1084 			if (colon)
1085 				colon = 0;
1086 			else
1087 				goto no_colon;
1088 			if (match("inode", 2)) { /* i# to inode conversion */
1089 				if (c_count == 2) {
1090 					addr = cur_ino;
1091 					value = get(INODE);
1092 					type = NULL;
1093 					laststyle = '=';
1094 					lastpo = 'i';
1095 					should_print = 1;
1096 					continue;
1097 				}
1098 				if (type == NUMB)
1099 					value = addr;
1100 				addr = itob(value);
1101 				if (!icheck(addr))
1102 					continue;
1103 				cur_ino = addr;
1104 				cur_inum = value;
1105 				value = get(INODE);
1106 				type = NULL;
1107 				continue;
1108 			}
1109 			if (match("ib", 2)) {	/* indirect block */
1110 				acting_on_inode = 1;
1111 				should_print = 1;
1112 				if (type == NUMB)
1113 					value = addr;
1114 				if (value >= NIADDR) {
1115 					printf("indirect blocks are 0 to ");
1116 					print(NIADDR - 1, 0, 0, 0);
1117 					printf("\n");
1118 					error++;
1119 					continue;
1120 				}
1121 				addr = (long)
1122 				      &((struct dinode *)cur_ino)->di_ib[value];
1123 				cur_bytes = (NDADDR - 1) * BLKSIZE;
1124 				temp = 1;
1125 				for (i = 0; i < value; i++) {
1126 					temp *= NINDIR(fs) * BLKSIZE;
1127 					cur_bytes += temp;
1128 				}
1129 				type = BLOCK;
1130 				dirslot = 0;
1131 				value = get(LONG);
1132 				if (!value && !override) {
1133 					printf("non existent block\n");
1134 					error++;
1135 				}
1136 				continue;
1137 			}
1138 			goto bad_syntax;
1139 
1140 		case 'l':
1141 			if (colon)
1142 				colon = 0;
1143 			else
1144 				goto no_colon;
1145 			if (match("ls", 2)) {		/* ls command */
1146 				temp = cur_inum;
1147 				recursive = long_list = 0;
1148 				top = filenames - 1;
1149 				for (;;) {
1150 					eat_spaces();
1151 					if ((c = getachar()) == '-') {
1152 						if ((c = getachar()) == 'R') {
1153 						  recursive = 1;
1154 						  continue;
1155 						} else if (c == 'l') {
1156 						  long_list = 1;
1157 						} else {
1158 						  printf("unknown option ");
1159 						  printf("'%c'\n", c);
1160 						  error++;
1161 						  break;
1162 						}
1163 					} else
1164 						ungetachar(c);
1165 					if ((c = getachar()) == '\n') {
1166 						if (c_count != 2) {
1167 							ungetachar(c);
1168 							break;
1169 						}
1170 					}
1171 					c_count++;
1172 					ungetachar(c);
1173 					parse();
1174 					restore_inode(temp);
1175 					if (error)
1176 						break;
1177 				}
1178 				recursive = 0;
1179 				if (error || nfiles == 0) {
1180 					if (!error) {
1181 						print_path(input_path,
1182 								input_pathp);
1183 						printf(" not found\n");
1184 					}
1185 					continue;
1186 				}
1187 				if (nfiles) {
1188 				    cmp_level = 0;
1189 				    qsort((char *)filenames, nfiles,
1190 					sizeof (struct filenames), ffcmp);
1191 				    ls(filenames, filenames + (nfiles - 1), 0);
1192 				} else {
1193 				    printf("no match\n");
1194 				    error++;
1195 				}
1196 				restore_inode(temp);
1197 				continue;
1198 			}
1199 			if (match("ln", 2)) {		/* link count */
1200 				acting_on_inode = 1;
1201 				should_print = 1;
1202 				addr = (long)
1203 					&((struct dinode *)cur_ino)->di_nlink;
1204 				value = get(SHORT);
1205 				type = NULL;
1206 				continue;
1207 			}
1208 			goto bad_syntax;
1209 
1210 		case 'm':
1211 			if (colon)
1212 				colon = 0;
1213 			else
1214 				goto no_colon;
1215 			addr = cur_ino;
1216 			if ((mode = icheck(addr)) == 0)
1217 				continue;
1218 			if (match("mt", 2)) { 		/* modification time */
1219 				acting_on_inode = 2;
1220 				should_print = 1;
1221 				addr = (long)
1222 					&((struct dinode *)cur_ino)->di_mtime;
1223 				value = get(LONG);
1224 				type = NULL;
1225 				continue;
1226 			}
1227 			if (match("md", 2)) {		/* mode */
1228 				acting_on_inode = 1;
1229 				should_print = 1;
1230 				addr = (long)
1231 					&((struct dinode *)cur_ino)->di_mode;
1232 				value = get(SHORT);
1233 				type = NULL;
1234 				continue;
1235 			}
1236 			if (match("maj", 2)) {	/* major device number */
1237 				acting_on_inode = 1;
1238 				should_print = 1;
1239 				if (devcheck(mode))
1240 					continue;
1241 				addr = (long)
1242 					&((struct dinode *)cur_ino)->di_db[1];
1243 				value = get(LONG);
1244 				type = NULL;
1245 				continue;
1246 			}
1247 			if (match("min", 2)) {	/* minor device number */
1248 				acting_on_inode = 1;
1249 				should_print = 1;
1250 				if (devcheck(mode))
1251 					continue;
1252 				addr = (long)
1253 					&((struct dinode *)cur_ino)->di_db[0];
1254 				value = get(LONG);
1255 				type = NULL;
1256 				continue;
1257 			}
1258 			goto bad_syntax;
1259 
1260 		case 'n':
1261 			if (colon)
1262 				colon = 0;
1263 			else
1264 				goto no_colon;
1265 			if (match("nm", 1)) {		/* directory name */
1266 				objsz = DIRECTORY;
1267 				acting_on_directory = 1;
1268 				cur_dir = addr;
1269 				if ((cptr = getblk(addr)) == 0)
1270 					continue;
1271 				dirp = (struct direct *)(cptr+blkoff(fs, addr));
1272 				stringsize = (long)dirp->d_reclen -
1273 				  ((long)&dirp->d_name[0] - (long)&dirp->d_ino);
1274 				addr = (long)
1275 					&((struct direct *)addr)->d_name[0];
1276 				type = NULL;
1277 				continue;
1278 			}
1279 			goto bad_syntax;
1280 
1281 		case 'o':
1282 			if (colon)
1283 				colon = 0;
1284 			else
1285 				goto no_colon;
1286 			if (match("override", 1)) {	/* override flip flop */
1287 				if (override = !override)
1288 					printf("error checking off\n");
1289 				else
1290 					printf("error checking on\n");
1291 				continue;
1292 			}
1293 			goto bad_syntax;
1294 
1295 		case 'p':
1296 			if (colon)
1297 				colon = 0;
1298 			else
1299 				goto no_colon;
1300 			if (match("pwd", 2)) {		/* print working dir */
1301 				print_path(current_path, current_pathp);
1302 				printf("\n");
1303 				continue;
1304 			}
1305 			if (match("prompt", 2)) {	/* change prompt */
1306 				if ((c = getachar()) != '=') {
1307 					printf("missing '='\n");
1308 					error++;
1309 					continue;
1310 				}
1311 				if ((c = getachar()) != '"') {
1312 					printf("missing '\"'\n");
1313 					error++;
1314 					continue;
1315 				}
1316 				i = 0;
1317 				prompt = &prompt[0];
1318 				while ((c = getachar()) != '"' &&
1319 				       c != '\n') {
1320 					prompt[i++] = c;
1321 					if (i >= PROMPTSIZE) {
1322 						printf("string too long\n");
1323 						error++;
1324 						break;
1325 					}
1326 				}
1327 				prompt[i] = '\0';
1328 				continue;
1329 			}
1330 			goto bad_syntax;
1331 
1332 		case 'q':
1333 			if (!colon)
1334 				goto no_colon;
1335 			if (match("quit", 1)) {		/* quit */
1336 				if ((c = getachar()) != '\n') {
1337 					error++;
1338 					continue;
1339 				}
1340 				exit(0);
1341 			}
1342 			goto bad_syntax;
1343 
1344 		case 's':
1345 			if (colon)
1346 				colon = 0;
1347 			else
1348 				goto no_colon;
1349 			if (match("sb", 2)) {		/* super block */
1350 				if (c_count == 2) {
1351 					cur_cgrp = -1;
1352 					type = objsz = SB;
1353 					laststyle = '=';
1354 					lastpo = 's';
1355 					should_print = 1;
1356 					continue;
1357 				}
1358 				if (type == NUMB)
1359 					value = addr;
1360 				if (value > fs->fs_ncg - 1) {
1361 					printf("maximum super block is ");
1362 					print(fs->fs_ncg - 1, 8, -8, 0);
1363 					printf("\n");
1364 					error++;
1365 					continue;
1366 				}
1367 				type = objsz = SB;
1368 				cur_cgrp = value;
1369 				addr = cgsblock(fs, cur_cgrp) << FRGSHIFT;
1370 				continue;
1371 			}
1372 			if (match("sz", 2)) {		/* file size */
1373 				acting_on_inode = 1;
1374 				should_print = 1;
1375 				addr = (long)
1376 					&((struct dinode *)cur_ino)->di_size;
1377 				value = get(LONG);
1378 				type = NULL;
1379 				continue;
1380 			}
1381 			goto bad_syntax;
1382 
1383 		case 'u':
1384 			if (colon)
1385 				colon = 0;
1386 			else
1387 				goto no_colon;
1388 			if (match("uid", 1)) {		/* user id */
1389 				acting_on_inode = 1;
1390 				should_print = 1;
1391 				addr = (long)
1392 					&((struct dinode *)cur_ino)->di_uid;
1393 				value = get(SHORT);
1394 				type = NULL;
1395 				continue;
1396 			}
1397 			goto bad_syntax;
1398 
1399 		case 'F': /* buffer status (internal use only) */
1400 			if (colon)
1401 				colon = 0;
1402 			else
1403 				goto no_colon;
1404 			for (bp = bhdr.fwd; bp != &bhdr; bp = bp->fwd)
1405 				printf("%8x %d\n",bp->blkno,bp->valid);
1406 			printf("\n");
1407 			printf("# commands\t\t%d\n", commands);
1408 			printf("# read requests\t\t%d\n", read_requests);
1409 			printf("# actual disk reads\t%d\n", actual_disk_reads);
1410 			continue;
1411 no_colon:
1412 		printf("a colon should precede a command\n");
1413 		error++;
1414 		continue;
1415 bad_syntax:
1416 		printf("more letters needed to distinguish command\n");
1417 		error++;
1418 		continue;
1419 		}
1420 	}
1421 }
1422 
1423 /*
1424  * getachar - get next character from input buffer.
1425  */
1426 char
1427 getachar()
1428 {
1429 	return(input_buffer[input_pointer++]);
1430 }
1431 
1432 /*
1433  * ungetachar - return character to input buffer.
1434  */
1435 ungetachar(c)
1436 	register char	c;
1437 {
1438 	if (input_pointer == 0) {
1439 		printf("internal problem maintaining input buffer\n");
1440 		error++;
1441 		return;
1442 	}
1443 	input_buffer[--input_pointer] = c;
1444 }
1445 
1446 /*
1447  * getnextinput - display the prompt and read an input line.
1448  *	An input line is up to 128 characters terminated by the newline
1449  *	character.  Handle overflow, shell escape, and eof.
1450  */
1451 getnextinput()
1452 {
1453 	register int	i;
1454 	register char	c;
1455 	register short	pid, rpid;
1456 	int		retcode;
1457 
1458 newline:
1459 	i = 0;
1460 	printf("%s", prompt);
1461 ignore_eol:
1462 	while ((c = getc(stdin)) != '\n' && !(c == '!' && i == 0) &&
1463 	       !feof(stdin) && i <= INPUTBUFFER - 2)
1464 		input_buffer[i++] = c;
1465 	if (input_buffer[i - 1] == '\\') {
1466 		input_buffer[i++] = c;
1467 		goto ignore_eol;
1468 	}
1469 	if (feof(stdin)) {
1470 		printf("\n");
1471 		exit(0);
1472 	}
1473 	if (c == '!') {
1474 		if ((pid = fork()) == 0) {
1475 			execl("/bin/sh", "sh", "-t", 0);
1476 			error++;
1477 			return;
1478 		}
1479 		while ((rpid = wait(&retcode)) != pid && rpid != -1)
1480 			;
1481 		printf("!\n");
1482 		goto newline;
1483 	}
1484 	if (c != '\n')
1485 		printf("input truncated to 128 characters\n");
1486 	input_buffer[i] = '\n';
1487 	input_pointer = 0;
1488 }
1489 
1490 /*
1491  * eat_spaces - read extraneous spaces.
1492  */
1493 eat_spaces()
1494 {
1495 	register char	c;
1496 
1497 	while ((c = getachar()) == ' ')
1498 		;
1499 	ungetachar(c);
1500 }
1501 
1502 /*
1503  * restore_inode - set up all inode indicators so inum is now
1504  *	the current inode.
1505  */
1506 restore_inode(inum)
1507 	long		inum;
1508 {
1509 	errinum = cur_inum = inum;
1510 	addr = errino = cur_ino = itob(inum);
1511 }
1512 
1513 /*
1514  * match - return false if the input does not match string up to
1515  *	upto letters.   Then proceed to chew up extraneous letters.
1516  */
1517 match(string, upto)
1518 	register char	*string;
1519 	register int	upto;
1520 {
1521 	register int	i, length = strlen(string) - 1;
1522 	register char	c;
1523 	int		save_upto = upto;
1524 
1525 	while (--upto) {
1526 		string++;
1527 		if ((c = getachar()) != *string) {
1528 			for (i = save_upto - upto; i; i--) {
1529 				ungetachar(c);
1530 				c = *--string;
1531 			}
1532 			return(0);
1533 		}
1534 		length--;
1535 	}
1536 	while (length--) {
1537 		string++;
1538 		if ((c = getachar()) != *string) {
1539 			ungetachar(c);
1540 			return(1);
1541 		}
1542 	}
1543 	return(1);
1544 }
1545 
1546 /*
1547  * expr - expression evaluator.  Will evaluate expressions from
1548  *	left to right with no operator precedence.  Parentheses may
1549  *	be used.
1550  */
1551 long
1552 expr()
1553 {
1554 	register long	numb = 0, temp;
1555 	register char	c;
1556 
1557 	numb = term();
1558 	for (;;) {
1559 		if (error)
1560 			return;
1561 		c = getachar();
1562 		switch (c) {
1563 
1564 		case '+':
1565 			numb += term();
1566 			continue;
1567 
1568 		case '-':
1569 			numb -= term();
1570 			continue;
1571 
1572 		case '*':
1573 			numb *= term();
1574 			continue;
1575 
1576 		case '%':
1577 			temp = term();
1578 			if (!temp) {
1579 				printf("divide by zero\n");
1580 				error++;
1581 				return;
1582 			}
1583 			numb /= temp;
1584 			continue;
1585 
1586 		case ')':
1587 			paren--;
1588 			return(numb);
1589 
1590 		default:
1591 			ungetachar(c);
1592 			if (paren && !error) {
1593 				printf("missing ')'\n");
1594 				error++;
1595 			}
1596 			return(numb);
1597 		}
1598 	}
1599 }
1600 
1601 /*
1602  * term - used by expression evaluator to get an operand.
1603  */
1604 long
1605 term()
1606 {
1607 	register char	c;
1608 
1609 	switch (c = getachar()) {
1610 
1611 	default:
1612 		ungetachar(c);
1613 
1614 	case '+':
1615 		return(getnumb());
1616 
1617 	case '-':
1618 		return(-getnumb());
1619 
1620 	case '(':
1621 		paren++;
1622 		return(expr());
1623 	}
1624 }
1625 
1626 /*
1627  * getnumb - read a number from the input stream.  A leading
1628  *	zero signifies octal interpretation, a leading '0x'
1629  *	signifies hexadecimal, and a leading '0t' signifies
1630  *	decimal.  If the first character is a character,
1631  *	return an error.
1632  */
1633 long
1634 getnumb()
1635 {
1636 
1637 	register char	c, savec;
1638 	long		number = 0, tbase, num;
1639 	extern short	error;
1640 
1641 	c = getachar();
1642 	if (!digit(c)) {
1643 		error++;
1644 		ungetachar(c);
1645 		return(-1);
1646 	}
1647 	if (c == '0') {
1648 		tbase = OCTAL;
1649 		if ((c = getachar()) == 'x')
1650 			tbase = HEX;
1651 		else if (c == 't')
1652 			tbase = DECIMAL;
1653 		else ungetachar(c);
1654 	} else {
1655 		tbase = base;
1656 		ungetachar(c);
1657 	}
1658 	for (;;) {
1659 		num = tbase;
1660 		c = savec = getachar();
1661 		if (HEXLETTER(c))
1662 			c = uppertolower(c);
1663 		switch (tbase) {
1664 		case HEX:
1665 			if (hexletter(c)) {
1666 				num = hextodigit(c);
1667 				break;
1668 			}
1669 		case DECIMAL:
1670 			if (digit(c))
1671 				num = numtodigit(c);
1672 			break;
1673 		case OCTAL:
1674 			if (octaldigit(c))
1675 				num = numtodigit(c);
1676 		}
1677 		if (num == tbase)
1678 			break;
1679 		number = number * tbase + num;
1680 	}
1681 	ungetachar(savec);
1682 	return(number);
1683 }
1684 
1685 /*
1686  * find - the syntax is almost identical to the unix command.
1687  *		find dir [-name pattern] [-inum number]
1688  *	Note:  only one of -name or -inum may be used at a time.
1689  *	       Also, the -print is not needed (implied).
1690  */
1691 find()
1692 {
1693 	register struct filenames	*fn;
1694 	register char			c;
1695 	long				temp;
1696 	short				mode;
1697 
1698 	eat_spaces();
1699 	temp = cur_inum;
1700 	top = filenames - 1;
1701 	doing_cd = 1;
1702 	parse();
1703 	doing_cd = 0;
1704 	if (nfiles != 1) {
1705 		restore_inode(temp);
1706 		if (!error) {
1707 			print_path(input_path, input_pathp);
1708 			if (nfiles == 0)
1709 				printf(" not found\n");
1710 			else
1711 				printf(" ambiguous\n");
1712 			error++;
1713 			return;
1714 		}
1715 	}
1716 	restore_inode(filenames->ino);
1717 	freemem(filenames, nfiles);
1718 	nfiles = 0;
1719 	top = filenames - 1;
1720 	if ((mode = icheck(addr)) == 0)
1721 		return;
1722 	if ((mode & IFMT) != IFDIR) {
1723 		print_path(input_path, input_pathp);
1724 		printf(" not a directory\n");
1725 		error++;
1726 		return;
1727 	}
1728 	eat_spaces();
1729 	if ((c = getachar()) != '-') {
1730 		printf("missing '-'\n");
1731 		error++;
1732 		return;
1733 	}
1734 	find_by_name = find_by_inode = 0;
1735 	c = getachar();
1736 	if (match("name", 4)) {
1737 		eat_spaces();
1738 		find_by_name = 1;
1739 	} else if (match("inum", 4)) {
1740 		eat_spaces();
1741 		find_ino = expr();
1742 		if (error)
1743 			return;
1744 		while ((c = getachar()) != '\n')
1745 			;
1746 		ungetachar(c);
1747 		find_by_inode = 1;
1748 	} else {
1749 		printf("use -name or -inum with find\n");
1750 		error++;
1751 		return;
1752 	}
1753 	doing_find = 1;
1754 	parse();
1755 	doing_find = 0;
1756 	if (error) {
1757 		restore_inode(temp);
1758 		return;
1759 	}
1760 	for (fn = filenames; fn <= top; fn++) {
1761 		if (fn->find == 0)
1762 			continue;
1763 		printf("i#: ");
1764 		print(fn->ino, 12, -8, 0);
1765 		print_path(fn->fname, fn->len);
1766 		printf("\n");
1767 	}
1768 	restore_inode(temp);
1769 }
1770 
1771 /*
1772  * ls - do an ls.  Should behave exactly as ls(1).
1773  *	Only -R and -l is supported and -l gives different results.
1774  */
1775 ls(fn0, fnlast, level)
1776 	struct filenames		*fn0, *fnlast;
1777 	short				level;
1778 {
1779 	register struct filenames	*fn, *fnn;
1780 	register int			i;
1781 	int				fcmp();
1782 
1783 	fn = fn0;
1784 	for (;;) {
1785 		fn0 = fn;
1786 		if (fn0->len) {
1787 			cmp_level = level;
1788 			qsort((char *)fn0, fnlast - fn0 + 1,
1789 				sizeof (struct filenames), fcmp);
1790 		}
1791 		for (fnn = fn, fn++; fn <= fnlast; fnn = fn, fn++) {
1792 			if (fnn->len != fn->len && level == fnn->len - 1)
1793 				break;
1794 			if (fnn->len == 0)
1795 				continue;
1796 			if (strcmp(fn->fname[level], fnn->fname[level]))
1797 				break;
1798 		}
1799 		if (fn0->len && level != fn0->len - 1)
1800 			ls(fn0, fnn, level + 1);
1801 		else {
1802 			if (fn0 != filenames)
1803 				printf("\n");
1804 			print_path(fn0->fname, fn0->len - 1);
1805 			printf(":\n");
1806 			if (fn0->len == 0)
1807 				cmp_level = level;
1808 			else
1809 				cmp_level = level + 1;
1810 			qsort((char *)fn0, fnn - fn0 + 1,
1811 				sizeof (struct filenames), fcmp);
1812 			formatf(fn0, fnn);
1813 			nfiles -= fnn - fn0 + 1;
1814 		}
1815 		if (fn > fnlast)
1816 			return;
1817 	}
1818 }
1819 
1820 /*
1821  * formatf - code lifted from ls.
1822  */
1823 formatf(fn0, fnlast)
1824 	register struct filenames	*fn0, *fnlast;
1825 {
1826 	register struct filenames	*fn;
1827 	int				width = 0, w, nentry = fnlast - fn0 + 1;
1828 	int				i, j, columns, lines;
1829 	char				*cp;
1830 
1831 	if (long_list) {
1832 		columns = 1;
1833 	} else {
1834 		for (fn = fn0; fn <= fnlast; fn++) {
1835 			int len = strlen(fn->fname[cmp_level]) + 2;
1836 
1837 			if (len > width)
1838 				width = len;
1839 		}
1840 		width = (width + 8) &~ 7;
1841 		columns = 80 / width;
1842 		if (columns == 0)
1843 			columns = 1;
1844 	}
1845 	lines = (nentry + columns - 1) / columns;
1846 	for (i = 0; i < lines; i++) {
1847 		for (j = 0; j < columns; j++) {
1848 			fn = fn0 + j * lines + i;
1849 			if (long_list) {
1850 				printf("i#: ");
1851 				print(fn->ino, 12, -8, 0);
1852 			}
1853 			cp = fmtentry(fn);
1854 			printf("%s", cp);
1855 			if (fn + lines > fnlast) {
1856 				printf("\n");
1857 				break;
1858 			}
1859 			w = strlen(cp);
1860 			while (w < width) {
1861 				w = (w + 8) &~ 7;
1862 				putchar('\t');
1863 			}
1864 		}
1865 	}
1866 }
1867 
1868 /*
1869  * fmtentry - code lifted from ls.
1870  */
1871 char *
1872 fmtentry(fn)
1873 	register struct filenames	*fn;
1874 {
1875 	static char			fmtres[BUFSIZ];
1876 	register struct dinode		*ip;
1877 	register char			*cptr, *cp, *dp;
1878 
1879 	dp = &fmtres[0];
1880 	for (cp = fn->fname[cmp_level]; *cp; cp++) {
1881 		if (*cp < ' ' || *cp >= 0177)
1882 			*dp++ = '?';
1883 		else
1884 			*dp++ = *cp;
1885 	}
1886 	addr = itob(fn->ino);
1887 	if ((cptr = getblk(addr)) == 0)
1888 		return(NULL);
1889 	cptr += blkoff(fs, addr);
1890 	ip = (struct dinode *)cptr;
1891 	switch (ip->di_mode & IFMT) {
1892 	case IFDIR:
1893 		*dp++ = '/';
1894 		break;
1895 	case IFLNK:
1896 		*dp++ = '@';
1897 		break;
1898 	case IFSOCK:
1899 		*dp++ = '=';
1900 		break;
1901 /*		case IFIFO:
1902 		*dp++ = 'f';
1903 		break;		SYSTEM V only */
1904 	case IFCHR:
1905 	case IFBLK:
1906 	case IFREG:
1907 		if (ip->di_mode & 0111)
1908 			*dp++ = '*';
1909 		else
1910 			*dp++ = ' ';
1911 		break;
1912 	default:
1913 		*dp++ = '?';
1914 
1915 	}
1916 	*dp++ = 0;
1917 	return (fmtres);
1918 }
1919 
1920 /*
1921  * fcmp - routine used by qsort.  Will sort first by name, then
1922  *	then by pathname length if names are equal.  Uses global
1923  *	cmp_level to tell what component of the path name we are comparing.
1924  */
1925 fcmp(f1, f2)
1926 	register struct filenames	*f1, *f2;
1927 {
1928 	int 				value;
1929 
1930 	if ((value = strcmp(f1->fname[cmp_level], f2->fname[cmp_level])))
1931 		return(value);
1932 	return (f1->len - f2->len);
1933 }
1934 
1935 /*
1936  * ffcmp - routine used by qsort.  Sort only by pathname length.
1937  */
1938 ffcmp(f1, f2)
1939 	register struct filenames	*f1, *f2;
1940 {
1941 	return (f1->len - f2->len);
1942 }
1943 
1944 /*
1945  * parse - set up the call to follow_path.
1946  */
1947 parse()
1948 {
1949 	register int	i, j;
1950 	char		c;
1951 
1952 	stack_pathp = input_pathp = -1;
1953 	if ((c = getachar()) == '/') {
1954 		while ((c = getachar()) == '/')
1955 			;
1956 		ungetachar(c);
1957 		cur_inum = 2;
1958 		if ((c = getachar()) == '\n') {
1959 			ungetachar('\n');
1960 			if (doing_cd) {
1961 				top++;
1962 				top->ino = 2;
1963 				top->len = -1;
1964 				nfiles = 1;
1965 				return;
1966 			}
1967 		} else
1968 			ungetachar(c);
1969 	} else {
1970 		ungetachar(c);
1971 		stack_pathp = current_pathp;
1972 		if (!doing_find)
1973 			input_pathp = current_pathp;
1974 		for (i = 0; i <= current_pathp; i++) {
1975 			if (!doing_find)
1976 				strcpy(input_path[i], current_path[i]);
1977 			strcpy(stack_path[i], current_path[i]);
1978 		}
1979 	}
1980 	getname();
1981 	follow_path(stack_pathp + 1, cur_inum);
1982 }
1983 
1984 /*
1985  * follow_path - called by cd, find, and ls.
1986  *	input_path holds the name typed by the user.
1987  *	stack_path holds the name at the current depth.
1988  */
1989 follow_path(level, inum)
1990 	long			level, inum;
1991 {
1992 	register struct direct	*dirp;
1993 	register char		**ccptr, *cptr, c;
1994 	register int		i;
1995 	struct filenames	*tos, *bos, *fn, *fnn, *fnnn;
1996 	long			block;
1997 	short			mode;
1998 
1999 	tos = top + 1;
2000 	restore_inode(inum);
2001 	if ((mode = icheck(addr)) == 0)
2002 		return;
2003 	if ((mode & IFMT) != IFDIR)
2004 	    return;
2005 	block = cur_bytes = 0;
2006 	while (cur_bytes < filesize) {
2007 	    if (block == 0 || bcomp(addr)) {
2008 		error = 0;
2009 		if ((addr = (bmap(block++) << FRGSHIFT)) == 0)
2010 		    break;
2011 		if ((cptr = getblk(addr)) == 0)
2012 		    break;
2013 		cptr += blkoff(fs, addr);
2014 	    }
2015 	    dirp = (struct direct *)cptr;
2016 	    if (dirp->d_ino) {
2017 		if (level > input_pathp || doing_find ||
2018 			compare(input_path[level], &dirp->d_name[0], 1)) {
2019 		    if (++top - filenames >= MAXFILES) {
2020 			printf("too many files\n");
2021 			error++;
2022 			return;
2023 		    }
2024 		    top->fname = (char **)calloc(FIRST_DEPTH, sizeof (char **));
2025 		    top->flag = 0;
2026 		    if (top->fname == 0) {
2027 			printf("out of memory\n");
2028 			error++;
2029 			return;
2030 		    }
2031 		    nfiles++;
2032 		    top->ino = dirp->d_ino;
2033 		    top->len = stack_pathp;
2034 		    top->find = 0;
2035 		    if (doing_find) {
2036 			if (find_by_name) {
2037 			    if (compare(input_path[0], &dirp->d_name[0], 1))
2038 				top->find = 1;
2039 			} else if (find_by_inode)
2040 			    if (find_ino == dirp->d_ino)
2041 				top->find = 1;
2042 		    }
2043 		    if (top->len + 1 >= FIRST_DEPTH && top->flag == 0) {
2044 			ccptr = (char **)calloc(SECOND_DEPTH, sizeof (char **));
2045 			if (ccptr == 0) {
2046 			    printf("out of memory\n");
2047 			    error++;
2048 			    return;
2049 			}
2050 			for (i = 0; i < FIRST_DEPTH; i++)
2051 				ccptr[i] = top->fname[i];
2052 			free((char *)top->fname);
2053 			top->fname = ccptr;
2054 			top->flag = 1;
2055 		    }
2056 		    if (top->len >= SECOND_DEPTH) {
2057 			printf("maximum depth exceeded, try to cd lower\n");
2058 			error++;
2059 			return;
2060 		    }
2061 		    /*
2062 		     * Copy current depth.
2063 		     */
2064 		    for (i = 0; i <= stack_pathp; i++) {
2065 			top->fname[i]=calloc(1, strlen(stack_path[i])+1);
2066 			if (top->fname[i] == 0) {
2067 			    printf("out of memory\n");
2068 			    error++;
2069 			    return;
2070 			}
2071 			strcpy(top->fname[i], stack_path[i]);
2072 		    }
2073 		    /*
2074 		     * Check for '.' or '..' typed.
2075 		     */
2076 		    if ((level <= input_pathp) &&
2077 				       (strcmp(input_path[level], ".") == 0 ||
2078 					strcmp(input_path[level], "..") == 0)) {
2079 			if (strcmp(input_path[level],"..") == 0 &&
2080 							 top->len >= 0) {
2081 			    free(top->fname[top->len]);
2082 			    top->len -= 1;
2083 			}
2084 		    } else {
2085 			/*
2086 			 * Check for duplicates.
2087 			 */
2088 			if (!doing_cd && !doing_find) {
2089 			    for (fn = filenames; fn < top; fn++) {
2090 				if (fn->ino == dirp->d_ino &&
2091 					    fn->len == stack_pathp + 1) {
2092 				    for (i = 0; i < fn->len; i++)
2093 					if (strcmp(fn->fname[i], stack_path[i]))
2094 					    break;
2095 				    if (i != fn->len ||
2096 					    strcmp(fn->fname[i], dirp->d_name))
2097 					continue;
2098 				    freemem(top, 1);
2099 				    if (top == filenames)
2100 					top = NULL;
2101 				    else
2102 					top--;
2103 					nfiles--;
2104 					goto duplicate;
2105 				}
2106 			    }
2107 			}
2108 			top->len += 1;
2109 			top->fname[top->len] = calloc(1,
2110 						strlen(&dirp->d_name[0])+1);
2111 			if (top->fname[top->len] == 0) {
2112 			    printf("out of memory\n");
2113 			    error++;
2114 			    return;
2115 			}
2116 			strcpy(top->fname[top->len], &dirp->d_name[0]);
2117 		    }
2118 		}
2119 	    }
2120 duplicate:
2121 	    addr += dirp->d_reclen;
2122 	    cptr += dirp->d_reclen;
2123 	    cur_bytes += dirp->d_reclen;
2124 	}
2125 	if (top < filenames)
2126 	    return;
2127 	if ((doing_cd && level == input_pathp) ||
2128 		(!recursive && !doing_find && level > input_pathp))
2129 	    return;
2130 	bos = top;
2131 	/*
2132 	 * Check newly added entries to determine if further expansion
2133 	 * is required.
2134 	 */
2135 	for (fn = tos; fn <= bos; fn++) {
2136 	    /*
2137 	     * Avoid '.' and '..' if beyond input.
2138 	     */
2139 	    if ((recursive || doing_find) && (level > input_pathp) &&
2140 		(strcmp(fn->fname[fn->len], ".") == 0 ||
2141 		 strcmp(fn->fname[fn->len], "..") == 0))
2142 		 continue;
2143 	    restore_inode(fn->ino);
2144 	    if ((mode = icheck(cur_ino)) == 0)
2145 		return;
2146 	    if ((mode & IFMT) == IFDIR || level < input_pathp) {
2147 		/*
2148 		 * Set up current depth, remove current entry and
2149 		 * continue recursion.
2150 		 */
2151 		for (i = 0; i <= fn->len; i++)
2152 		    strcpy(stack_path[i], fn->fname[i]);
2153 		stack_pathp = fn->len;
2154 		if (!doing_find &&
2155 			(!recursive || (recursive && level <= input_pathp))) {
2156 		    /*
2157 		     * Remove current entry by moving others up.
2158 		     */
2159 		    freemem(fn, 1);
2160 		    fnn = fn;
2161 		    for (fnnn = fnn, fnn++; fnn <= top; fnnn = fnn, fnn++) {
2162 			fnnn->ino = fnn->ino;
2163 			fnnn->len = fnn->len;
2164 			if (fnnn->len + 1 < FIRST_DEPTH) {
2165 			    fnnn->fname = (char **)calloc(FIRST_DEPTH,
2166 							sizeof (char **));
2167 			    fnnn->flag = 0;
2168 			} else if (fnnn->len < SECOND_DEPTH) {
2169 			    fnnn->fname = (char **)calloc(SECOND_DEPTH,
2170 							sizeof (char **));
2171 			    fnnn->flag = 1;
2172 			} else {
2173 			    printf("maximum depth exceeded, ");
2174 			    printf("try to cd lower\n");
2175 			    error++;
2176 			    return;
2177 			}
2178 			for (i = 0; i <= fnn->len; i++)
2179 			    fnnn->fname[i] = fnn->fname[i];
2180 		    }
2181 		    if (fn == tos)
2182 			fn--;
2183 		    top--;
2184 		    bos--;
2185 		    nfiles--;
2186 		}
2187 		follow_path(level + 1, cur_inum);
2188 		if (error)
2189 			return;
2190 	    }
2191 	}
2192 }
2193 
2194 /*
2195  * getname - break up the pathname entered by the user into components.
2196  */
2197 getname()
2198 {
2199 	register int	i;
2200 	char		c;
2201 
2202 	if ((c = getachar()) == '\n') {
2203 	    ungetachar(c);
2204 	    return;
2205 	}
2206 	ungetachar(c);
2207 	input_pathp++;
2208 clear:
2209 	for (i = 0; i < MAXNAMLEN; i++)
2210 	    input_path[input_pathp][i] = '\0';
2211 	for (;;) {
2212 	    c = getachar();
2213 	    if (c == '\\') {
2214 		if (strlen(input_path[input_pathp]) + 1 >= MAXNAMLEN) {
2215 		    printf("maximum name length exceeded, ");
2216 		    printf("truncating\n");
2217 		    return;
2218 		}
2219 		input_path[input_pathp][strlen(input_path[input_pathp])] = c;
2220 		input_path[input_pathp][strlen(input_path[input_pathp])] =
2221 						getachar();
2222 		continue;
2223 	    }
2224 	    if (c == ' ' || c == '\n') {
2225 		ungetachar(c);
2226 		return;
2227 	    }
2228 	    if (!doing_find && c == '/') {
2229 		if (++input_pathp >= MAXPATHLEN) {
2230 		    printf("maximum path length exceeded, ");
2231 		    printf("truncating\n");
2232 		    input_pathp--;
2233 		    return;
2234 		}
2235 		goto clear;
2236 	    }
2237 	    if (strlen(input_path[input_pathp]) >= MAXNAMLEN) {
2238 		printf("maximum name length exceeded, truncating\n");
2239 		return;
2240 	    }
2241 	    input_path[input_pathp][strlen(input_path[input_pathp])] = c;
2242 	}
2243 }
2244 
2245 /*
2246  * compare - check if a filename matches the pattern entered by the user.
2247  *	Handles '*', '?', and '[]'.
2248  */
2249 compare(s1, s2, at_start)
2250 	char		*s1, *s2;
2251 	short		at_start;
2252 {
2253 	register char	c, *s;
2254 
2255 	s = s2;
2256 	while (c = *s1) {
2257 		if (c == '*') {
2258 			if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
2259 				return(0);
2260 			if (*++s1 == 0)
2261 				return(1);
2262 			while (*s2) {
2263 				if (compare(s1, s2, 0))
2264 					return(1);
2265 				if (error)
2266 					return(0);
2267 				s2++;
2268 			}
2269 		}
2270 		if (*s2 == 0)
2271 			return(0);
2272 		if (c == '\\') {
2273 			s1++;
2274 			goto compare_chars;
2275 		}
2276 		if (c == '?') {
2277 			if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
2278 				return(0);
2279 			s1++;
2280 			s2++;
2281 			continue;
2282 		}
2283 		if (c == '[') {
2284 			s1++;
2285 			if (*s2 >= *s1++) {
2286 				if (*s1++ != '-') {
2287 					printf("missing '-'\n");
2288 					error++;
2289 					return(0);
2290 				}
2291 				if (*s2 <= *s1++) {
2292 					if (*s1++ != ']') {
2293 						printf("missing ']'");
2294 						error++;
2295 						return(0);
2296 					}
2297 					s2++;
2298 					continue;
2299 				}
2300 			}
2301 		}
2302 compare_chars:
2303 		if (*s1++ == *s2++)
2304 			continue;
2305 		else
2306 			return(0);
2307 	}
2308 	if (*s1 == *s2)
2309 		return(1);
2310 	return(0);
2311 }
2312 
2313 /*
2314  * freemem - free the memory allocated to the filenames structure.
2315  */
2316 freemem(p, numb)
2317 	struct filenames	*p;
2318 	int			numb;
2319 {
2320 	register int		i, j;
2321 
2322 	if (numb == 0)
2323 		return;
2324 	for (i = 0; i < numb; i++, p++) {
2325 		for (j = 0; j <= p->len; j++)
2326 			free(p->fname[j]);
2327 		free((char *)p->fname);
2328 	}
2329 }
2330 
2331 /*
2332  * print_path - print the pathname held in p.
2333  */
2334 print_path(p, pntr)
2335 	char		*p[];
2336 	short		pntr;
2337 {
2338 	register int	i;
2339 
2340 	printf("/");
2341 	if (pntr >= 0) {
2342 		for (i = 0; i < pntr; i++)
2343 			printf("%s/", p[i]);
2344 		printf("%s", p[pntr]);
2345 	}
2346 }
2347 
2348 /*
2349  * fill - fill a section with a value or string.
2350  *	addr,count:fill=[value, "string"].
2351  */
2352 fill()
2353 {
2354 	register char	*cptr;
2355 	register int	i;
2356 	short		eof_flag, end = 0, eof = 0;
2357 	long		temp, tcount, taddr;
2358 
2359 	if (!wrtflag) {
2360 		printf("not opened for write '-w'\n");
2361 		error++;
2362 		return;
2363 	}
2364 	temp = expr();
2365 	if (error)
2366 		return;
2367 	if ((cptr = getblk(addr)) == 0)
2368 		return;
2369 	if (type == NUMB)
2370 		eof_flag = 0;
2371 	else
2372 		eof_flag = 1;
2373 	taddr = addr;
2374 	switch (objsz) {
2375 	case LONG:
2376 		addr &= ~(LONG - 1);
2377 		break;
2378 	case SHORT:
2379 		addr &= ~(SHORT - 1);
2380 		temp &= 0177777L;
2381 		break;
2382 	case CHAR:
2383 		temp &= 0377;
2384 	}
2385 	cur_bytes -= taddr - addr;
2386 	cptr += blkoff(fs, addr);
2387 	tcount = check_addr(eof_flag, &end, &eof, 0);
2388 	for (i = 0; i < tcount; i++) {
2389 		switch (objsz) {
2390 		case LONG:
2391 			*(long *)cptr = temp;
2392 			break;
2393 		case SHORT:
2394 			*(short *)cptr = temp;
2395 			break;
2396 		case CHAR:
2397 			*cptr = temp;
2398 		}
2399 		cptr += objsz;
2400 	}
2401 	addr += (tcount - 1) * objsz;
2402 	cur_bytes += (tcount - 1) * objsz;
2403 	put(temp, objsz);
2404 	if (eof) {
2405 		printf("end of file\n");
2406 		error++;
2407 	} else if (end) {
2408 		printf("end of block\n");
2409 		error++;
2410 	}
2411 }
2412 
2413 /*
2414  * get - read a byte, short or long from the file system.
2415  *	The entire block containing the desired item is read
2416  *	and the appropriate data is extracted and returned.
2417  */
2418 long
2419 get(lngth)
2420 	short		lngth;
2421 {
2422 
2423 	register char	*bptr;
2424 	long		temp = addr;
2425 
2426 	objsz = lngth;
2427 	if (objsz == INODE || objsz == SHORT)
2428 		temp &= ~(SHORT - 1);
2429 	else if (objsz == DIRECTORY || objsz == LONG)
2430 		temp &= ~(LONG - 1);
2431 	if ((bptr = getblk(temp)) == 0)
2432 		return(-1);
2433 	bptr += blkoff(fs, temp);
2434 	switch (objsz) {
2435 	case CHAR:
2436 		return((long)*bptr);
2437 	case SHORT:
2438 	case INODE:
2439 		return((long)(*(short *)bptr));
2440 	case LONG:
2441 	case DIRECTORY:
2442 		return(*(long *)bptr);
2443 	}
2444 	return(0);
2445 }
2446 
2447 /*
2448  * cgrp_check - make sure that we don't bump the cylinder group
2449  *	beyond the total number of cylinder groups or before the start.
2450  */
2451 cgrp_check(cgrp)
2452 	long		cgrp;
2453 {
2454 	if (cgrp < 0) {
2455 		if (objsz == CGRP)
2456 			printf("beginning of cylinder groups\n");
2457 		else
2458 			printf("beginning of super blocks\n");
2459 		error++;
2460 		return(0);
2461 	}
2462 	if (cgrp >= fs->fs_ncg) {
2463 		if (objsz == CGRP)
2464 			printf("end of cylinder groups\n");
2465 		else
2466 			printf("end of super blocks\n");
2467 		error++;
2468 		return(0);
2469 	}
2470 	if (objsz == CGRP)
2471 		return(cgtod(fs, cgrp) << FRGSHIFT);
2472 	else
2473 		return(cgsblock(fs, cgrp) << FRGSHIFT);
2474 }
2475 
2476 /*
2477  * icheck -  make sure we can read the block containing the inode
2478  *	and determine the filesize (0 if inode not allocated).  Return
2479  *	0 if error otherwise return the mode.
2480  */
2481 icheck(address)
2482 	long			address;
2483 {
2484 	register char		*cptr;
2485 	register struct dinode	*ip;
2486 
2487 	if ((cptr = getblk(address)) == 0)
2488 		return(0);
2489 	cptr += blkoff(fs, address);
2490 	ip = (struct dinode *)cptr;
2491 	if ((ip->di_mode & IFMT) == 0) {
2492 		if (!override) {
2493 			printf("inode not allocated\n");
2494 			error++;
2495 			return(0);
2496 		}
2497 		blocksize = filesize = 0;
2498 	} else {
2499 		trapped++;
2500 		filesize = ip->di_size;
2501 		blocksize = filesize * 2;
2502 	}
2503 	return(ip->di_mode);
2504 }
2505 
2506 /*
2507  * getdirslot - get the address of the directory slot desired.
2508  */
2509 getdirslot(slot)
2510 	short			slot;
2511 {
2512 	register char		*cptr;
2513 	register struct direct	*dirp;
2514 	register short		i;
2515 	char			*string = &scratch[0];
2516 	short			bod = 0, mode, temp;
2517 
2518 	if (slot < 0) {
2519 		slot = 0;
2520 		bod++;
2521 	}
2522 	if (type != DIRECTORY) {
2523 		if (type == BLOCK)
2524 			string = "block";
2525 		else
2526 			string = "fragment";
2527 		addr = bod_addr;
2528 		if ((cptr = getblk(addr)) == 0)
2529 			return(0);
2530 		cptr += blkoff(fs, addr);
2531 		cur_bytes = 0;
2532 		dirp = (struct direct *)cptr;
2533 		for (dirslot = 0; dirslot < slot; dirslot++) {
2534 			dirp = (struct direct *)cptr;
2535 			if (blocksize > filesize) {
2536 				if (cur_bytes + dirp->d_reclen >= filesize) {
2537 					printf("end of file\n");
2538 					erraddr = addr;
2539 					errcur_bytes = cur_bytes;
2540 					stringsize = STRINGSIZE(dirp);
2541 					error++;
2542 					return(addr);
2543 				}
2544 			} else {
2545 				if (cur_bytes + dirp->d_reclen >= blocksize) {
2546 					printf("end of %s\n", string);
2547 					erraddr = addr;
2548 					errcur_bytes = cur_bytes;
2549 					stringsize = STRINGSIZE(dirp);
2550 					error++;
2551 					return(addr);
2552 				}
2553 			}
2554 			cptr += dirp->d_reclen;
2555 			addr += dirp->d_reclen;
2556 			cur_bytes += dirp->d_reclen;
2557 		}
2558 		if (bod) {
2559 			if (blocksize > filesize)
2560 				printf("beginning of file\n");
2561 			else
2562 				printf("beginning of %s\n", string);
2563 			erraddr = addr;
2564 			errcur_bytes = cur_bytes;
2565 			error++;
2566 		}
2567 		stringsize = STRINGSIZE(dirp);
2568 		return(addr);
2569 	} else {
2570 		addr = cur_ino;
2571 		if ((mode = icheck(addr)) == 0)
2572 			return(0);
2573 		if (!override && (mode & IFDIR) == 0) {
2574 			printf("inode is not a directory\n");
2575 			error++;
2576 			return(0);
2577 		}
2578 		temp = slot;
2579 		i = cur_bytes = 0;
2580 		for (;;) {
2581 			if (i == 0 || bcomp(addr)) {
2582 				error = 0;
2583 				if ((addr=(bmap(i++) << FRGSHIFT)) == 0)
2584 					break;
2585 				if ((cptr = getblk(addr)) == 0)
2586 					break;
2587 				cptr += blkoff(fs, addr);
2588 			}
2589 			dirp = (struct direct *)cptr;
2590 			value = dirp->d_ino;
2591 			if (!temp--)
2592 				break;
2593 			if (cur_bytes + dirp->d_reclen >= filesize) {
2594 				printf("end of file\n");
2595 				dirslot = slot - temp - 1;
2596 				objsz = DIRECTORY;
2597 				erraddr = addr;
2598 				errcur_bytes = cur_bytes;
2599 				stringsize = STRINGSIZE(dirp);
2600 				error++;
2601 				return(addr);
2602 			}
2603 			addr += dirp->d_reclen;
2604 			cptr += dirp->d_reclen;
2605 			cur_bytes += dirp->d_reclen;
2606 		}
2607 		dirslot = slot;
2608 		objsz = DIRECTORY;
2609 		if (bod) {
2610 			printf("beginning of file\n");
2611 			erraddr = addr;
2612 			errcur_bytes = cur_bytes;
2613 			error++;
2614 		}
2615 		stringsize = STRINGSIZE(dirp);
2616 		return(addr);
2617 	}
2618 }
2619 
2620 /*
2621  * putf - print a byte as an ascii character if possible.
2622  *	The exceptions are tabs, newlines, backslashes
2623  *	and nulls which are printed as the standard C
2624  *	language escapes. Characters which are not
2625  *	recognized are printed as \?.
2626  */
2627 putf(c)
2628 	register	char  c;
2629 {
2630 
2631 	if (c<=037 || c>=0177 || c=='\\') {
2632 		printf("\\");
2633 		switch (c) {
2634 		case '\\':
2635 			printf("\\");
2636 			break;
2637 		case '\t':
2638 			printf("t");
2639 			break;
2640 		case '\n':
2641 			printf("n");
2642 			break;
2643 		case '\0':
2644 			printf("0");
2645 			break;
2646 		default:
2647 			printf("?");
2648 		}
2649 	}
2650 	else {
2651 		printf("%c", c);
2652 		printf(" ");
2653 	}
2654 }
2655 
2656 /*
2657  * put - write an item into the buffer for the current address
2658  *	block.  The value is checked to make sure that it will
2659  *	fit in the size given without truncation.  If successful,
2660  *	the entire block is written back to the file system.
2661  */
2662 put(item,lngth)
2663 	long		item;
2664 	short		lngth;
2665 {
2666 
2667 	register char	*bptr, *sbptr;
2668 	register long	*vptr;
2669 	long		s_err,nbytes;
2670 	long		olditem;
2671 
2672 	if (!wrtflag) {
2673 		printf("not opened for write '-w'\n");
2674 		error++;
2675 		return;
2676 	}
2677 	objsz = lngth;
2678 	if ((sbptr = getblk(addr)) == 0)
2679 		return;
2680 	bptr = sbptr + blkoff(fs, addr);
2681 	switch (objsz) {
2682 	case LONG:
2683 	case DIRECTORY:
2684 		olditem = *(long *)bptr;
2685 		*(long *)bptr = item;
2686 		break;
2687 	case SHORT:
2688 	case INODE:
2689 		olditem = (long)*(short *)bptr;
2690 		item &= 0177777L;
2691 		*(short *)bptr = item;
2692 		break;
2693 	case CHAR:
2694 		olditem = (long)*bptr;
2695 		item &= 0377;
2696 		*bptr = lobyte(loword(item));
2697 		break;
2698 	default:
2699 		error++;
2700 		return;
2701 	}
2702 	if ((s_err = lseek(fd, addr & fs->fs_bmask, 0)) == -1) {
2703 		error++;
2704 		printf("seek error : %x\n",addr);
2705 		return(0);
2706 	}
2707 	if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
2708 		error++;
2709 		printf("write error : addr   = %x\n",addr);
2710 		printf("            : s_err  = %x\n",s_err);
2711 		printf("            : nbytes = %x\n",nbytes);
2712 		return(0);
2713 	}
2714 	if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
2715 		index(base);
2716 		print(olditem, 8, -8, 0);
2717 		printf("\t=\t");
2718 		print(item, 8, -8, 0);
2719 		printf("\n");
2720 	} else {
2721 		if (objsz == DIRECTORY) {
2722 			addr = cur_dir;
2723 			fprnt('?', 'd');
2724 		} else {
2725 			addr = cur_ino;
2726 			objsz = INODE;
2727 			fprnt('?', 'i');
2728 		}
2729 	}
2730 	return;
2731 }
2732 
2733 /*
2734  * getblk - check if the desired block is in the file system.
2735  *	Search the incore buffers to see if the block is already
2736  *	available. If successful, unlink the buffer control block
2737  *	from its position in the buffer list and re-insert it at
2738  *	the head of the list.  If failure, use the last buffer
2739  *	in the list for the desired block. Again, this control
2740  *	block is placed at the head of the list. This process
2741  *	will leave commonly requested blocks in the in-core buffers.
2742  *	Finally, a pointer to the buffer is returned.
2743  */
2744 char *
2745 getblk(address)
2746 	long			address;
2747 {
2748 
2749 	register struct buf	*bp;
2750 	long			s_err, nbytes;
2751 	unsigned long		block;
2752 
2753 	read_requests++;
2754 	block = lblkno(fs, address);
2755 	if (block >= fragstoblks(fs, fs->fs_size)) {
2756 		printf("block exceeds maximum block in file system\n");
2757 		error++;
2758 		return(0);
2759 	}
2760 	for (bp=bhdr.fwd; bp!= &bhdr; bp=bp->fwd)
2761 		if (bp->valid && bp->blkno==block)
2762 			goto xit;
2763 	actual_disk_reads++;
2764 	bp = bhdr.back;
2765 	bp->blkno = block;
2766 	bp->valid = 0;
2767 	if ((s_err = lseek(fd, address & fs->fs_bmask, 0)) == -1) {
2768 		error++;
2769 		printf("seek error : %x\n",address);
2770 		return(0);
2771 	}
2772 	if ((nbytes = read(fd, bp->blkaddr, BLKSIZE)) != BLKSIZE) {
2773 		error++;
2774 		printf("read error : addr   = %x\n",address);
2775 		printf("           : s_err  = %x\n",s_err);
2776 		printf("           : nbytes = %x\n",nbytes);
2777 		return(0);
2778 	}
2779 	bp->valid++;
2780 xit:	bp->back->fwd = bp->fwd;
2781 	bp->fwd->back = bp->back;
2782 	insert(bp);
2783 	return(bp->blkaddr);
2784 }
2785 
2786 /*
2787  * insert - place the designated buffer control block
2788  *	at the head of the linked list of buffers.
2789  */
2790 insert(bp)
2791 	register struct buf	*bp;
2792 {
2793 
2794 	bp->back = &bhdr;
2795 	bp->fwd = bhdr.fwd;
2796 	bhdr.fwd->back = bp;
2797 	bhdr.fwd = bp;
2798 }
2799 
2800 /*
2801  * err - called on interrupts.  Set the current address
2802  *	back to the last address stored in erraddr. Reset all
2803  *	appropriate flags.  A reset call is made to return
2804  *	to the main loop;
2805  */
2806 err()
2807 {
2808 	freemem(filenames, nfiles);
2809 	nfiles = 0;
2810 	signal(2,err);
2811 	addr = erraddr;
2812 	cur_ino = errino;
2813 	cur_inum = errinum;
2814 	cur_bytes = errcur_bytes;
2815 	error = 0;
2816 	c_count = 0;
2817 	printf("\n?\n");
2818 	fseek(stdin, 0L, 2);
2819 	longjmp(env,0);
2820 }
2821 
2822 /*
2823  * devcheck - check that the given mode represents a
2824  *	special device. The IFCHR bit is on for both
2825  *	character and block devices.
2826  */
2827 devcheck(md)
2828 	register	short md;
2829 {
2830 	if (override)
2831 		return(0);
2832 	if (md & IFCHR)
2833 		return(0);
2834 	printf("not character or block device\n");
2835 	error++;
2836 	return(1);
2837 }
2838 
2839 /*
2840  * nullblk - return error if address is zero.  This is done
2841  *	to prevent block 0 from being used as an indirect block
2842  *	for a large file or as a data block for a small file.
2843  */
2844 nullblk(bn)
2845 	long		bn;
2846 {
2847 	if (bn != 0)
2848 		return(0);
2849 	printf("non existent block\n");
2850 	error++;
2851 	return(1);
2852 }
2853 
2854 /*
2855  * puta - put ascii characters into a buffer.  The string
2856  *	terminates with a quote or newline.  The leading quote,
2857  *	which is optional for directory names, was stripped off
2858  *	by the assignment case in the main loop.
2859  */
2860 puta()
2861 {
2862 	register char		*cptr, c;
2863 	register int		i;
2864 	char			*sbptr;
2865 	short			terror = 0;
2866 	long			maxchars, s_err, nbytes, temp;
2867 	long			taddr = addr, tcount = 0, item, olditem = 0;
2868 
2869 	if (!wrtflag) {
2870 		printf("not opened for write '-w'\n");
2871 		error++;
2872 		return;
2873 	}
2874 	if ((sbptr = getblk(addr)) == 0)
2875 		return;
2876 	cptr = sbptr + blkoff(fs, addr);
2877 	if (objsz == DIRECTORY) {
2878 		if (acting_on_directory)
2879 			maxchars = stringsize - 1;
2880 		else
2881 			maxchars = LONG;
2882 	} else if (objsz == INODE)
2883 		maxchars = objsz - (addr - cur_ino);
2884 	else
2885 		maxchars = min(blocksize - cur_bytes, filesize - cur_bytes);
2886 	while ((c = getachar()) != '"') {
2887 		if (tcount >= maxchars) {
2888 			printf("string too long\n");
2889 			if (objsz == DIRECTORY)
2890 				addr = cur_dir;
2891 			else if (acting_on_inode || objsz == INODE)
2892 				addr = cur_ino;
2893 			else
2894 				addr = taddr;
2895 			erraddr = addr;
2896 			errcur_bytes = cur_bytes;
2897 			terror++;
2898 			break;
2899 		}
2900 		tcount++;
2901 		if (c == '\n') {
2902 			ungetachar(c);
2903 			break;
2904 		}
2905 		temp = (long)*cptr;
2906 		olditem <<= BITSPERCHAR;
2907 		olditem += temp & 0xff;
2908 		if (c == '\\') {
2909 			switch (c = getachar()) {
2910 			case 't':
2911 				*cptr++ = '\t';
2912 				break;
2913 			case 'n':
2914 				*cptr++ = '\n';
2915 				break;
2916 			case '0':
2917 				*cptr++ = '\0';
2918 				break;
2919 			default:
2920 				*cptr++ = c;
2921 				break;
2922 			}
2923 		}
2924 		else
2925 			*cptr++ = c;
2926 	}
2927 	if (objsz == DIRECTORY && acting_on_directory)
2928 		for (i = tcount; i <= maxchars; i++)
2929 			*cptr++ = '\0';
2930 	if ((s_err = lseek(fd, addr & fs->fs_bmask, 0)) == -1) {
2931 		error++;
2932 		printf("seek error : %x\n",addr);
2933 		return(0);
2934 	}
2935 	if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
2936 		error++;
2937 		printf("write error : addr   = %x\n",addr);
2938 		printf("            : s_err  = %x\n",s_err);
2939 		printf("            : nbytes = %x\n",nbytes);
2940 		return(0);
2941 	}
2942 	if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
2943 		addr += tcount;
2944 		cur_bytes += tcount;
2945 		taddr = addr;
2946 		if (objsz != CHAR) {
2947 			addr &= ~(objsz - 1);
2948 			cur_bytes -= taddr - addr;
2949 		}
2950 		if (addr == taddr) {
2951 			addr -= objsz;
2952 			taddr = addr;
2953 		}
2954 		tcount = LONG - (taddr - addr);
2955 		index(base);
2956 		if ((cptr = getblk(addr)) == 0)
2957 			return;
2958 		cptr += blkoff(fs, addr);
2959 		switch (objsz) {
2960 		case LONG:
2961 			item = *(long *)cptr;
2962 			if (tcount < LONG) {
2963 				olditem <<= tcount * BITSPERCHAR;
2964 				temp = 1;
2965 				for (i = 0; i < (tcount*BITSPERCHAR); i++)
2966 					temp <<= 1;
2967 				olditem += item & (temp - 1);
2968 			}
2969 			break;
2970 		case SHORT:
2971 			item = (long)*(short *)cptr;
2972 			if (tcount < SHORT) {
2973 				olditem <<= tcount * BITSPERCHAR;
2974 				temp = 1;
2975 				for (i = 0; i < (tcount * BITSPERCHAR); i++)
2976 					temp <<= 1;
2977 				olditem += item & (temp - 1);
2978 			}
2979 			olditem &= 0177777L;
2980 			break;
2981 		case CHAR:
2982 			item = (long)*cptr;
2983 			olditem &= 0377;
2984 		}
2985 		print(olditem, 8, -8, 0);
2986 		printf("\t=\t");
2987 		print(item, 8, -8, 0);
2988 		printf("\n");
2989 	} else {
2990 		if (objsz == DIRECTORY) {
2991 			addr = cur_dir;
2992 			fprnt('?', 'd');
2993 		} else {
2994 			addr = cur_ino;
2995 			objsz = INODE;
2996 			fprnt('?', 'i');
2997 		}
2998 	}
2999 	if (terror)
3000 		error++;
3001 }
3002 
3003 /*
3004  * fprnt - print data.  'count' elements are printed where '*' will
3005  *	print an entire blocks worth or up to the eof, whichever
3006  *	occurs first.  An error will occur if crossing a block boundary
3007  *	is attempted since consecutive blocks don't usually have
3008  *	meaning.  Current print types:
3009  *		/		b   - print as bytes (base sensitive)
3010  *				c   - print as characters
3011  *				o O - print as octal shorts (longs)
3012  *				d D - print as decimal shorts (longs)
3013  *				x X - print as hexadecimal shorts (longs)
3014  *		?		c   - print as cylinder groups
3015  *				d   - print as directories
3016  *				i   - print as inodes
3017  *				s   - print as super blocks
3018  */
3019 fprnt(style, po)
3020 	register char		style, po;
3021 {
3022 	register int		i;
3023 	register struct fs	*sb;
3024 	register struct cg	*cg;
3025 	register struct direct	*dirp;
3026 	register struct dinode	*ip;
3027 	int			tbase;
3028 	char			c, *cptr, *p;
3029 	long			tinode, tcount, temp, taddr;
3030 	short			offset, mode, end = 0, eof = 0, eof_flag;
3031 	unsigned short		*sptr;
3032 	unsigned long		*lptr;
3033 
3034 	laststyle = style;
3035 	lastpo = po;
3036 	should_print = 0;
3037 	if (count != 1) {
3038 		if (clear) {
3039 			count = 1;
3040 			star = 0;
3041 			clear = 0;
3042 		} else
3043 			clear = 1;
3044 	}
3045 	tcount = count;
3046 	offset = blkoff(fs, addr);
3047 
3048 	if (style == '/') {
3049 		if (type == NUMB)
3050 			eof_flag = 0;
3051 		else
3052 			eof_flag = 1;
3053 		switch (po) {
3054 
3055 		case 'c': /* print as characters */
3056 		case 'b': /* or bytes */
3057 			if ((cptr = getblk(addr)) == 0)
3058 				return;
3059 			cptr += offset;
3060 			objsz = CHAR;
3061 			tcount = check_addr(eof_flag, &end, &eof, 0);
3062 			if (tcount) {
3063 				for (i=0; tcount--; i++) {
3064 					if (i % 16 == 0) {
3065 						if (i)
3066 							printf("\n");
3067 						index(base);
3068 					}
3069 					if (po == 'c') {
3070 						putf(*cptr++);
3071 						if ((i + 1) % 16)
3072 							printf("  ");
3073 					} else {
3074 						if ((i + 1) % 16 == 0)
3075 							print(*cptr++ & 0377,
3076 								2,-2,0);
3077 						else
3078 							print(*cptr++ & 0377,
3079 								4,-2,0);
3080 					}
3081 					addr += CHAR;
3082 					cur_bytes += CHAR;
3083 				}
3084 				printf("\n");
3085 			}
3086 			addr -= CHAR;
3087 			erraddr = addr;
3088 			cur_bytes -= CHAR;
3089 			errcur_bytes = cur_bytes;
3090 			if (eof) {
3091 				printf("end of file\n");
3092 				error++;
3093 			} else if (end) {
3094 				if (type == BLOCK)
3095 					printf("end of block\n");
3096 				else
3097 					printf("end of fragment\n");
3098 				error++;
3099 			}
3100 			return;
3101 
3102 		case 'o': /* print as octal shorts */
3103 			tbase = OCTAL;
3104 			goto otx;
3105 		case 'd': /* print as decimal shorts */
3106 			tbase = DECIMAL;
3107 			goto otx;
3108 		case 'x': /* print as hex shorts */
3109 			tbase = HEX;
3110 otx:
3111 			if ((cptr = getblk(addr)) == 0)
3112 				return;
3113 			taddr = addr;
3114 			addr &= ~(SHORT - 1);
3115 			cur_bytes -= taddr - addr;
3116 			cptr += blkoff(fs, addr);
3117 			sptr = (unsigned short *)cptr;
3118 			objsz = SHORT;
3119 			tcount = check_addr(eof_flag, &end, &eof, 0);
3120 			if (tcount) {
3121 				for (i=0; tcount--; i++) {
3122 					sptr = (unsigned short *)
3123 					   print_check(sptr, &tcount, tbase, i);
3124 					switch (po) {
3125 					case 'o':
3126 						printf("%06o ",*sptr++);
3127 						break;
3128 					case 'd':
3129 						printf("%05d  ",*sptr++);
3130 						break;
3131 					case 'x':
3132 						printf("%04x   ",*sptr++);
3133 					}
3134 					addr += SHORT;
3135 					cur_bytes += SHORT;
3136 				}
3137 				printf("\n");
3138 			}
3139 			addr -= SHORT;
3140 			erraddr = addr;
3141 			cur_bytes -= SHORT;
3142 			errcur_bytes = cur_bytes;
3143 			if (eof) {
3144 				printf("end of file\n");
3145 				error++;
3146 			} else if (end) {
3147 				if (type == BLOCK)
3148 					printf("end of block\n");
3149 				else
3150 					printf("end of fragment\n");
3151 				error++;
3152 			}
3153 			return;
3154 
3155 		case 'O': /* print as octal longs */
3156 			tbase = OCTAL;
3157 			goto OTX;
3158 		case 'D': /* print as decimal longs */
3159 			tbase = DECIMAL;
3160 			goto OTX;
3161 		case 'X': /* print as hex longs */
3162 			tbase = HEX;
3163 OTX:
3164 			if ((cptr = getblk(addr)) == 0)
3165 				return;
3166 			taddr = addr;
3167 			addr &= ~(LONG - 1);
3168 			cur_bytes -= taddr - addr;
3169 			cptr += blkoff(fs, addr);
3170 			lptr = (unsigned long *)cptr;
3171 			objsz = LONG;
3172 			tcount = check_addr(eof_flag, &end, &eof, 0);
3173 			if (tcount) {
3174 				for (i=0; tcount--; i++) {
3175 					lptr =
3176 					   print_check(lptr, &tcount, tbase, i);
3177 					switch (po) {
3178 					case 'O':
3179 						printf("%011o    ",*lptr++);
3180 						break;
3181 					case 'D':
3182 						printf("%010u     ",*lptr++);
3183 						break;
3184 					case 'X':
3185 						printf("%08x       ",*lptr++);
3186 					}
3187 					addr += LONG;
3188 					cur_bytes += LONG;
3189 				}
3190 				printf("\n");
3191 			}
3192 			addr -= LONG;
3193 			erraddr = addr;
3194 			cur_bytes -= LONG;
3195 			errcur_bytes = cur_bytes;
3196 			if (eof) {
3197 				printf("end of file\n");
3198 				error++;
3199 			} else if (end) {
3200 				if (type == BLOCK)
3201 					printf("end of block\n");
3202 				else
3203 					printf("end of fragment\n");
3204 				error++;
3205 			}
3206 			return;
3207 
3208 		default:
3209 			error++;
3210 			printf("no such print option\n");
3211 			return;
3212 		}
3213 	} else
3214 		switch (po) {
3215 
3216 		case 'c': /* print as cylinder group */
3217 			if (type != NUMB)
3218 				if (cur_cgrp + count > fs->fs_ncg) {
3219 					tcount = fs->fs_ncg - cur_cgrp;
3220 					if (!star)
3221 						end++;
3222 				}
3223 			addr &= ~(LONG - 1);
3224 			for (; tcount--;) {
3225 				erraddr = addr;
3226 				errcur_bytes = cur_bytes;
3227 				if (type != NUMB) {
3228 					addr = cgtod(fs, cur_cgrp)
3229 						<< FRGSHIFT;
3230 					cur_cgrp++;
3231 				}
3232 				if ((cptr = getblk(addr)) == 0) {
3233 					if (cur_cgrp)
3234 						cur_cgrp--;
3235 					return;
3236 				}
3237 				cptr += blkoff(fs, addr);
3238 				cg = (struct cg *)cptr;
3239 				if (type == NUMB) {
3240 					cur_cgrp = cg->cg_cgx + 1;
3241 					type = objsz = CGRP;
3242 					if (cur_cgrp + count - 1 > fs->fs_ncg) {
3243 						tcount = fs->fs_ncg - cur_cgrp;
3244 						if (!star)
3245 							end++;
3246 					}
3247 				}
3248 				if (!override && !cg_chkmagic(cg)) {
3249 					printf("invalid cylinder group ");
3250 					printf("magic word\n");
3251 					if (cur_cgrp)
3252 						cur_cgrp--;
3253 					error++;
3254 					return;
3255 				}
3256 				printcg(cg);
3257 				if (tcount)
3258 					printf("\n");
3259 			}
3260 			cur_cgrp--;
3261 			if (end) {
3262 				printf("end of cylinder groups\n");
3263 				error++;
3264 			}
3265 			return;
3266 
3267 		case 'd': /* print as directories */
3268 			if ((cptr = getblk(addr)) == 0)
3269 				return;
3270 			if (type == NUMB) {
3271 				if (fragoff(fs, addr)) {
3272 					printf("address must be at the ");
3273 					printf("beginning of a fragment\n");
3274 					error++;
3275 					return;
3276 				}
3277 				bod_addr = addr;
3278 				type = FRAGMENT;
3279 				dirslot = 0;
3280 				cur_bytes = 0;
3281 				blocksize = FRGSIZE;
3282 				filesize = FRGSIZE * 2;
3283 			}
3284 			cptr += offset;
3285 			objsz = DIRECTORY;
3286 			while (tcount-- && cur_bytes < filesize &&
3287 			       cur_bytes < blocksize && !bcomp(addr)) {
3288 				dirp = (struct direct *)cptr;
3289 				tinode = dirp->d_ino;
3290 				printf("i#: ");
3291 				if (tinode == 0)
3292 					printf("free\t");
3293 				else
3294 					print(tinode, 12, -8, 0);
3295 				printf("%s\n",&dirp->d_name[0]);
3296 				erraddr = addr;
3297 				errcur_bytes = cur_bytes;
3298 				addr += dirp->d_reclen;
3299 				cptr += dirp->d_reclen;
3300 				cur_bytes += dirp->d_reclen;
3301 				dirslot++;
3302 			}
3303 			addr = erraddr;
3304 			cur_dir = addr;
3305 			cur_bytes = errcur_bytes;
3306 			stringsize = STRINGSIZE(dirp);
3307 			dirslot--;
3308 			if (tcount >= 0 && !star) {
3309 				switch (type) {
3310 				case FRAGMENT:
3311 					printf("end of fragment\n");
3312 					break;
3313 				case BLOCK:
3314 					printf("end of block\n");
3315 					break;
3316 				default:
3317 					printf("end of directory\n");
3318 				}
3319 				error++;
3320 			} else
3321 				error = 0;
3322 			return;
3323 
3324 		case 'i': /* print as inodes */
3325 			if ((ip = (struct dinode *)getblk(addr)) == 0)
3326 				return;
3327 			for (i=1; i < fs->fs_ncg; i++)
3328 				if (addr < (cgimin(fs,i) << FRGSHIFT))
3329 					break;
3330 			i--;
3331 			offset /= INODE;
3332 			temp = (addr - (cgimin(fs,i) << FRGSHIFT)) >> FRGSHIFT;
3333 			temp = (i * fs->fs_ipg) + fragstoblks(fs,temp) *
3334 							INOPB(fs) + offset;
3335 			if (count + offset > INOPB(fs)) {
3336 				tcount = INOPB(fs) - offset;
3337 				if (!star)
3338 					end++;
3339 			}
3340 			objsz = INODE;
3341 			ip += offset;
3342 			for (i=0; tcount--; ip++, temp++) {
3343 				if ((mode = icheck(addr)) == 0)
3344 					if (!override)
3345 						continue;
3346 				p = " ugtrwxrwxrwx";
3347 
3348 				switch (mode & IFMT) {
3349 				case IFDIR:
3350 					c = 'd';
3351 					break;
3352 				case IFCHR:
3353 					c = 'c';
3354 					break;
3355 				case IFBLK:
3356 					c = 'b';
3357 					break;
3358 				case IFREG:
3359 					c = '-';
3360 					break;
3361 				case IFLNK:
3362 					c = 'l';
3363 					break;
3364 				case IFSOCK:
3365 					c = 's';
3366 					break;
3367 				default:
3368 					c = '?';
3369 					if (!override)
3370 						goto empty;
3371 
3372 				}
3373 				printf("i#: ");
3374 				print(temp,12,-8,0);
3375 				printf("   md: ");
3376 				printf("%c", c);
3377 				for (mode = mode << 4; *++p; mode = mode << 1) {
3378 					if (mode & IFREG)
3379 						printf("%c", *p);
3380 					else
3381 						printf("-");
3382 				}
3383 				printf("  uid: ");
3384 				print(ip->di_uid,8,-4,0);
3385 				printf("      gid: ");
3386 				print(ip->di_gid,8,-4,0);
3387 				printf("\n");
3388 				printf("ln: ");
3389 				print(ip->di_nlink,8,-4,0);
3390 				printf("       bs: ");
3391 				print(ip->di_blocks,12,-8,0);
3392 				printf("   sz : ");
3393 				print(ip->di_size,12,-8,0);
3394 				printf("\n");
3395 				if (ip->di_mode & IFCHR) {
3396 					printf("maj: ");
3397 					print(ip->di_db[1] & 0377,4,-2,0);
3398 					printf("  min: ");
3399 					print(ip->di_db[0] & 0377,4,-2,0);
3400 					printf("\n");
3401 				} else {
3402 					for (i = 0; i < NDADDR; ) {
3403 						if (ip->di_db[i] == 0)
3404 							break;
3405 						printf("db#%x: ",i);
3406 						print(ip->di_db[i],11,-8,0);
3407 						if (++i % 4 == 0)
3408 							printf("\n");
3409 						else
3410 							printf("  ");
3411 					}
3412 					if (i % 4)
3413 						printf("\n");
3414 					for (i = 0; i < NIADDR; i++) {
3415 						if (ip->di_ib[i] == 0)
3416 							break;
3417 						printf("ib#%x: ",i);
3418 						print(ip->di_ib[i],11,-8,0);
3419 						printf("  ");
3420 					}
3421 					if (i)
3422 						printf("\n");
3423 				}
3424 				if (count == 1) {
3425 					printf("\taccessed: %s",
3426 						ctime(&ip->di_atime));
3427 					printf("\tmodified: %s",
3428 						ctime(&ip->di_mtime));
3429 					printf("\tcreated : %s",
3430 						ctime(&ip->di_ctime));
3431 				}
3432 				if (tcount)
3433 					printf("\n");
3434 empty:
3435 				if (c == '?' && !override) {
3436 					printf("i#: ");
3437 					print(temp, 12, -8, 0);
3438 					printf("  is unallocated\n");
3439 					if (count != 1)
3440 						printf("\n");
3441 				}
3442 				cur_ino = erraddr = addr;
3443 				errcur_bytes = cur_bytes;
3444 				cur_inum++;
3445 				addr = addr + INODE;
3446 			}
3447 			addr = erraddr;
3448 			cur_bytes = errcur_bytes;
3449 			cur_inum--;
3450 			if (end) {
3451 				printf("end of block\n");
3452 				error++;
3453 			}
3454 			return;
3455 
3456 		case 's': /* print as super block */
3457 			if (cur_cgrp == -1) {
3458 				addr = SBLOCK * DEV_BSIZE;
3459 				type = NUMB;
3460 			}
3461 			addr &= ~(LONG - 1);
3462 			if (type != NUMB)
3463 				if (cur_cgrp + count > fs->fs_ncg) {
3464 					tcount = fs->fs_ncg - cur_cgrp;
3465 					if (!star)
3466 						end++;
3467 				}
3468 			for (; tcount--;) {
3469 				erraddr = addr;
3470 				cur_bytes = errcur_bytes;
3471 				if (type != NUMB) {
3472 					addr = cgsblock(fs, cur_cgrp)
3473 							<< FRGSHIFT;
3474 					cur_cgrp++;
3475 				}
3476 				if ((cptr = getblk(addr)) == 0) {
3477 					if (cur_cgrp)
3478 						cur_cgrp--;
3479 					return;
3480 				}
3481 				cptr += blkoff(fs, addr);
3482 				sb = (struct fs *)cptr;
3483 				if (type == NUMB) {
3484 					for (i = 0; i < fs->fs_ncg; i++)
3485 						if (addr == cgsblock(fs, i) <<
3486 								FRGSHIFT)
3487 							break;
3488 					if (i == fs->fs_ncg)
3489 						cur_cgrp = 0;
3490 					else
3491 						cur_cgrp = i + 1;
3492 					type = objsz = SB;
3493 					if (cur_cgrp + count - 1 > fs->fs_ncg) {
3494 						tcount = fs->fs_ncg - cur_cgrp;
3495 						if (!star)
3496 							end++;
3497 					}
3498 				}
3499 				if (sb->fs_magic != FS_MAGIC) {
3500 					cur_cgrp = 0;
3501 					if (!override) {
3502 						printf("invalid super block ");
3503 						printf("magic word\n");
3504 						cur_cgrp--;
3505 						error++;
3506 						return;
3507 					}
3508 				}
3509 				if (cur_cgrp == 0)
3510 					printf("\tsuper block:\n");
3511 				else {
3512 					printf("\tsuper block in cylinder ");
3513 					printf("group ");
3514 					print(cur_cgrp - 1, 0, 0, 0);
3515 					printf(":\n");
3516 				}
3517 				printsb(sb);
3518 				if (tcount)
3519 					printf("\n");
3520 			}
3521 			cur_cgrp--;
3522 			if (end) {
3523 				printf("end of super blocks\n");
3524 				error++;
3525 			}
3526 			return;
3527 		default:
3528 			error++;
3529 			printf("no such print option\n");
3530 			return;
3531 		}
3532 }
3533 
3534 /*
3535  * valid_addr - call check_addr to validate the current address.
3536  */
3537 valid_addr()
3538 {
3539 	short	eof_flag, end = 0, eof = 0;
3540 	long	tcount = count;
3541 
3542 	if (!trapped)
3543 		return(1);
3544 	if (cur_bytes < 0) {
3545 		cur_bytes = 0;
3546 		if (blocksize > filesize) {
3547 			printf("beginning of file\n");
3548 		} else {
3549 			if (type == BLOCK)
3550 				printf("beginning of block\n");
3551 			else
3552 				printf("beginning of fragment\n");
3553 		}
3554 		error++;
3555 		return(0);
3556 	}
3557 	count = 1;
3558 	check_addr(1, &end, &eof, (filesize < blocksize));
3559 	count = tcount;
3560 	if (eof) {
3561 		printf("end of file\n");
3562 		error++;
3563 		return(0);
3564 	}
3565 	if (end == 2) {
3566 		if (erraddr > addr) {
3567 			if (type == BLOCK)
3568 				printf("beginning of block\n");
3569 			else
3570 				printf("beginning of fragment\n");
3571 			error++;
3572 			return(0);
3573 		}
3574 	}
3575 	if (end) {
3576 		if (type == BLOCK)
3577 			printf("end of block\n");
3578 		else
3579 			printf("end of fragment\n");
3580 		error++;
3581 		return(0);
3582 	}
3583 	return(1);
3584 }
3585 
3586 /*
3587  * check_addr - check if the address crosses the end of block or
3588  *	end of file.  Return the proper count.
3589  */
3590 check_addr(eof_flag, end, eof, keep_on)
3591 	short	eof_flag, *end, *eof, keep_on;
3592 {
3593 	long	temp, tcount = count, taddr = addr, tcur_bytes = cur_bytes;
3594 
3595 	if (bcomp(addr + count * objsz - 1) ||
3596 	    (keep_on && taddr < (bmap(cur_block) << FRGSHIFT))) {
3597 		error = 0;
3598 		addr = taddr;
3599 		cur_bytes = tcur_bytes;
3600 		if (keep_on) {
3601 			if (addr < erraddr) {
3602 				if (cur_bytes < 0) {
3603 					(*end) = 2;
3604 					return;
3605 				}
3606 				temp = cur_block - lblkno(fs, cur_bytes);
3607 				cur_block -= temp;
3608 				if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
3609 					cur_block += temp;
3610 					return;
3611 				}
3612 				temp = tcur_bytes - cur_bytes;
3613 				addr += temp;
3614 				cur_bytes += temp;
3615 				return;
3616 			} else {
3617 				if (cur_bytes >= filesize) {
3618 					(*eof)++;
3619 					return;
3620 				}
3621 				temp = lblkno(fs, cur_bytes) - cur_block;
3622 				cur_block += temp;
3623 				if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
3624 					cur_block -= temp;
3625 					return;
3626 				}
3627 				temp = tcur_bytes - cur_bytes;
3628 				addr += temp;
3629 				cur_bytes += temp;
3630 				return;
3631 			}
3632 		}
3633 		tcount = (blkroundup(fs, addr+1)-addr) / objsz;
3634 		if (!star)
3635 			(*end) = 2;
3636 	}
3637 	addr = taddr;
3638 	cur_bytes = tcur_bytes;
3639 	if (eof_flag) {
3640 		if (blocksize > filesize) {
3641 			if (cur_bytes >= filesize) {
3642 				tcount = 0;
3643 				(*eof)++;
3644 			} else if (tcount > (filesize - cur_bytes) / objsz) {
3645 				tcount = (filesize - cur_bytes) / objsz;
3646 				if (!star || tcount == 0)
3647 					(*eof)++;
3648 			}
3649 		} else {
3650 			if (cur_bytes >= blocksize) {
3651 				tcount = 0;
3652 				(*end)++;
3653 			} else if (tcount > (blocksize - cur_bytes) / objsz) {
3654 				tcount = (blocksize - cur_bytes) / objsz;
3655 				if (!star || tcount == 0)
3656 					(*end)++;
3657 			}
3658 		}
3659 	}
3660 	return(tcount);
3661 }
3662 
3663 /*
3664  * print_check - check if the index needs to be printed and delete
3665  *	rows of zeros from the output.
3666  */
3667 unsigned long *
3668 print_check(lptr, tcount, tbase, i)
3669 	unsigned long	*lptr;
3670 	long		*tcount;
3671 	short		tbase;
3672 	register int	i;
3673 {
3674 	register int	j, k, temp = BYTESPERLINE / objsz;
3675 	short		first_time = 0;
3676 	unsigned long	*tlptr;
3677 	unsigned short	*tsptr, *sptr;
3678 
3679 	sptr = (unsigned short *)lptr;
3680 	if (i == 0)
3681 		first_time = 1;
3682 	if (i % temp == 0) {
3683 		if (*tcount >= temp - 1) {
3684 			if (objsz == SHORT)
3685 				tsptr = sptr;
3686 			else
3687 				tlptr = lptr;
3688 			k = *tcount - 1;
3689 			for (j = i; k--; j++)
3690 				if (objsz == SHORT) {
3691 					if (*tsptr++ != 0)
3692 						break;
3693 				} else {
3694 					if (*tlptr++ != 0)
3695 						break;
3696 				}
3697 			if (j > (i + temp - 1)) {
3698 				j = (j - i) / temp;
3699 				while (j-- > 0) {
3700 					if (objsz == SHORT)
3701 						sptr += temp;
3702 					else
3703 						lptr += temp;
3704 					*tcount -= temp;
3705 					i += temp;
3706 					addr += BYTESPERLINE;
3707 					cur_bytes += BYTESPERLINE;
3708 				}
3709 				if (first_time)
3710 					printf("*");
3711 				else
3712 					printf("\n*");
3713 			}
3714 			if (i)
3715 				printf("\n");
3716 			index(tbase);
3717 		} else {
3718 			if (i)
3719 				printf("\n");
3720 			index(tbase);
3721 		}
3722 	}
3723 	if(objsz == SHORT)
3724 		return((unsigned long *)sptr);
3725 	else
3726 		return(lptr);
3727 }
3728 
3729 /*
3730  * index - print a byte index for the printout in base b
3731  *	with leading zeros.
3732  */
3733 index(b)
3734 	int	b;
3735 {
3736 	int	tbase = base;
3737 
3738 	base = b;
3739 	print(addr, 8, 8, 1);
3740 	printf(":\t");
3741 	base = tbase;
3742 }
3743 
3744 /*
3745  * print - print out the value to digits places with/without
3746  *	leading zeros and right/left justified in the current base.
3747  */
3748 print(value, fieldsz, digits, lead)
3749 	int		value, fieldsz, digits, lead;
3750 {
3751 	register int	i, left = 0;
3752 	char		mode = BASE[base - OCTAL];
3753 	char		*string = &scratch[0];
3754 
3755 	if (digits < 0) {
3756 		left = 1;
3757 		digits *= -1;
3758 	}
3759 	if (base != HEX)
3760 		if (digits)
3761 			digits = digits + (digits - 1)/((base >> 1) - 1) + 1;
3762 		else
3763 			digits = 1;
3764 	if (lead) {
3765 		if (left)
3766 			sprintf(string, "%%%c%d%d.%d%c",
3767 				'-', 0, digits, lead, mode);
3768 		else
3769 			sprintf(string, "%%%d%d.%d%c", 0, digits, lead, mode);
3770 	} else {
3771 		if (left)
3772 			sprintf(string, "%%%c%d%c", '-', digits, mode);
3773 		else
3774 			sprintf(string, "%%%d%c", digits, mode);
3775 	}
3776 	printf(string, value);
3777 	for (i = 0; i < fieldsz - digits; i++)
3778 		printf(" ");
3779 }
3780 
3781 /*
3782  * Print out the contents of a superblock.
3783  */
3784 printsb(fs)
3785 	struct fs *fs;
3786 {
3787 	int c, i, j, k, size;
3788 
3789 	if (fs->fs_postblformat == FS_42POSTBLFMT)
3790 		fs->fs_nrpos = 8;
3791 	printf("magic\t%x\tformat\t%s\ttime\t%s", fs->fs_magic,
3792 	    fs->fs_postblformat == FS_42POSTBLFMT ? "static" : "dynamic",
3793 	    ctime(&fs->fs_time));
3794 	printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n",
3795 	    fs->fs_cstotal.cs_nbfree, fs->fs_cstotal.cs_ndir,
3796 	    fs->fs_cstotal.cs_nifree, fs->fs_cstotal.cs_nffree);
3797 	printf("ncg\t%d\tncyl\t%d\tsize\t%d\tblocks\t%d\n",
3798 	    fs->fs_ncg, fs->fs_ncyl, fs->fs_size, fs->fs_dsize);
3799 	printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n",
3800 	    fs->fs_bsize, fs->fs_bshift, fs->fs_bmask);
3801 	printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n",
3802 	    fs->fs_fsize, fs->fs_fshift, fs->fs_fmask);
3803 	printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n",
3804 	    fs->fs_frag, fs->fs_fragshift, fs->fs_fsbtodb);
3805 	printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n",
3806 	    fs->fs_cpg, fs->fs_fpg / fs->fs_frag, fs->fs_fpg, fs->fs_ipg);
3807 	printf("minfree\t%d%%\toptim\t%s\tmaxcontig %d\tmaxbpg\t%d\n",
3808 	    fs->fs_minfree, fs->fs_optim == FS_OPTSPACE ? "space" : "time",
3809 	    fs->fs_maxcontig, fs->fs_maxbpg);
3810 	printf("rotdelay %dms\theadswitch %dus\ttrackseek %dus\trps\t%d\n",
3811 	    fs->fs_rotdelay, fs->fs_headswitch, fs->fs_trkseek, fs->fs_rps);
3812 	printf("ntrak\t%d\tnsect\t%d\tnpsect\t%d\tspc\t%d\n",
3813 	    fs->fs_ntrak, fs->fs_nsect, fs->fs_npsect, fs->fs_spc);
3814 	printf("trackskew %d\tinterleave %d\n",
3815 	    fs->fs_trackskew, fs->fs_interleave);
3816 	printf("nindir\t%d\tinopb\t%d\tnspf\t%d\n",
3817 	    fs->fs_nindir, fs->fs_inopb, fs->fs_nspf);
3818 	printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n",
3819 	    fs->fs_sblkno, fs->fs_cblkno, fs->fs_iblkno, fs->fs_dblkno);
3820 	printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n",
3821 	    fs->fs_sbsize, fs->fs_cgsize, fs->fs_cgoffset, fs->fs_cgmask);
3822 	printf("csaddr\t%d\tcssize\t%d\tshift\t%d\tmask\t0x%08x\n",
3823 	    fs->fs_csaddr, fs->fs_cssize, fs->fs_csshift, fs->fs_csmask);
3824 	printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\n",
3825 	    fs->fs_cgrotor, fs->fs_fmod, fs->fs_ronly);
3826 	if (fs->fs_cpc != 0)
3827 		printf("blocks available in each of %d rotational positions",
3828 		     fs->fs_nrpos);
3829 	else
3830 		printf("insufficient space to maintain rotational tables\n");
3831 	for (c = 0; c < fs->fs_cpc; c++) {
3832 		printf("\ncylinder number %d:", c);
3833 		for (i = 0; i < fs->fs_nrpos; i++) {
3834 			if (fs_postbl(fs, c)[i] == -1)
3835 				continue;
3836 			printf("\n   position %d:\t", i);
3837 			for (j = fs_postbl(fs, c)[i], k = 1; ;
3838 			     j += fs_rotbl(fs)[j], k++) {
3839 				printf("%5d", j);
3840 				if (k % 12 == 0)
3841 					printf("\n\t\t");
3842 				if (fs_rotbl(fs)[j] == 0)
3843 					break;
3844 			}
3845 		}
3846 	}
3847 	printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t");
3848 	for (i = 0, j = 0; i < fs->fs_cssize; i += fs->fs_bsize, j++) {
3849 		size = fs->fs_cssize - i < fs->fs_bsize ?
3850 		    fs->fs_cssize - i : fs->fs_bsize;
3851 		fs->fs_csp[j] = (struct csum *)calloc(1, size);
3852 		lseek(fd, fsbtodb(fs, (fs->fs_csaddr + j * fs->fs_frag)) *
3853 		    fs->fs_fsize / fsbtodb(fs, 1), 0);
3854 		if (read(fd, fs->fs_csp[j], size) != size) {
3855 			for (j--; j >= 0; j--)
3856 				free(fs->fs_csp[j]);
3857 			return;
3858 		}
3859 	}
3860 	for (i = 0; i < fs->fs_ncg; i++) {
3861 		struct csum *cs = &fs->fs_cs(fs, i);
3862 		if (i && i % 4 == 0)
3863 			printf("\n\t");
3864 		printf("(%d,%d,%d,%d) ",
3865 		    cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree);
3866 	}
3867 	for (j--; j >= 0; j--)
3868 		free(fs->fs_csp[j]);
3869 	printf("\n");
3870 	if (fs->fs_ncyl % fs->fs_cpg) {
3871 		printf("cylinders in last group %d\n",
3872 		    i = fs->fs_ncyl % fs->fs_cpg);
3873 		printf("blocks in last group %d\n",
3874 		    i * fs->fs_spc / NSPB(fs));
3875 	}
3876 }
3877 
3878 /*
3879  * Print out the contents of a cylinder group.
3880  */
3881 printcg(cg)
3882 	struct cg *cg;
3883 {
3884 	int i,j;
3885 
3886 	printf("\ncg %d:\n", cg->cg_cgx);
3887 	printf("magic\t%x\ttell\t%x\ttime\t%s",
3888 	    fs->fs_postblformat == FS_42POSTBLFMT ?
3889 	    ((struct ocg *)cg)->cg_magic : cg->cg_magic,
3890 	    fsbtodb(fs, cgtod(fs, cg->cg_cgx)) * fs->fs_fsize / fsbtodb(fs, 1),
3891 	    ctime(&cg->cg_time));
3892 	printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n",
3893 	    cg->cg_cgx, cg->cg_ncyl, cg->cg_niblk, cg->cg_ndblk);
3894 	printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n",
3895 	    cg->cg_cs.cs_nbfree, cg->cg_cs.cs_ndir,
3896 	    cg->cg_cs.cs_nifree, cg->cg_cs.cs_nffree);
3897 	printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum",
3898 	    cg->cg_rotor, cg->cg_irotor, cg->cg_frotor);
3899 	for (i = 1, j = 0; i < fs->fs_frag; i++) {
3900 		printf("\t%d", cg->cg_frsum[i]);
3901 		j += i * cg->cg_frsum[i];
3902 	}
3903 	printf("\nsum of frsum: %d\niused:\t", j);
3904 	pbits(cg_inosused(cg), fs->fs_ipg);
3905 	printf("free:\t");
3906 	pbits(cg_blksfree(cg), fs->fs_fpg);
3907 	printf("b:\n");
3908 	for (i = 0; i < fs->fs_cpg; i++) {
3909 		if (cg_blktot(cg)[i] == 0)
3910 			continue;
3911 		printf("   c%d:\t(%d)\t", i, cg_blktot(cg)[i]);
3912 		for (j = 0; j < fs->fs_nrpos; j++) {
3913 			if (fs->fs_cpc == 0 ||
3914 			    fs_postbl(fs, i % fs->fs_cpc)[j] == -1)
3915 				continue;
3916 			printf(" %d", cg_blks(fs, cg, i)[j]);
3917 		}
3918 		printf("\n");
3919 	}
3920 }
3921 
3922 /*
3923  * Print out the contents of a bit array.
3924  */
3925 pbits(cp, max)
3926 	register char *cp;
3927 	int max;
3928 {
3929 	register int i;
3930 	int count = 0, j;
3931 
3932 	for (i = 0; i < max; i++)
3933 		if (isset(cp, i)) {
3934 			if (count)
3935 				printf(",%s", count % 6 ? " " : "\n\t");
3936 			count++;
3937 			printf("%d", i);
3938 			j = i;
3939 			while ((i+1)<max && isset(cp, i+1))
3940 				i++;
3941 			if (i != j)
3942 				printf("-%d", i);
3943 		}
3944 	printf("\n");
3945 }
3946 
3947 /*
3948  * bcomp - used to check for block over/under flows when stepping through
3949  *	a file system.
3950  */
3951 bcomp(addr)
3952 	long	addr;
3953 {
3954 	if (override)
3955 		return(0);
3956 	if (lblkno(fs, addr) == (bhdr.fwd)->blkno)
3957 		return(0);
3958 	error++;
3959 	return(1);
3960 }
3961 
3962 /*
3963  * bmap - maps the logical block number of a file into
3964  *	the corresponding physical block on the file
3965  *	system.
3966  */
3967 long
3968 bmap(bn)
3969 	long			bn;
3970 {
3971 	register int		i, j;
3972 	register struct dinode	*ip;
3973 	int			sh;
3974 	long			nb;
3975 
3976 	ip = (struct dinode *)cur_ino;
3977 	if (bn < NDADDR) {
3978 		addr = (long)&ip->di_db[bn];
3979 		cur_bytes = bn * BLKSIZE;
3980 		return(nullblk(nb=get(LONG)) ? 0L : nb);
3981 	}
3982 
3983 	sh = 1;
3984 	bn -= NDADDR;
3985 	for (j = NIADDR; j > 0; j--) {
3986 		sh *= NINDIR(fs);
3987 		if (bn < sh)
3988 			break;
3989 		bn -= sh;
3990 	}
3991 	if (j == 0) {
3992 		printf("file too big\n");
3993 		error++;
3994 		return(0L);
3995 	}
3996 	addr = (long)&ip->di_ib[NIADDR - j];
3997 	nb = get(LONG);
3998 	if (nb == 0)
3999 		return(0L);
4000 	for (; j <= NIADDR; j++) {
4001 		sh /= NINDIR(fs);
4002 		addr = (nb << FRGSHIFT) + ((bn / sh) % NINDIR(fs)) * LONG;
4003 		if (nullblk(nb = get(LONG)))
4004 			return(0L);
4005 	}
4006 	return(nb);
4007 }
4008