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