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