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