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