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