xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsdb/fsdb.c (revision c7fd2ed091e4e4beb47e1da3a6197a2c38f29c02)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright (c) 1988 Regents of the University of California.
10  * All rights reserved.
11  *
12  * This code is derived from software contributed to Berkeley by
13  * Computer Consoles Inc.
14  *
15  * Redistribution and use in source and binary forms are permitted
16  * provided that: (1) source distributions retain this entire copyright
17  * notice and comment, and (2) distributions including binaries display
18  * the following acknowledgement:  ``This product includes software
19  * developed by the University of California, Berkeley and its contributors''
20  * in the documentation or other materials provided with the distribution
21  * and in all advertising materials mentioning features or use of this
22  * software. Neither the name of the University nor the names of its
23  * contributors may be used to endorse or promote products derived
24  * from this software without specific prior written permission.
25  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
27  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28  */
29 
30 #ifndef lint
31 char copyright[] =
32 "@(#) Copyright(c) 1988 Regents of the University of California.\n\
33 All rights reserved.\n";
34 #endif /* not lint */
35 
36 #ifndef lint
37 static char sccsid[] = "@(#)fsdb.c	5.8 (Berkeley) 6/1/90";
38 #endif /* not lint */
39 
40 /*
41  *  fsdb - file system debugger
42  *
43  *  usage: fsdb [-o suboptions] special
44  *  options/suboptions:
45  *	-o
46  *		?		display usage
47  *		o		override some error conditions
48  *		p="string"	set prompt to string
49  *		w		open for write
50  */
51 
52 #include <sys/param.h>
53 #include <sys/signal.h>
54 #include <sys/file.h>
55 #include <inttypes.h>
56 #include <sys/sysmacros.h>
57 
58 #ifdef sun
59 #include <unistd.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <fcntl.h>
63 #include <signal.h>
64 #include <sys/types.h>
65 #include <sys/vnode.h>
66 #include <sys/mntent.h>
67 #include <sys/wait.h>
68 #include <sys/fs/ufs_fsdir.h>
69 #include <sys/fs/ufs_fs.h>
70 #include <sys/fs/ufs_inode.h>
71 #include <sys/fs/ufs_acl.h>
72 #include <sys/fs/ufs_log.h>
73 #else
74 #include <sys/dir.h>
75 #include <ufs/fs.h>
76 #include <ufs/dinode.h>
77 #include <paths.h>
78 #endif /* sun */
79 
80 #include <stdio.h>
81 #include <setjmp.h>
82 
83 #define	OLD_FSDB_COMPATIBILITY	/* To support the obsoleted "-z" option */
84 
85 #ifndef _PATH_BSHELL
86 #define	_PATH_BSHELL	"/bin/sh"
87 #endif /* _PATH_BSHELL */
88 /*
89  * Defines from the 4.3-tahoe file system, for systems with the 4.2 or 4.3
90  * file system.
91  */
92 #ifndef FS_42POSTBLFMT
93 #define	cg_blktot(cgp) (((cgp))->cg_btot)
94 #define	cg_blks(fs, cgp, cylno) (((cgp))->cg_b[cylno])
95 #define	cg_inosused(cgp) (((cgp))->cg_iused)
96 #define	cg_blksfree(cgp) (((cgp))->cg_free)
97 #define	cg_chkmagic(cgp) ((cgp)->cg_magic == CG_MAGIC)
98 #endif
99 
100 /*
101  * Never changing defines.
102  */
103 #define	OCTAL		8		/* octal base */
104 #define	DECIMAL		10		/* decimal base */
105 #define	HEX		16		/* hexadecimal base */
106 
107 /*
108  * Adjustable defines.
109  */
110 #define	NBUF		10		/* number of cache buffers */
111 #define	PROMPTSIZE	80		/* size of user definable prompt */
112 #define	MAXFILES	40000		/* max number of files ls can handle */
113 #define	FIRST_DEPTH	10		/* default depth for find and ls */
114 #define	SECOND_DEPTH	100		/* second try at depth (maximum) */
115 #define	INPUTBUFFER	1040		/* size of input buffer */
116 #define	BYTESPERLINE	16		/* bytes per line of /dxo output */
117 #define	NREG		36		/* number of save registers */
118 
119 #define	DEVPREFIX	"/dev/"		/* Uninteresting part of "special" */
120 
121 #if defined(OLD_FSDB_COMPATIBILITY)
122 #define	FSDB_OPTIONS	"o:wp:z:"
123 #else
124 #define	FSDB_OPTIONS	"o:wp:"
125 #endif /* OLD_FSDB_COMPATIBILITY */
126 
127 
128 /*
129  * Values dependent on sizes of structs and such.
130  */
131 #define	NUMB		3			/* these three are arbitrary, */
132 #define	BLOCK		5			/* but must be different from */
133 #define	FRAGMENT	7			/* the rest (hence odd). */
134 #define	BITSPERCHAR	8			/* couldn't find it anywhere  */
135 #define	CHAR		(sizeof (char))
136 #define	SHORT		(sizeof (short))
137 #define	LONG		(sizeof (long))
138 #define	U_OFFSET_T	(sizeof (u_offset_t))	/* essentially "long long" */
139 #define	INODE		(sizeof (struct dinode))
140 #define	DIRECTORY	(sizeof (struct direct))
141 #define	CGRP		(sizeof (struct cg))
142 #define	SB		(sizeof (struct fs))
143 #define	BLKSIZE		(fs->fs_bsize)		/* for clarity */
144 #define	FRGSIZE		(fs->fs_fsize)
145 #define	BLKSHIFT	(fs->fs_bshift)
146 #define	FRGSHIFT	(fs->fs_fshift)
147 #define	SHADOW_DATA	(sizeof (struct ufs_fsd))
148 
149 /*
150  * Messy macros that would otherwise clutter up such glamorous code.
151  */
152 #define	itob(i)		(((u_offset_t)itod(fs, (i)) << \
153 	(u_offset_t)FRGSHIFT) + (u_offset_t)itoo(fs, (i)) * (u_offset_t)INODE)
154 #define	min(x, y)	((x) < (y) ? (x) : (y))
155 #define	STRINGSIZE(d)	((long)d->d_reclen - \
156 				((long)&d->d_name[0] - (long)&d->d_ino))
157 #define	letter(c)	((((c) >= 'a')&&((c) <= 'z')) ||\
158 				(((c) >= 'A')&&((c) <= 'Z')))
159 #define	digit(c)	(((c) >= '0') && ((c) <= '9'))
160 #define	HEXLETTER(c)	(((c) >= 'A') && ((c) <= 'F'))
161 #define	hexletter(c)	(((c) >= 'a') && ((c) <= 'f'))
162 #define	octaldigit(c)	(((c) >= '0') && ((c) <= '7'))
163 #define	uppertolower(c)	((c) - 'A' + 'a')
164 #define	hextodigit(c)	((c) - 'a' + 10)
165 #define	numtodigit(c)	((c) - '0')
166 
167 #if !defined(loword)
168 #define	loword(X)	(((ushort_t *)&X)[1])
169 #endif /* loword */
170 
171 #if !defined(lobyte)
172 #define	lobyte(X)	(((unsigned char *)&X)[1])
173 #endif /* lobyte */
174 
175 /*
176  * buffer cache structure.
177  */
178 static struct lbuf {
179 	struct	lbuf  *fwd;
180 	struct	lbuf  *back;
181 	char	*blkaddr;
182 	short	valid;
183 	u_offset_t	blkno;
184 } lbuf[NBUF], bhdr;
185 
186 /*
187  * used to hold save registers (see '<' and '>').
188  */
189 struct	save_registers {
190 	u_offset_t	sv_addr;
191 	u_offset_t	sv_value;
192 	long		sv_objsz;
193 } regs[NREG];
194 
195 /*
196  * cd, find, and ls use this to hold filenames.  Each filename is broken
197  * up by a slash.  In other words, /usr/src/adm would have a len field
198  * of 2 (starting from 0), and filenames->fname[0-2] would hold usr,
199  * src, and adm components of the pathname.
200  */
201 static struct filenames {
202 	ino_t	ino;		/* inode */
203 	long	len;		/* number of components */
204 	char	flag;		/* flag if using SECOND_DEPTH allocator */
205 	char	find;		/* flag if found by find */
206 	char	**fname;	/* hold components of pathname */
207 } *filenames, *top;
208 
209 enum log_enum { LOG_NDELTAS, LOG_ALLDELTAS, LOG_CHECKSCAN };
210 #ifdef sun
211 struct fs	*fs;
212 static union {
213 	struct fs	un_filesystem;
214 	char		un_sbsize[SBSIZE];
215 } fs_un;
216 #define	filesystem	fs_un.un_filesystem
217 #else
218 struct fs filesystem, *fs;	/* super block */
219 #endif /* sun */
220 
221 /*
222  * Global data.
223  */
224 static char		*input_path[MAXPATHLEN];
225 static char		*stack_path[MAXPATHLEN];
226 static char		*current_path[MAXPATHLEN];
227 static char		input_buffer[INPUTBUFFER];
228 static char		*prompt;
229 static char		*buffers;
230 static char		scratch[64];
231 static char		BASE[] = "o u     x";
232 static char		PROMPT[PROMPTSIZE];
233 static char		laststyle = '/';
234 static char		lastpo = 'x';
235 static short		input_pointer;
236 static short		current_pathp;
237 static short		stack_pathp;
238 static short		input_pathp;
239 static short		cmp_level;
240 static int		nfiles;
241 static short		type = NUMB;
242 static short		dirslot;
243 static short		fd;
244 static short		c_count;
245 static short		error;
246 static short		paren;
247 static short		trapped;
248 static short		doing_cd;
249 static short		doing_find;
250 static short		find_by_name;
251 static short		find_by_inode;
252 static short		long_list;
253 static short		recursive;
254 static short		objsz = SHORT;
255 static short		override = 0;
256 static short		wrtflag = O_RDONLY;
257 static short		base = HEX;
258 static short		acting_on_inode;
259 static short		acting_on_directory;
260 static short		should_print = 1;
261 static short		clear;
262 static short		star;
263 static u_offset_t	addr;
264 static u_offset_t	bod_addr;
265 static u_offset_t	value;
266 static u_offset_t	erraddr;
267 static long		errcur_bytes;
268 static u_offset_t	errino;
269 static long		errinum;
270 static long		cur_cgrp;
271 static u_offset_t	cur_ino;
272 static long		cur_inum;
273 static u_offset_t	cur_dir;
274 static long		cur_block;
275 static long		cur_bytes;
276 static long		find_ino;
277 static u_offset_t	filesize;
278 static u_offset_t	blocksize;
279 static long		stringsize;
280 static long		count = 1;
281 static long		commands;
282 static long		read_requests;
283 static long		actual_disk_reads;
284 static jmp_buf		env;
285 static long		maxfiles;
286 static long		cur_shad;
287 
288 #ifndef sun
289 extern char	*malloc(), *calloc();
290 #endif
291 static char		getachar();
292 static char		*getblk(), *fmtentry();
293 
294 static offset_t		get();
295 static long		bmap();
296 static long		expr();
297 static long		term();
298 static long		getnumb();
299 static u_offset_t	getdirslot();
300 static unsigned long	*print_check();
301 
302 static void		usage();
303 static void		ungetachar();
304 static void		getnextinput();
305 static void		eat_spaces();
306 static void		restore_inode();
307 static void		find();
308 static void		ls();
309 static void		formatf();
310 static void		parse();
311 static void		follow_path();
312 static void		getname();
313 static void		freemem();
314 static void		print_path();
315 static void		fill();
316 static void		put();
317 static void		insert();
318 static void		puta();
319 static void		fprnt();
320 static void		index();
321 #ifdef _LARGEFILE64_SOURCE
322 static void		printll
323 	(u_offset_t value, int fieldsz, int digits, int lead);
324 #define	print(value, fieldsz, digits, lead) \
325 	printll((u_offset_t)value, fieldsz, digits, lead)
326 #else /* !_LARGEFILE64_SOURCE */
327 static void		print(long value, int fieldsz, int digits, int lead);
328 #endif /* _LARGEFILE64_SOURCE */
329 static void		printsb();
330 static void		printcg();
331 static void		pbits();
332 static void		old_fsdb();	/* Support for old fsdb functionality */
333 
334 static int		isnumber();
335 static int		icheck();
336 static int		cgrp_check();
337 static int		valid_addr();
338 static int		match();
339 static int		devcheck();
340 static int		bcomp();
341 static int		compare();
342 static int		check_addr();
343 static int		fcmp();
344 static int		ffcmp();
345 
346 static int		getshadowslot();
347 static void		getshadowdata();
348 static void		syncshadowscan();
349 
350 #ifdef sun
351 static void		err();
352 #else
353 static int		err();
354 #endif /* sun */
355 
356 /* Suboption vector */
357 static char *subopt_v[] = {
358 #define	OVERRIDE	0
359 	"o",
360 #define	NEW_PROMPT	1
361 	"p",
362 #define	WRITE_ENABLED	2
363 	"w",
364 #define	ALT_PROMPT	3
365 	"prompt",
366 	NULL
367 };
368 
369 /*
370  * main - lines are read up to the unprotected ('\') newline and
371  *	held in an input buffer.  Characters may be read from the
372  *	input buffer using getachar() and unread using ungetachar().
373  *	Reading the whole line ahead allows the use of debuggers
374  *	which would otherwise be impossible since the debugger
375  *	and fsdb could not share stdin.
376  */
377 
378 void
379 main(argc, argv)
380 	int			argc;
381 	char			*argv[];
382 {
383 
384 	register char		c, *cptr;
385 	register short		i;
386 	register struct direct	*dirp;
387 	register struct lbuf	*bp;
388 	char			*progname;
389 	short			colon, mode;
390 	long			temp;
391 
392 	/* Options/Suboptions processing */
393 	int	opt;
394 	char	*subopts;
395 	char	*optval;
396 
397 	/*
398 	 * The following are used to support the old fsdb functionality
399 	 * of clearing an inode. It's better to use 'clri'.
400 	 */
401 	int			inum;	/* Inode number to clear */
402 	char			*special;
403 
404 	setbuf(stdin, NULL);
405 	progname = argv[0];
406 	prompt = &PROMPT[0];
407 	/*
408 	 * Parse options.
409 	 */
410 	while ((opt = getopt(argc, argv, FSDB_OPTIONS)) != EOF) {
411 		switch (opt) {
412 #if defined(OLD_FSDB_COMPATIBILITY)
413 		case 'z':	/* Hack - Better to use clri */
414 			(void) fprintf(stderr, "%s\n%s\n%s\n%s\n",
415 "Warning: The '-z' option of 'fsdb_ufs' has been declared obsolete",
416 "and may not be supported in a future version of Solaris.",
417 "While this functionality is currently still supported, the",
418 "recommended procedure to clear an inode is to use clri(1M).");
419 			if (isnumber(optarg)) {
420 				inum = atoi(optarg);
421 				special = argv[optind];
422 				/* Doesn't return */
423 				old_fsdb(inum, special);
424 			} else {
425 				usage(progname);
426 				exit(31+1);
427 			}
428 			/* Should exit() before here */
429 			/*NOTREACHED*/
430 #endif /* OLD_FSDB_COMPATIBILITY */
431 		case 'o':
432 			/* UFS Specific Options */
433 			subopts = optarg;
434 			while (*subopts != '\0') {
435 				switch (getsubopt(&subopts, subopt_v,
436 								&optval)) {
437 				case OVERRIDE:
438 					printf("error checking off\n");
439 					override = 1;
440 					break;
441 
442 				/*
443 				 * Change the "-o prompt=foo" option to
444 				 * "-o p=foo" to match documentation.
445 				 * ALT_PROMPT continues support for the
446 				 * undocumented "-o prompt=foo" option so
447 				 * that we don't break anyone.
448 				 */
449 				case NEW_PROMPT:
450 				case ALT_PROMPT:
451 					if (optval == NULL) {
452 						(void) fprintf(stderr,
453 							"No prompt string\n");
454 						usage(progname);
455 					}
456 					(void) strncpy(PROMPT, optval,
457 								PROMPTSIZE);
458 					break;
459 
460 				case WRITE_ENABLED:
461 					/* suitable for open */
462 					wrtflag = O_RDWR;
463 					break;
464 
465 				default:
466 					usage(progname);
467 					/* Should exit here */
468 				}
469 			}
470 			break;
471 
472 		default:
473 			usage(progname);
474 		}
475 	}
476 
477 	if ((argc - optind) != 1) {	/* Should just have "special" left */
478 		usage(progname);
479 	}
480 	special = argv[optind];
481 
482 	/*
483 	 * Unless it's already been set, the default prompt includes the
484 	 * name of the special device.
485 	 */
486 	if (*prompt == NULL)
487 		(void) sprintf(prompt, "%s > ", special);
488 
489 	/*
490 	 * Attempt to open the special file.
491 	 */
492 	if ((fd = open(special, wrtflag)) < 0) {
493 		perror(special);
494 		exit(1);
495 	}
496 	/*
497 	 * Read in the super block and validate (not too picky).
498 	 */
499 	if (llseek(fd, (offset_t)(SBLOCK * DEV_BSIZE), 0) == -1) {
500 		perror(special);
501 		exit(1);
502 	}
503 
504 #ifdef sun
505 	if (read(fd, &filesystem, SBSIZE) != SBSIZE) {
506 		printf("%s: cannot read superblock\n", special);
507 		exit(1);
508 	}
509 #else
510 	if (read(fd, &filesystem, sizeof (filesystem)) != sizeof (filesystem)) {
511 		printf("%s: cannot read superblock\n", special);
512 		exit(1);
513 	}
514 #endif /* sun */
515 
516 	fs = &filesystem;
517 	if ((fs->fs_magic != FS_MAGIC) && (fs->fs_magic != MTB_UFS_MAGIC)) {
518 		if (!override) {
519 			printf("%s: Bad magic number in file system\n",
520 								special);
521 			exit(1);
522 		}
523 
524 		printf("WARNING: Bad magic number in file system. ");
525 		printf("Continue? (y/n): ");
526 		(void) fflush(stdout);
527 		if (gets(input_buffer) == NULL) {
528 			exit(1);
529 		}
530 
531 		if (*input_buffer != 'y' && *input_buffer != 'Y') {
532 			exit(1);
533 		}
534 	}
535 
536 	if (fs->fs_magic == MTB_UFS_MAGIC &&
537 	    (fs->fs_version > MTB_UFS_VERSION_1 ||
538 	    fs->fs_version < MTB_UFS_VERSION_MIN)) {
539 		if (!override) {
540 			printf("%s: Unrecognized UFS version number: %d\n",
541 			    special, fs->fs_version);
542 			exit(1);
543 		}
544 
545 		printf("WARNING: Unrecognized UFS version number. ");
546 		printf("Continue? (y/n): ");
547 		(void) fflush(stdout);
548 		if (gets(input_buffer) == NULL) {
549 			exit(1);
550 		}
551 
552 		if (*input_buffer != 'y' && *input_buffer != 'Y') {
553 			exit(1);
554 		}
555 	}
556 #ifdef FS_42POSTBLFMT
557 	if (fs->fs_postblformat == FS_42POSTBLFMT)
558 		fs->fs_nrpos = 8;
559 #endif
560 	printf("fsdb of %s %s -- last mounted on %s\n",
561 		special,
562 		(wrtflag == O_RDWR) ? "(Opened for write)" : "(Read only)",
563 		&fs->fs_fsmnt[0]);
564 #ifdef sun
565 	printf("fs_clean is currently set to ");
566 	switch (fs->fs_clean) {
567 
568 	case FSACTIVE:
569 		printf("FSACTIVE\n");
570 		break;
571 	case FSCLEAN:
572 		printf("FSCLEAN\n");
573 		break;
574 	case FSSTABLE:
575 		printf("FSSTABLE\n");
576 		break;
577 	case FSBAD:
578 		printf("FSBAD\n");
579 		break;
580 	case FSSUSPEND:
581 		printf("FSSUSPEND\n");
582 		break;
583 	case FSLOG:
584 		printf("FSLOG\n");
585 		break;
586 	case FSFIX:
587 		printf("FSFIX\n");
588 		if (!override) {
589 			printf("%s: fsck may be running on this file system\n",
590 								special);
591 			exit(1);
592 		}
593 
594 		printf("WARNING: fsck may be running on this file system. ");
595 		printf("Continue? (y/n): ");
596 		(void) fflush(stdout);
597 		if (gets(input_buffer) == NULL) {
598 			exit(1);
599 		}
600 
601 		if (*input_buffer != 'y' && *input_buffer != 'Y') {
602 			exit(1);
603 		}
604 		break;
605 	default:
606 		printf("an unknown value (0x%x)\n", fs->fs_clean);
607 		break;
608 	}
609 
610 	if (fs->fs_state == (FSOKAY - fs->fs_time)) {
611 		printf("fs_state consistent (fs_clean CAN be trusted)\n");
612 	} else {
613 		printf("fs_state inconsistent (fs_clean CAN'T trusted)\n");
614 	}
615 #endif /* sun */
616 	/*
617 	 * Malloc buffers and set up cache.
618 	 */
619 	buffers = malloc(NBUF * BLKSIZE);
620 	bhdr.fwd = bhdr.back = &bhdr;
621 	for (i = 0; i < NBUF; i++) {
622 		bp = &lbuf[i];
623 		bp->blkaddr = buffers + (i * BLKSIZE);
624 		bp->valid = 0;
625 		insert(bp);
626 	}
627 	/*
628 	 * Malloc filenames structure.  The space for the actual filenames
629 	 * is allocated as it needs it. We estimate the size based on the
630 	 * number of inodes(objects) in the filesystem and the number of
631 	 * directories.  The number of directories are padded by 3 because
632 	 * each directory traversed during a "find" or "ls -R" needs 3
633 	 * entries.
634 	 */
635 	maxfiles = (long)((((u_offset_t)fs->fs_ncg * (u_offset_t)fs->fs_ipg) -
636 	    (u_offset_t)fs->fs_cstotal.cs_nifree) +
637 	    ((u_offset_t)fs->fs_cstotal.cs_ndir * (u_offset_t)3));
638 
639 	filenames = (struct filenames *)calloc(maxfiles,
640 	    sizeof (struct filenames));
641 	if (filenames == NULL) {
642 		/*
643 		 * If we could not allocate memory for all of files
644 		 * in the filesystem then, back off to the old fixed
645 		 * value.
646 		 */
647 		maxfiles = MAXFILES;
648 		filenames = (struct filenames *)calloc(maxfiles,
649 		    sizeof (struct filenames));
650 		if (filenames == NULL) {
651 			printf("out of memory\n");
652 			exit(1);
653 		}
654 	}
655 
656 	restore_inode(2);
657 	/*
658 	 * Malloc a few filenames (needed by pwd for example).
659 	 */
660 	for (i = 0; i < MAXPATHLEN; i++) {
661 		input_path[i] = calloc(1, MAXNAMLEN);
662 		stack_path[i] = calloc(1, MAXNAMLEN);
663 		current_path[i] = calloc(1, MAXNAMLEN);
664 		if (current_path[i] == NULL) {
665 			printf("out of memory\n");
666 			exit(1);
667 		}
668 	}
669 	current_pathp = -1;
670 
671 	(void) signal(2, err);
672 	(void) setjmp(env);
673 
674 	getnextinput();
675 	/*
676 	 * Main loop and case statement.  If an error condition occurs
677 	 * initialization and recovery is attempted.
678 	 */
679 	for (;;) {
680 		if (error) {
681 			freemem(filenames, nfiles);
682 			nfiles = 0;
683 			c_count = 0;
684 			count = 1;
685 			star = 0;
686 			error = 0;
687 			paren = 0;
688 			acting_on_inode = 0;
689 			acting_on_directory = 0;
690 			should_print = 1;
691 			addr = erraddr;
692 			cur_ino = errino;
693 			cur_inum = errinum;
694 			cur_bytes = errcur_bytes;
695 			printf("?\n");
696 			getnextinput();
697 			if (error)
698 				continue;
699 		}
700 		c_count++;
701 
702 		switch (c = getachar()) {
703 
704 		case '\n': /* command end */
705 			freemem(filenames, nfiles);
706 			nfiles = 0;
707 			if (should_print && laststyle == '=') {
708 				ungetachar(c);
709 				goto calc;
710 			}
711 			if (c_count == 1) {
712 				clear = 0;
713 				should_print = 1;
714 				erraddr = addr;
715 				errino = cur_ino;
716 				errinum = cur_inum;
717 				errcur_bytes = cur_bytes;
718 				switch (objsz) {
719 				case DIRECTORY:
720 					if ((addr = getdirslot(
721 							(long)dirslot+1)) == 0)
722 						should_print = 0;
723 					if (error) {
724 						ungetachar(c);
725 						continue;
726 					}
727 					break;
728 				case INODE:
729 					cur_inum++;
730 					addr = itob(cur_inum);
731 					if (!icheck(addr)) {
732 						cur_inum--;
733 						should_print = 0;
734 					}
735 					break;
736 				case CGRP:
737 				case SB:
738 					cur_cgrp++;
739 					addr = cgrp_check(cur_cgrp);
740 					if (addr == 0) {
741 						cur_cgrp--;
742 						continue;
743 					}
744 					break;
745 				case SHADOW_DATA:
746 					if ((addr = getshadowslot(
747 					    (long)cur_shad + 1)) == 0)
748 						should_print = 0;
749 					if (error) {
750 						ungetachar(c);
751 						continue;
752 					}
753 					break;
754 				default:
755 					addr += objsz;
756 					cur_bytes += objsz;
757 					if (valid_addr() == 0)
758 						continue;
759 				}
760 			}
761 			if (type == NUMB)
762 				trapped = 0;
763 			if (should_print)
764 				switch (objsz) {
765 				case DIRECTORY:
766 					fprnt('?', 'd');
767 					break;
768 				case INODE:
769 					fprnt('?', 'i');
770 					if (!error)
771 						cur_ino = addr;
772 					break;
773 				case CGRP:
774 					fprnt('?', 'c');
775 					break;
776 				case SB:
777 					fprnt('?', 's');
778 					break;
779 				case SHADOW_DATA:
780 					fprnt('?', 'S');
781 					break;
782 				case CHAR:
783 				case SHORT:
784 				case LONG:
785 					fprnt(laststyle, lastpo);
786 				}
787 			if (error) {
788 				ungetachar(c);
789 				continue;
790 			}
791 			c_count = colon = acting_on_inode = 0;
792 			acting_on_directory = 0;
793 			should_print = 1;
794 			getnextinput();
795 			if (error)
796 				continue;
797 			erraddr = addr;
798 			errino = cur_ino;
799 			errinum = cur_inum;
800 			errcur_bytes = cur_bytes;
801 			continue;
802 
803 		case '(': /* numeric expression or unknown command */
804 		default:
805 			colon = 0;
806 			if (digit(c) || c == '(') {
807 				ungetachar(c);
808 				addr = expr();
809 				type = NUMB;
810 				value = addr;
811 				continue;
812 			}
813 			printf("unknown command or bad syntax\n");
814 			error++;
815 			continue;
816 
817 		case '?': /* general print facilities */
818 		case '/':
819 			fprnt(c, getachar());
820 			continue;
821 
822 		case ';': /* command separator and . */
823 		case '\t':
824 		case ' ':
825 		case '.':
826 			continue;
827 
828 		case ':': /* command indicator */
829 			colon++;
830 			commands++;
831 			should_print = 0;
832 			stringsize = 0;
833 			trapped = 0;
834 			continue;
835 
836 		case ',': /* count indicator */
837 			colon = star = 0;
838 			if ((c = getachar()) == '*') {
839 				star = 1;
840 				count = BLKSIZE;
841 			} else {
842 				ungetachar(c);
843 				count = expr();
844 				if (error)
845 					continue;
846 				if (!count)
847 					count = 1;
848 			}
849 			clear = 0;
850 			continue;
851 
852 		case '+': /* address addition */
853 			colon = 0;
854 			c = getachar();
855 			ungetachar(c);
856 			if (c == '\n')
857 				temp = 1;
858 			else {
859 				temp = expr();
860 				if (error)
861 					continue;
862 			}
863 			erraddr = addr;
864 			errcur_bytes = cur_bytes;
865 			switch (objsz) {
866 			case DIRECTORY:
867 				addr = getdirslot((long)(dirslot + temp));
868 				if (error)
869 					continue;
870 				break;
871 			case INODE:
872 				cur_inum += temp;
873 				addr = itob(cur_inum);
874 				if (!icheck(addr)) {
875 					cur_inum -= temp;
876 					continue;
877 				}
878 				break;
879 			case CGRP:
880 			case SB:
881 				cur_cgrp += temp;
882 				if ((addr = cgrp_check(cur_cgrp)) == 0) {
883 					cur_cgrp -= temp;
884 					continue;
885 				}
886 				break;
887 			case SHADOW_DATA:
888 				addr = getshadowslot((long)(cur_shad + temp));
889 				if (error)
890 				    continue;
891 				break;
892 
893 			default:
894 				laststyle = '/';
895 				addr += temp * objsz;
896 				cur_bytes += temp * objsz;
897 				if (valid_addr() == 0)
898 					continue;
899 			}
900 			value = get(objsz);
901 			continue;
902 
903 		case '-': /* address subtraction */
904 			colon = 0;
905 			c = getachar();
906 			ungetachar(c);
907 			if (c == '\n')
908 				temp = 1;
909 			else {
910 				temp = expr();
911 				if (error)
912 					continue;
913 			}
914 			erraddr = addr;
915 			errcur_bytes = cur_bytes;
916 			switch (objsz) {
917 			case DIRECTORY:
918 				addr = getdirslot((long)(dirslot - temp));
919 				if (error)
920 					continue;
921 				break;
922 			case INODE:
923 				cur_inum -= temp;
924 				addr = itob(cur_inum);
925 				if (!icheck(addr)) {
926 					cur_inum += temp;
927 					continue;
928 				}
929 				break;
930 			case CGRP:
931 			case SB:
932 				cur_cgrp -= temp;
933 				if ((addr = cgrp_check(cur_cgrp)) == 0) {
934 					cur_cgrp += temp;
935 					continue;
936 				}
937 				break;
938 			case SHADOW_DATA:
939 				addr = getshadowslot((long)(cur_shad - temp));
940 				if (error)
941 					continue;
942 				break;
943 			default:
944 				laststyle = '/';
945 				addr -= temp * objsz;
946 				cur_bytes -= temp * objsz;
947 				if (valid_addr() == 0)
948 					continue;
949 			}
950 			value = get(objsz);
951 			continue;
952 
953 		case '*': /* address multiplication */
954 			colon = 0;
955 			temp = expr();
956 			if (error)
957 				continue;
958 			if (objsz != INODE && objsz != DIRECTORY)
959 				laststyle = '/';
960 			addr *= temp;
961 			value = get(objsz);
962 			continue;
963 
964 		case '%': /* address division */
965 			colon = 0;
966 			temp = expr();
967 			if (error)
968 				continue;
969 			if (!temp) {
970 				printf("divide by zero\n");
971 				error++;
972 				continue;
973 			}
974 			if (objsz != INODE && objsz != DIRECTORY)
975 				laststyle = '/';
976 			addr /= temp;
977 			value = get(objsz);
978 			continue;
979 
980 		case '=': { /* assignment operation */
981 			short tbase;
982 calc:
983 			tbase = base;
984 
985 			c = getachar();
986 			if (c == '\n') {
987 				ungetachar(c);
988 				c = lastpo;
989 				if (acting_on_inode == 1) {
990 					if (c != 'o' && c != 'd' && c != 'x' &&
991 					    c != 'O' && c != 'D' && c != 'X') {
992 						switch (objsz) {
993 						case LONG:
994 							c = lastpo = 'X';
995 							break;
996 						case SHORT:
997 							c = lastpo = 'x';
998 							break;
999 						case CHAR:
1000 							c = lastpo = 'c';
1001 						}
1002 					}
1003 				} else {
1004 					if (acting_on_inode == 2)
1005 						c = lastpo = 't';
1006 				}
1007 			} else if (acting_on_inode)
1008 				lastpo = c;
1009 			should_print = star = 0;
1010 			count = 1;
1011 			erraddr = addr;
1012 			errcur_bytes = cur_bytes;
1013 			switch (c) {
1014 			case '"': /* character string */
1015 				if (type == NUMB) {
1016 					blocksize = BLKSIZE;
1017 					filesize = BLKSIZE * 2;
1018 					cur_bytes = blkoff(fs, addr);
1019 					if (objsz == DIRECTORY ||
1020 								objsz == INODE)
1021 						lastpo = 'X';
1022 				}
1023 				puta();
1024 				continue;
1025 			case '+': /* =+ operator */
1026 				temp = expr();
1027 				value = get(objsz);
1028 				if (!error)
1029 					put(value+temp, objsz);
1030 				continue;
1031 			case '-': /* =- operator */
1032 				temp = expr();
1033 				value = get(objsz);
1034 				if (!error)
1035 					put(value-temp, objsz);
1036 				continue;
1037 			case 'b':
1038 			case 'c':
1039 				if (objsz == CGRP)
1040 					fprnt('?', c);
1041 				else
1042 					fprnt('/', c);
1043 				continue;
1044 			case 'i':
1045 				addr = cur_ino;
1046 				fprnt('?', 'i');
1047 				continue;
1048 			case 's':
1049 				fprnt('?', 's');
1050 				continue;
1051 			case 't':
1052 			case 'T':
1053 				laststyle = '=';
1054 				printf("\t\t");
1055 				{
1056 					/*
1057 					 * Truncation is intentional so
1058 					 * ctime is happy.
1059 					 */
1060 					time_t tvalue = (time_t)value;
1061 					printf("%s", ctime(&tvalue));
1062 				}
1063 				continue;
1064 			case 'o':
1065 				base = OCTAL;
1066 				goto otx;
1067 			case 'd':
1068 				if (objsz == DIRECTORY) {
1069 					addr = cur_dir;
1070 					fprnt('?', 'd');
1071 					continue;
1072 				}
1073 				base = DECIMAL;
1074 				goto otx;
1075 			case 'x':
1076 				base = HEX;
1077 otx:
1078 				laststyle = '=';
1079 				printf("\t\t");
1080 				if (acting_on_inode)
1081 					print(value & 0177777L, 12, -8, 0);
1082 				else
1083 					print(addr & 0177777L, 12, -8, 0);
1084 				printf("\n");
1085 				base = tbase;
1086 				continue;
1087 			case 'O':
1088 				base = OCTAL;
1089 				goto OTX;
1090 			case 'D':
1091 				base = DECIMAL;
1092 				goto OTX;
1093 			case 'X':
1094 				base = HEX;
1095 OTX:
1096 				laststyle = '=';
1097 				printf("\t\t");
1098 				if (acting_on_inode)
1099 					print(value, 12, -8, 0);
1100 				else
1101 					print(addr, 12, -8, 0);
1102 				printf("\n");
1103 				base = tbase;
1104 				continue;
1105 			default: /* regular assignment */
1106 				ungetachar(c);
1107 				value = expr();
1108 				if (error)
1109 					printf("syntax error\n");
1110 				else
1111 					put(value, objsz);
1112 				continue;
1113 			}
1114 		}
1115 
1116 		case '>': /* save current address */
1117 			colon = 0;
1118 			should_print = 0;
1119 			c = getachar();
1120 			if (!letter(c) && !digit(c)) {
1121 				printf("invalid register specification, ");
1122 				printf("must be letter or digit\n");
1123 				error++;
1124 				continue;
1125 			}
1126 			if (letter(c)) {
1127 				if (c < 'a')
1128 					c = uppertolower(c);
1129 				c = hextodigit(c);
1130 			} else
1131 				c = numtodigit(c);
1132 			regs[c].sv_addr = addr;
1133 			regs[c].sv_value = value;
1134 			regs[c].sv_objsz = objsz;
1135 			continue;
1136 
1137 		case '<': /* restore saved address */
1138 			colon = 0;
1139 			should_print = 0;
1140 			c = getachar();
1141 			if (!letter(c) && !digit(c)) {
1142 				printf("invalid register specification, ");
1143 				printf("must be letter or digit\n");
1144 				error++;
1145 				continue;
1146 			}
1147 			if (letter(c)) {
1148 				if (c < 'a')
1149 					c = uppertolower(c);
1150 				c = hextodigit(c);
1151 			} else
1152 				c = numtodigit(c);
1153 			addr = regs[c].sv_addr;
1154 			value = regs[c].sv_value;
1155 			objsz = regs[c].sv_objsz;
1156 			continue;
1157 
1158 		case 'a':
1159 			if (colon)
1160 				colon = 0;
1161 			else
1162 				goto no_colon;
1163 			if (match("at", 2)) { 		/* access time */
1164 				acting_on_inode = 2;
1165 				should_print = 1;
1166 				addr = (long)
1167 					&((struct dinode *)cur_ino)->di_atime;
1168 				value = get(LONG);
1169 				type = NULL;
1170 				continue;
1171 			}
1172 			goto bad_syntax;
1173 
1174 		case 'b':
1175 			if (colon)
1176 				colon = 0;
1177 			else
1178 				goto no_colon;
1179 			if (match("block", 2)) { 	/* block conversion */
1180 				if (type == NUMB) {
1181 					value = addr;
1182 					cur_bytes = 0;
1183 					blocksize = BLKSIZE;
1184 					filesize = BLKSIZE * 2;
1185 				}
1186 				addr = value << FRGSHIFT;
1187 				bod_addr = addr;
1188 				value = get(LONG);
1189 				type = BLOCK;
1190 				dirslot = 0;
1191 				trapped++;
1192 				continue;
1193 			}
1194 			if (match("bs", 2)) {		/* block size */
1195 				acting_on_inode = 1;
1196 				should_print = 1;
1197 				if (icheck(cur_ino) == 0)
1198 					continue;
1199 				addr = (long)
1200 					&((struct dinode *)cur_ino)->di_blocks;
1201 				value = get(LONG);
1202 				type = NULL;
1203 				continue;
1204 			}
1205 			if (match("base", 2)) {		/* change/show base */
1206 showbase:
1207 				if ((c = getachar()) == '\n') {
1208 					ungetachar(c);
1209 					printf("base =\t\t");
1210 					switch (base) {
1211 					case OCTAL:
1212 						printf("OCTAL\n");
1213 						continue;
1214 					case DECIMAL:
1215 						printf("DECIMAL\n");
1216 						continue;
1217 					case HEX:
1218 						printf("HEX\n");
1219 						continue;
1220 					}
1221 				}
1222 				if (c != '=') {
1223 					printf("missing '='\n");
1224 					error++;
1225 					continue;
1226 				}
1227 				value = expr();
1228 				switch (value) {
1229 				default:
1230 					printf("invalid base\n");
1231 					error++;
1232 					break;
1233 				case OCTAL:
1234 				case DECIMAL:
1235 				case HEX:
1236 					base = (short)value;
1237 				}
1238 				goto showbase;
1239 			}
1240 			goto bad_syntax;
1241 
1242 		case 'c':
1243 			if (colon)
1244 				colon = 0;
1245 			else
1246 				goto no_colon;
1247 			if (match("cd", 2)) {		/* change directory */
1248 				top = filenames - 1;
1249 				eat_spaces();
1250 				if ((c = getachar()) == '\n') {
1251 					ungetachar(c);
1252 					current_pathp = -1;
1253 					restore_inode(2);
1254 					continue;
1255 				}
1256 				ungetachar(c);
1257 				temp = cur_inum;
1258 				doing_cd = 1;
1259 				parse();
1260 				doing_cd = 0;
1261 				if (nfiles != 1) {
1262 					restore_inode((ino_t)temp);
1263 					if (!error) {
1264 						print_path(input_path,
1265 							(int)input_pathp);
1266 						if (nfiles == 0)
1267 							printf(" not found\n");
1268 						else
1269 							printf(" ambiguous\n");
1270 						error++;
1271 					}
1272 					continue;
1273 				}
1274 				restore_inode(filenames->ino);
1275 				if ((mode = icheck(addr)) == 0)
1276 					continue;
1277 				if ((mode & IFMT) != IFDIR) {
1278 					restore_inode((ino_t)temp);
1279 					print_path(input_path,
1280 							(int)input_pathp);
1281 					printf(" not a directory\n");
1282 					error++;
1283 					continue;
1284 				}
1285 				for (i = 0; i <= top->len; i++)
1286 					(void) strcpy(current_path[i],
1287 						top->fname[i]);
1288 				current_pathp = top->len;
1289 				continue;
1290 			}
1291 			if (match("cg", 2)) {		/* cylinder group */
1292 				if (type == NUMB)
1293 					value = addr;
1294 				if (value > fs->fs_ncg - 1) {
1295 					printf("maximum cylinder group is ");
1296 					print(fs->fs_ncg - 1, 8, -8, 0);
1297 					printf("\n");
1298 					error++;
1299 					continue;
1300 				}
1301 				type = objsz = CGRP;
1302 				cur_cgrp = (long)value;
1303 				addr = cgtod(fs, cur_cgrp) << FRGSHIFT;
1304 				continue;
1305 			}
1306 			if (match("ct", 2)) {		/* creation time */
1307 				acting_on_inode = 2;
1308 				should_print = 1;
1309 				addr = (long)
1310 					&((struct dinode *)cur_ino)->di_ctime;
1311 				value = get(LONG);
1312 				type = NULL;
1313 				continue;
1314 			}
1315 			goto bad_syntax;
1316 
1317 		case 'd':
1318 			if (colon)
1319 				colon = 0;
1320 			else
1321 				goto no_colon;
1322 			if (match("directory", 2)) { 	/* directory offsets */
1323 				if (type == NUMB)
1324 					value = addr;
1325 				objsz = DIRECTORY;
1326 				type = DIRECTORY;
1327 				addr = (u_offset_t)getdirslot((long)value);
1328 				continue;
1329 			}
1330 			if (match("db", 2)) {		/* direct block */
1331 				acting_on_inode = 1;
1332 				should_print = 1;
1333 				if (type == NUMB)
1334 					value = addr;
1335 				if (value >= NDADDR) {
1336 					printf("direct blocks are 0 to ");
1337 					print(NDADDR - 1, 0, 0, 0);
1338 					printf("\n");
1339 					error++;
1340 					continue;
1341 				}
1342 				addr = cur_ino;
1343 				if (!icheck(addr))
1344 					continue;
1345 				addr = (long)
1346 					&((struct dinode *)cur_ino)->
1347 								di_db[value];
1348 				bod_addr = addr;
1349 				cur_bytes = (value) * BLKSIZE;
1350 				cur_block = (long)value;
1351 				type = BLOCK;
1352 				dirslot = 0;
1353 				value = get(LONG);
1354 				if (!value && !override) {
1355 					printf("non existent block\n");
1356 					error++;
1357 				}
1358 				continue;
1359 			}
1360 			goto bad_syntax;
1361 
1362 		case 'f':
1363 			if (colon)
1364 				colon = 0;
1365 			else
1366 				goto no_colon;
1367 			if (match("find", 3)) {		/* find command */
1368 				find();
1369 				continue;
1370 			}
1371 			if (match("fragment", 2)) {	/* fragment conv. */
1372 				if (type == NUMB) {
1373 					value = addr;
1374 					cur_bytes = 0;
1375 					blocksize = FRGSIZE;
1376 					filesize = FRGSIZE * 2;
1377 				}
1378 				if (min(blocksize, filesize) - cur_bytes >
1379 							FRGSIZE) {
1380 					blocksize = cur_bytes + FRGSIZE;
1381 					filesize = blocksize * 2;
1382 				}
1383 				addr = value << FRGSHIFT;
1384 				bod_addr = addr;
1385 				value = get(LONG);
1386 				type = FRAGMENT;
1387 				dirslot = 0;
1388 				trapped++;
1389 				continue;
1390 			}
1391 			if (match("file", 4)) {		/* access as file */
1392 				acting_on_inode = 1;
1393 				should_print = 1;
1394 				if (type == NUMB)
1395 					value = addr;
1396 				addr = cur_ino;
1397 				if ((mode = icheck(addr)) == 0)
1398 					continue;
1399 				if (!override) {
1400 					switch (mode & IFMT) {
1401 					case IFCHR:
1402 					case IFBLK:
1403 					    printf("special device\n");
1404 					    error++;
1405 					    continue;
1406 					}
1407 				}
1408 				if ((addr = (u_offset_t)
1409 				    (bmap((long)value) << FRGSHIFT)) == 0)
1410 					continue;
1411 				cur_block = (long)value;
1412 				bod_addr = addr;
1413 				type = BLOCK;
1414 				dirslot = 0;
1415 				continue;
1416 			}
1417 			if (match("fill", 4)) {		/* fill */
1418 				if (getachar() != '=') {
1419 					printf("missing '='\n");
1420 					error++;
1421 					continue;
1422 				}
1423 				if (objsz == INODE || objsz == DIRECTORY ||
1424 				    objsz == SHADOW_DATA) {
1425 					printf(
1426 					    "can't fill inode or directory\n");
1427 					error++;
1428 					continue;
1429 				}
1430 				fill();
1431 				continue;
1432 			}
1433 			goto bad_syntax;
1434 
1435 		case 'g':
1436 			if (colon)
1437 				colon = 0;
1438 			else
1439 				goto no_colon;
1440 			if (match("gid", 1)) {		/* group id */
1441 				acting_on_inode = 1;
1442 				should_print = 1;
1443 				addr = (long)
1444 					&((struct dinode *)cur_ino)->di_gid;
1445 				value = get(SHORT);
1446 				type = NULL;
1447 				continue;
1448 			}
1449 			goto bad_syntax;
1450 
1451 		case 'i':
1452 			if (colon)
1453 				colon = 0;
1454 			else
1455 				goto no_colon;
1456 			if (match("inode", 2)) { /* i# to inode conversion */
1457 				if (c_count == 2) {
1458 					addr = cur_ino;
1459 					value = get(INODE);
1460 					type = NULL;
1461 					laststyle = '=';
1462 					lastpo = 'i';
1463 					should_print = 1;
1464 					continue;
1465 				}
1466 				if (type == NUMB)
1467 					value = addr;
1468 				addr = itob(value);
1469 				if (!icheck(addr))
1470 					continue;
1471 				cur_ino = addr;
1472 				cur_inum = (long)value;
1473 				value = get(INODE);
1474 				type = NULL;
1475 				continue;
1476 			}
1477 			if (match("ib", 2)) {	/* indirect block */
1478 				acting_on_inode = 1;
1479 				should_print = 1;
1480 				if (type == NUMB)
1481 					value = addr;
1482 				if (value >= NIADDR) {
1483 					printf("indirect blocks are 0 to ");
1484 					print(NIADDR - 1, 0, 0, 0);
1485 					printf("\n");
1486 					error++;
1487 					continue;
1488 				}
1489 				addr = (long)&((struct dinode *)cur_ino)->
1490 								di_ib[value];
1491 				cur_bytes = (NDADDR - 1) * BLKSIZE;
1492 				temp = 1;
1493 				for (i = 0; i < value; i++) {
1494 					temp *= NINDIR(fs) * BLKSIZE;
1495 					cur_bytes += temp;
1496 				}
1497 				type = BLOCK;
1498 				dirslot = 0;
1499 				value = get(LONG);
1500 				if (!value && !override) {
1501 					printf("non existent block\n");
1502 					error++;
1503 				}
1504 				continue;
1505 			}
1506 			goto bad_syntax;
1507 
1508 		case 'l':
1509 			if (colon)
1510 				colon = 0;
1511 			else
1512 				goto no_colon;
1513 			if (match("log_head", 8)) {
1514 				log_display_header();
1515 				should_print = 0;
1516 				continue;
1517 			}
1518 			if (match("log_delta", 9)) {
1519 				log_show(LOG_NDELTAS);
1520 				should_print = 0;
1521 				continue;
1522 			}
1523 			if (match("log_show", 8)) {
1524 				log_show(LOG_ALLDELTAS);
1525 				should_print = 0;
1526 				continue;
1527 			}
1528 			if (match("log_chk", 7)) {
1529 				log_show(LOG_CHECKSCAN);
1530 				should_print = 0;
1531 				continue;
1532 			}
1533 			if (match("log_otodb", 9)) {
1534 				if (log_lodb((u_offset_t)addr, &temp)) {
1535 					addr = temp;
1536 					should_print = 1;
1537 					laststyle = '=';
1538 				} else
1539 					error++;
1540 				continue;
1541 			}
1542 			if (match("ls", 2)) {		/* ls command */
1543 				temp = cur_inum;
1544 				recursive = long_list = 0;
1545 				top = filenames - 1;
1546 				for (;;) {
1547 					eat_spaces();
1548 					if ((c = getachar()) == '-') {
1549 						if ((c = getachar()) == 'R') {
1550 							recursive = 1;
1551 							continue;
1552 						} else if (c == 'l') {
1553 							long_list = 1;
1554 						} else {
1555 							printf(
1556 							    "unknown option ");
1557 							printf("'%c'\n", c);
1558 							error++;
1559 							break;
1560 						}
1561 					} else
1562 						ungetachar(c);
1563 					if ((c = getachar()) == '\n') {
1564 						if (c_count != 2) {
1565 							ungetachar(c);
1566 							break;
1567 						}
1568 					}
1569 					c_count++;
1570 					ungetachar(c);
1571 					parse();
1572 					restore_inode((ino_t)temp);
1573 					if (error)
1574 						break;
1575 				}
1576 				recursive = 0;
1577 				if (error || nfiles == 0) {
1578 					if (!error) {
1579 						print_path(input_path,
1580 							(int)input_pathp);
1581 						printf(" not found\n");
1582 					}
1583 					continue;
1584 				}
1585 				if (nfiles) {
1586 				    cmp_level = 0;
1587 				    qsort((char *)filenames, nfiles,
1588 					sizeof (struct filenames), ffcmp);
1589 				    ls(filenames, filenames + (nfiles - 1), 0);
1590 				} else {
1591 				    printf("no match\n");
1592 				    error++;
1593 				}
1594 				restore_inode((ino_t)temp);
1595 				continue;
1596 			}
1597 			if (match("ln", 2)) {		/* link count */
1598 				acting_on_inode = 1;
1599 				should_print = 1;
1600 				addr = (long)
1601 					&((struct dinode *)cur_ino)->di_nlink;
1602 				value = get(SHORT);
1603 				type = NULL;
1604 				continue;
1605 			}
1606 			goto bad_syntax;
1607 
1608 		case 'm':
1609 			if (colon)
1610 				colon = 0;
1611 			else
1612 				goto no_colon;
1613 			addr = cur_ino;
1614 			if ((mode = icheck(addr)) == 0)
1615 				continue;
1616 			if (match("mt", 2)) { 		/* modification time */
1617 				acting_on_inode = 2;
1618 				should_print = 1;
1619 				addr = (long)
1620 					&((struct dinode *)cur_ino)->di_mtime;
1621 				value = get(LONG);
1622 				type = NULL;
1623 				continue;
1624 			}
1625 			if (match("md", 2)) {		/* mode */
1626 				acting_on_inode = 1;
1627 				should_print = 1;
1628 				addr = (long)
1629 					&((struct dinode *)cur_ino)->di_mode;
1630 				value = get(SHORT);
1631 				type = NULL;
1632 				continue;
1633 			}
1634 			if (match("maj", 2)) {	/* major device number */
1635 				acting_on_inode = 1;
1636 				should_print = 1;
1637 				if (devcheck(mode))
1638 					continue;
1639 				addr = (uintptr_t)
1640 					&((struct dinode *)cur_ino)->di_ordev;
1641 				{
1642 					long	dvalue;
1643 					dvalue = get(LONG);
1644 					value = major(dvalue);
1645 				}
1646 				type = NULL;
1647 				continue;
1648 			}
1649 			if (match("min", 2)) {	/* minor device number */
1650 				acting_on_inode = 1;
1651 				should_print = 1;
1652 				if (devcheck(mode))
1653 					continue;
1654 				addr = (uintptr_t)
1655 					&((struct dinode *)cur_ino)->di_ordev;
1656 				{
1657 					long	dvalue;
1658 					dvalue = (long)get(LONG);
1659 					value = minor(dvalue);
1660 				}
1661 				type = NULL;
1662 				continue;
1663 			}
1664 			goto bad_syntax;
1665 
1666 		case 'n':
1667 			if (colon)
1668 				colon = 0;
1669 			else
1670 				goto no_colon;
1671 			if (match("nm", 1)) {		/* directory name */
1672 				objsz = DIRECTORY;
1673 				acting_on_directory = 1;
1674 				cur_dir = addr;
1675 				if ((cptr = getblk(addr)) == 0)
1676 					continue;
1677 				/*LINTED*/
1678 				dirp = (struct direct *)(cptr+blkoff(fs, addr));
1679 				stringsize = (long)dirp->d_reclen -
1680 						((long)&dirp->d_name[0] -
1681 							(long)&dirp->d_ino);
1682 				addr = (long)
1683 					&((struct direct *)addr)->d_name[0];
1684 				type = NULL;
1685 				continue;
1686 			}
1687 			goto bad_syntax;
1688 
1689 		case 'o':
1690 			if (colon)
1691 				colon = 0;
1692 			else
1693 				goto no_colon;
1694 			if (match("override", 1)) {	/* override flip flop */
1695 				override = !override;
1696 				if (override)
1697 					printf("error checking off\n");
1698 				else
1699 					printf("error checking on\n");
1700 				continue;
1701 			}
1702 			goto bad_syntax;
1703 
1704 		case 'p':
1705 			if (colon)
1706 				colon = 0;
1707 			else
1708 				goto no_colon;
1709 			if (match("pwd", 2)) {		/* print working dir */
1710 				print_path(current_path, (int)current_pathp);
1711 				printf("\n");
1712 				continue;
1713 			}
1714 			if (match("prompt", 2)) {	/* change prompt */
1715 				if ((c = getachar()) != '=') {
1716 					printf("missing '='\n");
1717 					error++;
1718 					continue;
1719 				}
1720 				if ((c = getachar()) != '"') {
1721 					printf("missing '\"'\n");
1722 					error++;
1723 					continue;
1724 				}
1725 				i = 0;
1726 				prompt = &prompt[0];
1727 				while ((c = getachar()) != '"' && c != '\n') {
1728 					prompt[i++] = c;
1729 					if (i >= PROMPTSIZE) {
1730 						printf("string too long\n");
1731 						error++;
1732 						break;
1733 					}
1734 				}
1735 				prompt[i] = '\0';
1736 				continue;
1737 			}
1738 			goto bad_syntax;
1739 
1740 		case 'q':
1741 			if (!colon)
1742 				goto no_colon;
1743 			if (match("quit", 1)) {		/* quit */
1744 				if ((c = getachar()) != '\n') {
1745 					error++;
1746 					continue;
1747 				}
1748 				exit(0);
1749 			}
1750 			goto bad_syntax;
1751 
1752 		case 's':
1753 			if (colon)
1754 				colon = 0;
1755 			else
1756 				goto no_colon;
1757 			if (match("sb", 2)) {		/* super block */
1758 				if (c_count == 2) {
1759 					cur_cgrp = -1;
1760 					type = objsz = SB;
1761 					laststyle = '=';
1762 					lastpo = 's';
1763 					should_print = 1;
1764 					continue;
1765 				}
1766 				if (type == NUMB)
1767 					value = addr;
1768 				if (value > fs->fs_ncg - 1) {
1769 					printf("maximum super block is ");
1770 					print(fs->fs_ncg - 1, 8, -8, 0);
1771 					printf("\n");
1772 					error++;
1773 					continue;
1774 				}
1775 				type = objsz = SB;
1776 				cur_cgrp = (long)value;
1777 				addr = cgsblock(fs, cur_cgrp) << FRGSHIFT;
1778 				continue;
1779 			}
1780 			if (match("shadow", 2)) {	/* shadow inode data */
1781 				if (type == NUMB)
1782 					value = addr;
1783 				objsz = SHADOW_DATA;
1784 				type = SHADOW_DATA;
1785 				addr = getshadowslot(value);
1786 				continue;
1787 			}
1788 			if (match("si", 2)) {   /* shadow inode field */
1789 				acting_on_inode = 1;
1790 				should_print = 1;
1791 				addr = (long)
1792 				    &((struct dinode *)cur_ino)->di_shadow;
1793 				value = get(LONG);
1794 				type = NULL;
1795 				continue;
1796 			}
1797 
1798 			if (match("sz", 2)) {		/* file size */
1799 				acting_on_inode = 1;
1800 				should_print = 1;
1801 				addr = (long)
1802 					&((struct dinode *)cur_ino)->di_size;
1803 				value = get(U_OFFSET_T);
1804 				type = NULL;
1805 				objsz = U_OFFSET_T;
1806 				laststyle = '=';
1807 				lastpo = 'X';
1808 				continue;
1809 			}
1810 			goto bad_syntax;
1811 
1812 		case 'u':
1813 			if (colon)
1814 				colon = 0;
1815 			else
1816 				goto no_colon;
1817 			if (match("uid", 1)) {		/* user id */
1818 				acting_on_inode = 1;
1819 				should_print = 1;
1820 				addr = (long)
1821 					&((struct dinode *)cur_ino)->di_uid;
1822 				value = get(SHORT);
1823 				type = NULL;
1824 				continue;
1825 			}
1826 			goto bad_syntax;
1827 
1828 		case 'F': /* buffer status (internal use only) */
1829 			if (colon)
1830 				colon = 0;
1831 			else
1832 				goto no_colon;
1833 			for (bp = bhdr.fwd; bp != &bhdr; bp = bp->fwd)
1834 				printf("%8" PRIx64 " %d\n",
1835 				    bp->blkno, bp->valid);
1836 			printf("\n");
1837 			printf("# commands\t\t%ld\n", commands);
1838 			printf("# read requests\t\t%ld\n", read_requests);
1839 			printf("# actual disk reads\t%ld\n", actual_disk_reads);
1840 			continue;
1841 no_colon:
1842 		printf("a colon should precede a command\n");
1843 		error++;
1844 		continue;
1845 bad_syntax:
1846 		printf("more letters needed to distinguish command\n");
1847 		error++;
1848 		continue;
1849 		}
1850 	}
1851 }
1852 
1853 /*
1854  * usage - print usage and exit
1855  */
1856 static void
1857 usage(progname)
1858 	char *progname;
1859 {
1860 	printf("usage:   %s [options] special\n", progname);
1861 	printf("options:\n");
1862 	printf("\t-o		Specify ufs filesystem sepcific options\n");
1863 	printf("		Available suboptions are:\n");
1864 	printf("\t\t?		display usage\n");
1865 	printf("\t\to		override some error conditions\n");
1866 	printf("\t\tp=\"string\"	set prompt to string\n");
1867 	printf("\t\tw		open for write\n");
1868 	exit(1);
1869 }
1870 
1871 /*
1872  * getachar - get next character from input buffer.
1873  */
1874 static char
1875 getachar()
1876 {
1877 	return (input_buffer[input_pointer++]);
1878 }
1879 
1880 /*
1881  * ungetachar - return character to input buffer.
1882  */
1883 static void
1884 ungetachar(c)
1885 	register char	c;
1886 {
1887 	if (input_pointer == 0) {
1888 		printf("internal problem maintaining input buffer\n");
1889 		error++;
1890 		return;
1891 	}
1892 	input_buffer[--input_pointer] = c;
1893 }
1894 
1895 /*
1896  * getnextinput - display the prompt and read an input line.
1897  *	An input line is up to 128 characters terminated by the newline
1898  *	character.  Handle overflow, shell escape, and eof.
1899  */
1900 static void
1901 getnextinput()
1902 {
1903 	register int	i;
1904 	register char	c;
1905 	register short	pid, rpid;
1906 	int		retcode;
1907 
1908 newline:
1909 	i = 0;
1910 	printf("%s", prompt);
1911 ignore_eol:
1912 	while ((c = getc(stdin)) != '\n' && !(c == '!' && i == 0) &&
1913 					!feof(stdin) && i <= INPUTBUFFER - 2)
1914 		input_buffer[i++] = c;
1915 	if (i > 0 && input_buffer[i - 1] == '\\') {
1916 		input_buffer[i++] = c;
1917 		goto ignore_eol;
1918 	}
1919 	if (feof(stdin)) {
1920 		printf("\n");
1921 		exit(0);
1922 	}
1923 	if (c == '!') {
1924 		if ((pid = fork()) == 0) {
1925 			(void) execl(_PATH_BSHELL, "sh", "-t", 0);
1926 			error++;
1927 			return;
1928 		}
1929 		while ((rpid = wait(&retcode)) != pid && rpid != -1)
1930 			;
1931 		printf("!\n");
1932 		goto newline;
1933 	}
1934 	if (c != '\n')
1935 		printf("input truncated to 128 characters\n");
1936 	input_buffer[i] = '\n';
1937 	input_pointer = 0;
1938 }
1939 
1940 /*
1941  * eat_spaces - read extraneous spaces.
1942  */
1943 static void
1944 eat_spaces()
1945 {
1946 	register char	c;
1947 
1948 	while ((c = getachar()) == ' ')
1949 		;
1950 	ungetachar(c);
1951 }
1952 
1953 /*
1954  * restore_inode - set up all inode indicators so inum is now
1955  *	the current inode.
1956  */
1957 static void
1958 restore_inode(inum)
1959 	ino_t		inum;
1960 {
1961 	errinum = cur_inum = inum;
1962 	addr = errino = cur_ino = itob(inum);
1963 }
1964 
1965 /*
1966  * match - return false if the input does not match string up to
1967  *	upto letters.   Then proceed to chew up extraneous letters.
1968  */
1969 static int
1970 match(string, upto)
1971 	register char	*string;
1972 	register int	upto;
1973 {
1974 	register int	i, length = strlen(string) - 1;
1975 	register char	c;
1976 	int		save_upto = upto;
1977 
1978 	while (--upto) {
1979 		string++;
1980 		if ((c = getachar()) != *string) {
1981 			for (i = save_upto - upto; i; i--) {
1982 				ungetachar(c);
1983 				c = *--string;
1984 			}
1985 			return (0);
1986 		}
1987 		length--;
1988 	}
1989 	while (length--) {
1990 		string++;
1991 		if ((c = getachar()) != *string) {
1992 			ungetachar(c);
1993 			return (1);
1994 		}
1995 	}
1996 	return (1);
1997 }
1998 
1999 /*
2000  * expr - expression evaluator.  Will evaluate expressions from
2001  *	left to right with no operator precedence.  Parentheses may
2002  *	be used.
2003  */
2004 static long
2005 expr()
2006 {
2007 	register long	numb = 0, temp;
2008 	register char	c;
2009 
2010 	numb = term();
2011 	for (;;) {
2012 		if (error)
2013 			return (~0);	/* error is set so value is ignored */
2014 		c = getachar();
2015 		switch (c) {
2016 
2017 		case '+':
2018 			numb += term();
2019 			continue;
2020 
2021 		case '-':
2022 			numb -= term();
2023 			continue;
2024 
2025 		case '*':
2026 			numb *= term();
2027 			continue;
2028 
2029 		case '%':
2030 			temp = term();
2031 			if (!temp) {
2032 				printf("divide by zero\n");
2033 				error++;
2034 				return (~0);
2035 			}
2036 			numb /= temp;
2037 			continue;
2038 
2039 		case ')':
2040 			paren--;
2041 			return (numb);
2042 
2043 		default:
2044 			ungetachar(c);
2045 			if (paren && !error) {
2046 				printf("missing ')'\n");
2047 				error++;
2048 			}
2049 			return (numb);
2050 		}
2051 	}
2052 }
2053 
2054 /*
2055  * term - used by expression evaluator to get an operand.
2056  */
2057 static long
2058 term()
2059 {
2060 	register char	c;
2061 
2062 	switch (c = getachar()) {
2063 
2064 	default:
2065 		ungetachar(c);
2066 		/*FALLTHRU*/
2067 	case '+':
2068 		return (getnumb());
2069 
2070 	case '-':
2071 		return (-getnumb());
2072 
2073 	case '(':
2074 		paren++;
2075 		return (expr());
2076 	}
2077 }
2078 
2079 /*
2080  * getnumb - read a number from the input stream.  A leading
2081  *	zero signifies octal interpretation, a leading '0x'
2082  *	signifies hexadecimal, and a leading '0t' signifies
2083  *	decimal.  If the first character is a character,
2084  *	return an error.
2085  */
2086 static long
2087 getnumb()
2088 {
2089 
2090 	register char	c, savec;
2091 	long		number = 0, tbase, num;
2092 	extern short	error;
2093 
2094 	c = getachar();
2095 	if (!digit(c)) {
2096 		error++;
2097 		ungetachar(c);
2098 		return (-1);
2099 	}
2100 	if (c == '0') {
2101 		tbase = OCTAL;
2102 		if ((c = getachar()) == 'x')
2103 			tbase = HEX;
2104 		else if (c == 't')
2105 			tbase = DECIMAL;
2106 		else ungetachar(c);
2107 	} else {
2108 		tbase = base;
2109 		ungetachar(c);
2110 	}
2111 	for (;;) {
2112 		num = tbase;
2113 		c = savec = getachar();
2114 		if (HEXLETTER(c))
2115 			c = uppertolower(c);
2116 		switch (tbase) {
2117 		case HEX:
2118 			if (hexletter(c)) {
2119 				num = hextodigit(c);
2120 				break;
2121 			}
2122 			/*FALLTHRU*/
2123 		case DECIMAL:
2124 			if (digit(c))
2125 				num = numtodigit(c);
2126 			break;
2127 		case OCTAL:
2128 			if (octaldigit(c))
2129 				num = numtodigit(c);
2130 		}
2131 		if (num == tbase)
2132 			break;
2133 		number = number * tbase + num;
2134 	}
2135 	ungetachar(savec);
2136 	return (number);
2137 }
2138 
2139 /*
2140  * find - the syntax is almost identical to the unix command.
2141  *		find dir [-name pattern] [-inum number]
2142  *	Note:  only one of -name or -inum may be used at a time.
2143  *	       Also, the -print is not needed (implied).
2144  */
2145 static void
2146 find()
2147 {
2148 	register struct filenames	*fn;
2149 	register char			c;
2150 	long				temp;
2151 	short				mode;
2152 
2153 	eat_spaces();
2154 	temp = cur_inum;
2155 	top = filenames - 1;
2156 	doing_cd = 1;
2157 	parse();
2158 	doing_cd = 0;
2159 	if (nfiles != 1) {
2160 		restore_inode((ino_t)temp);
2161 		if (!error) {
2162 			print_path(input_path, (int)input_pathp);
2163 			if (nfiles == 0)
2164 				printf(" not found\n");
2165 			else
2166 				printf(" ambiguous\n");
2167 			error++;
2168 			return;
2169 		}
2170 	}
2171 	restore_inode(filenames->ino);
2172 	freemem(filenames, nfiles);
2173 	nfiles = 0;
2174 	top = filenames - 1;
2175 	if ((mode = icheck(addr)) == 0)
2176 		return;
2177 	if ((mode & IFMT) != IFDIR) {
2178 		print_path(input_path, (int)input_pathp);
2179 		printf(" not a directory\n");
2180 		error++;
2181 		return;
2182 	}
2183 	eat_spaces();
2184 	if ((c = getachar()) != '-') {
2185 		restore_inode((ino_t)temp);
2186 		printf("missing '-'\n");
2187 		error++;
2188 		return;
2189 	}
2190 	find_by_name = find_by_inode = 0;
2191 	c = getachar();
2192 	if (match("name", 4)) {
2193 		eat_spaces();
2194 		find_by_name = 1;
2195 	} else if (match("inum", 4)) {
2196 		eat_spaces();
2197 		find_ino = expr();
2198 		if (error) {
2199 			restore_inode((ino_t)temp);
2200 			return;
2201 		}
2202 		while ((c = getachar()) != '\n')
2203 			;
2204 		ungetachar(c);
2205 		find_by_inode = 1;
2206 	} else {
2207 		restore_inode((ino_t)temp);
2208 		printf("use -name or -inum with find\n");
2209 		error++;
2210 		return;
2211 	}
2212 	doing_find = 1;
2213 	parse();
2214 	doing_find = 0;
2215 	if (error) {
2216 		restore_inode((ino_t)temp);
2217 		return;
2218 	}
2219 	for (fn = filenames; fn <= top; fn++) {
2220 		if (fn->find == 0)
2221 			continue;
2222 		printf("i#: ");
2223 		print(fn->ino, 12, -8, 0);
2224 		print_path(fn->fname, (int)fn->len);
2225 		printf("\n");
2226 	}
2227 	restore_inode((ino_t)temp);
2228 }
2229 
2230 /*
2231  * ls - do an ls.  Should behave exactly as ls(1).
2232  *	Only -R and -l is supported and -l gives different results.
2233  */
2234 static void
2235 ls(fn0, fnlast, level)
2236 	struct filenames		*fn0, *fnlast;
2237 	short				level;
2238 {
2239 	register struct filenames	*fn, *fnn;
2240 
2241 	fn = fn0;
2242 	for (;;) {
2243 		fn0 = fn;
2244 		if (fn0->len) {
2245 			cmp_level = level;
2246 			qsort((char *)fn0, fnlast - fn0 + 1,
2247 				sizeof (struct filenames), fcmp);
2248 		}
2249 		for (fnn = fn, fn++; fn <= fnlast; fnn = fn, fn++) {
2250 			if (fnn->len != fn->len && level == fnn->len - 1)
2251 				break;
2252 			if (fnn->len == 0)
2253 				continue;
2254 			if (strcmp(fn->fname[level], fnn->fname[level]))
2255 				break;
2256 		}
2257 		if (fn0->len && level != fn0->len - 1)
2258 			ls(fn0, fnn, level + 1);
2259 		else {
2260 			if (fn0 != filenames)
2261 				printf("\n");
2262 			print_path(fn0->fname, (int)(fn0->len - 1));
2263 			printf(":\n");
2264 			if (fn0->len == 0)
2265 				cmp_level = level;
2266 			else
2267 				cmp_level = level + 1;
2268 			qsort((char *)fn0, fnn - fn0 + 1,
2269 				sizeof (struct filenames), fcmp);
2270 			formatf(fn0, fnn);
2271 			nfiles -= fnn - fn0 + 1;
2272 		}
2273 		if (fn > fnlast)
2274 			return;
2275 	}
2276 }
2277 
2278 /*
2279  * formatf - code lifted from ls.
2280  */
2281 static void
2282 formatf(fn0, fnlast)
2283 	register struct filenames	*fn0, *fnlast;
2284 {
2285 	register struct filenames	*fn;
2286 	int				width = 0, w, nentry = fnlast - fn0 + 1;
2287 	int				i, j, columns, lines;
2288 	char				*cp;
2289 
2290 	if (long_list) {
2291 		columns = 1;
2292 	} else {
2293 		for (fn = fn0; fn <= fnlast; fn++) {
2294 			int len = strlen(fn->fname[cmp_level]) + 2;
2295 
2296 			if (len > width)
2297 				width = len;
2298 		}
2299 		width = (width + 8) &~ 7;
2300 		columns = 80 / width;
2301 		if (columns == 0)
2302 			columns = 1;
2303 	}
2304 	lines = (nentry + columns - 1) / columns;
2305 	for (i = 0; i < lines; i++) {
2306 		for (j = 0; j < columns; j++) {
2307 			fn = fn0 + j * lines + i;
2308 			if (long_list) {
2309 				printf("i#: ");
2310 				print(fn->ino, 12, -8, 0);
2311 			}
2312 			if ((cp = fmtentry(fn)) == NULL) {
2313 				printf("cannot read inode %ld\n", fn->ino);
2314 				return;
2315 			}
2316 			printf("%s", cp);
2317 			if (fn + lines > fnlast) {
2318 				printf("\n");
2319 				break;
2320 			}
2321 			w = strlen(cp);
2322 			while (w < width) {
2323 				w = (w + 8) &~ 7;
2324 				(void) putchar('\t');
2325 			}
2326 		}
2327 	}
2328 }
2329 
2330 /*
2331  * fmtentry - code lifted from ls.
2332  */
2333 static char *
2334 fmtentry(fn)
2335 	register struct filenames	*fn;
2336 {
2337 	static char			fmtres[BUFSIZ];
2338 	register struct dinode		*ip;
2339 	register char			*cptr, *cp, *dp;
2340 
2341 	dp = &fmtres[0];
2342 	for (cp = fn->fname[cmp_level]; *cp; cp++) {
2343 		if (*cp < ' ' || *cp >= 0177)
2344 			*dp++ = '?';
2345 		else
2346 			*dp++ = *cp;
2347 	}
2348 	addr = itob(fn->ino);
2349 	if ((cptr = getblk(addr)) == 0)
2350 		return (NULL);
2351 	cptr += blkoff(fs, addr);
2352 	/*LINTED*/
2353 	ip = (struct dinode *)cptr;
2354 	switch (ip->di_mode & IFMT) {
2355 	case IFDIR:
2356 		*dp++ = '/';
2357 		break;
2358 	case IFLNK:
2359 		*dp++ = '@';
2360 		break;
2361 	case IFSOCK:
2362 		*dp++ = '=';
2363 		break;
2364 #ifdef IFIFO
2365 	case IFIFO:
2366 		*dp++ = 'p';
2367 		break;
2368 #endif
2369 	case IFCHR:
2370 	case IFBLK:
2371 	case IFREG:
2372 		if (ip->di_mode & 0111)
2373 			*dp++ = '*';
2374 		else
2375 			*dp++ = ' ';
2376 		break;
2377 	default:
2378 		*dp++ = '?';
2379 
2380 	}
2381 	*dp++ = 0;
2382 	return (fmtres);
2383 }
2384 
2385 /*
2386  * fcmp - routine used by qsort.  Will sort first by name, then
2387  *	then by pathname length if names are equal.  Uses global
2388  *	cmp_level to tell what component of the path name we are comparing.
2389  */
2390 static int
2391 fcmp(f1, f2)
2392 	register struct filenames	*f1, *f2;
2393 {
2394 	int 				value;
2395 
2396 	if ((value = strcmp(f1->fname[cmp_level], f2->fname[cmp_level])))
2397 		return (value);
2398 	return (f1->len - f2->len);
2399 }
2400 
2401 /*
2402  * ffcmp - routine used by qsort.  Sort only by pathname length.
2403  */
2404 static int
2405 ffcmp(f1, f2)
2406 	register struct filenames	*f1, *f2;
2407 {
2408 	return (f1->len - f2->len);
2409 }
2410 
2411 /*
2412  * parse - set up the call to follow_path.
2413  */
2414 static void
2415 parse()
2416 {
2417 	register int	i;
2418 	char		c;
2419 
2420 	stack_pathp = input_pathp = -1;
2421 	if ((c = getachar()) == '/') {
2422 		while ((c = getachar()) == '/')
2423 			;
2424 		ungetachar(c);
2425 		cur_inum = 2;
2426 		c = getachar();
2427 		if ((c == '\n') || ((doing_cd) && (c == ' '))) {
2428 			ungetachar(c);
2429 			if (doing_cd) {
2430 				top++;
2431 				top->ino = 2;
2432 				top->len = -1;
2433 				nfiles = 1;
2434 				return;
2435 			}
2436 		} else
2437 			ungetachar(c);
2438 	} else {
2439 		ungetachar(c);
2440 		stack_pathp = current_pathp;
2441 		if (!doing_find)
2442 			input_pathp = current_pathp;
2443 		for (i = 0; i <= current_pathp; i++) {
2444 			if (!doing_find)
2445 				(void) strcpy(input_path[i], current_path[i]);
2446 			(void) strcpy(stack_path[i], current_path[i]);
2447 		}
2448 	}
2449 	getname();
2450 	follow_path((long)(stack_pathp + 1), cur_inum);
2451 }
2452 
2453 /*
2454  * follow_path - called by cd, find, and ls.
2455  *	input_path holds the name typed by the user.
2456  *	stack_path holds the name at the current depth.
2457  */
2458 static void
2459 follow_path(level, inum)
2460 	long			level, inum;
2461 {
2462 	register struct direct	*dirp;
2463 	register char		**ccptr, *cptr;
2464 	register int		i;
2465 	struct filenames	*tos, *bos, *fn, *fnn, *fnnn;
2466 	long			block;
2467 	short			mode;
2468 
2469 	tos = top + 1;
2470 	restore_inode((ino_t)inum);
2471 	if ((mode = icheck(addr)) == 0)
2472 		return;
2473 	if ((mode & IFMT) != IFDIR)
2474 	    return;
2475 	block = cur_bytes = 0;
2476 	while (cur_bytes < filesize) {
2477 	    if (block == 0 || bcomp(addr)) {
2478 		error = 0;
2479 		if ((addr = ((u_offset_t)bmap(block++) <<
2480 				(u_offset_t)FRGSHIFT)) == 0)
2481 		    break;
2482 		if ((cptr = getblk(addr)) == 0)
2483 		    break;
2484 		cptr += blkoff(fs, addr);
2485 	    }
2486 		/*LINTED*/
2487 	    dirp = (struct direct *)cptr;
2488 	    if (dirp->d_ino) {
2489 		if (level > input_pathp || doing_find ||
2490 			compare(input_path[level], &dirp->d_name[0], 1)) {
2491 		    if ((doing_find) &&
2492 			((strcmp(dirp->d_name, ".") == 0 ||
2493 					strcmp(dirp->d_name, "..") == 0)))
2494 			goto duplicate;
2495 		    if (++top - filenames >= maxfiles) {
2496 			printf("too many files\n");
2497 			error++;
2498 			return;
2499 		    }
2500 		    top->fname = (char **)calloc(FIRST_DEPTH, sizeof (char **));
2501 		    top->flag = 0;
2502 		    if (top->fname == 0) {
2503 			printf("out of memory\n");
2504 			error++;
2505 			return;
2506 		    }
2507 		    nfiles++;
2508 		    top->ino = dirp->d_ino;
2509 		    top->len = stack_pathp;
2510 		    top->find = 0;
2511 		    if (doing_find) {
2512 			if (find_by_name) {
2513 			    if (compare(input_path[0], &dirp->d_name[0], 1))
2514 				top->find = 1;
2515 			} else if (find_by_inode)
2516 			    if (find_ino == dirp->d_ino)
2517 				top->find = 1;
2518 		    }
2519 		    if (top->len + 1 >= FIRST_DEPTH && top->flag == 0) {
2520 			ccptr = (char **)calloc(SECOND_DEPTH, sizeof (char **));
2521 			if (ccptr == 0) {
2522 			    printf("out of memory\n");
2523 			    error++;
2524 			    return;
2525 			}
2526 			for (i = 0; i < FIRST_DEPTH; i++)
2527 				ccptr[i] = top->fname[i];
2528 			free((char *)top->fname);
2529 			top->fname = ccptr;
2530 			top->flag = 1;
2531 		    }
2532 		    if (top->len >= SECOND_DEPTH) {
2533 			printf("maximum depth exceeded, try to cd lower\n");
2534 			error++;
2535 			return;
2536 		    }
2537 			/*
2538 			 * Copy current depth.
2539 			 */
2540 		    for (i = 0; i <= stack_pathp; i++) {
2541 			top->fname[i] = calloc(1, strlen(stack_path[i])+1);
2542 			if (top->fname[i] == 0) {
2543 			    printf("out of memory\n");
2544 			    error++;
2545 			    return;
2546 			}
2547 			(void) strcpy(top->fname[i], stack_path[i]);
2548 		    }
2549 			/*
2550 			 * Check for '.' or '..' typed.
2551 			 */
2552 		    if ((level <= input_pathp) &&
2553 				(strcmp(input_path[level], ".") == 0 ||
2554 					strcmp(input_path[level], "..") == 0)) {
2555 			if (strcmp(input_path[level], "..") == 0 &&
2556 							top->len >= 0) {
2557 			    free(top->fname[top->len]);
2558 			    top->len -= 1;
2559 			}
2560 		    } else {
2561 			/*
2562 			 * Check for duplicates.
2563 			 */
2564 			if (!doing_cd && !doing_find) {
2565 			    for (fn = filenames; fn < top; fn++) {
2566 				if (fn->ino == dirp->d_ino &&
2567 					    fn->len == stack_pathp + 1) {
2568 				    for (i = 0; i < fn->len; i++)
2569 					if (strcmp(fn->fname[i], stack_path[i]))
2570 					    break;
2571 				    if (i != fn->len ||
2572 					    strcmp(fn->fname[i], dirp->d_name))
2573 					continue;
2574 				    freemem(top, 1);
2575 				    if (top == filenames)
2576 					top = NULL;
2577 				    else
2578 					top--;
2579 					nfiles--;
2580 					goto duplicate;
2581 				}
2582 			    }
2583 			}
2584 			top->len += 1;
2585 			top->fname[top->len] = calloc(1,
2586 						strlen(&dirp->d_name[0])+1);
2587 			if (top->fname[top->len] == 0) {
2588 			    printf("out of memory\n");
2589 			    error++;
2590 			    return;
2591 			}
2592 			(void) strcpy(top->fname[top->len], &dirp->d_name[0]);
2593 		    }
2594 		}
2595 	    }
2596 duplicate:
2597 	    addr += dirp->d_reclen;
2598 	    cptr += dirp->d_reclen;
2599 	    cur_bytes += dirp->d_reclen;
2600 	}
2601 	if (top < filenames)
2602 	    return;
2603 	if ((doing_cd && level == input_pathp) ||
2604 		(!recursive && !doing_find && level > input_pathp))
2605 	    return;
2606 	bos = top;
2607 	/*
2608 	 * Check newly added entries to determine if further expansion
2609 	 * is required.
2610 	 */
2611 	for (fn = tos; fn <= bos; fn++) {
2612 		/*
2613 		 * Avoid '.' and '..' if beyond input.
2614 		 */
2615 	    if ((recursive || doing_find) && (level > input_pathp) &&
2616 		(strcmp(fn->fname[fn->len], ".") == 0 ||
2617 			strcmp(fn->fname[fn->len], "..") == 0))
2618 		continue;
2619 	    restore_inode(fn->ino);
2620 	    if ((mode = icheck(cur_ino)) == 0)
2621 		return;
2622 	    if ((mode & IFMT) == IFDIR || level < input_pathp) {
2623 		/*
2624 		 * Set up current depth, remove current entry and
2625 		 * continue recursion.
2626 		 */
2627 		for (i = 0; i <= fn->len; i++)
2628 		    (void) strcpy(stack_path[i], fn->fname[i]);
2629 		stack_pathp = fn->len;
2630 		if (!doing_find &&
2631 			(!recursive || (recursive && level <= input_pathp))) {
2632 			/*
2633 			 * Remove current entry by moving others up.
2634 			 */
2635 		    freemem(fn, 1);
2636 		    fnn = fn;
2637 		    for (fnnn = fnn, fnn++; fnn <= top; fnnn = fnn, fnn++) {
2638 			fnnn->ino = fnn->ino;
2639 			fnnn->len = fnn->len;
2640 			if (fnnn->len + 1 < FIRST_DEPTH) {
2641 			    fnnn->fname = (char **)calloc(FIRST_DEPTH,
2642 							sizeof (char **));
2643 			    fnnn->flag = 0;
2644 			} else if (fnnn->len < SECOND_DEPTH) {
2645 			    fnnn->fname = (char **)calloc(SECOND_DEPTH,
2646 							sizeof (char **));
2647 			    fnnn->flag = 1;
2648 			} else {
2649 			    printf("maximum depth exceeded, ");
2650 			    printf("try to cd lower\n");
2651 			    error++;
2652 			    return;
2653 			}
2654 			for (i = 0; i <= fnn->len; i++)
2655 			    fnnn->fname[i] = fnn->fname[i];
2656 		    }
2657 		    if (fn == tos)
2658 			fn--;
2659 		    top--;
2660 		    bos--;
2661 		    nfiles--;
2662 		}
2663 		follow_path(level + 1, cur_inum);
2664 		if (error)
2665 			return;
2666 	    }
2667 	}
2668 }
2669 
2670 /*
2671  * getname - break up the pathname entered by the user into components.
2672  */
2673 static void
2674 getname()
2675 {
2676 	register int	i;
2677 	char		c;
2678 
2679 	if ((c = getachar()) == '\n') {
2680 	    ungetachar(c);
2681 	    return;
2682 	}
2683 	ungetachar(c);
2684 	input_pathp++;
2685 clear:
2686 	for (i = 0; i < MAXNAMLEN; i++)
2687 	    input_path[input_pathp][i] = '\0';
2688 	for (;;) {
2689 	    c = getachar();
2690 	    if (c == '\\') {
2691 		if ((int)strlen(input_path[input_pathp]) + 1 >= MAXNAMLEN) {
2692 		    printf("maximum name length exceeded, ");
2693 		    printf("truncating\n");
2694 		    return;
2695 		}
2696 		input_path[input_pathp][strlen(input_path[input_pathp])] = c;
2697 		input_path[input_pathp][strlen(input_path[input_pathp])] =
2698 						getachar();
2699 		continue;
2700 	    }
2701 	    if (c == ' ' || c == '\n') {
2702 		ungetachar(c);
2703 		return;
2704 	    }
2705 	    if (!doing_find && c == '/') {
2706 		if (++input_pathp >= MAXPATHLEN) {
2707 		    printf("maximum path length exceeded, ");
2708 		    printf("truncating\n");
2709 		    input_pathp--;
2710 		    return;
2711 		}
2712 		goto clear;
2713 	    }
2714 	    if ((int)strlen(input_path[input_pathp]) >= MAXNAMLEN) {
2715 		printf("maximum name length exceeded, truncating\n");
2716 		return;
2717 	    }
2718 	    input_path[input_pathp][strlen(input_path[input_pathp])] = c;
2719 	}
2720 }
2721 
2722 /*
2723  * compare - check if a filename matches the pattern entered by the user.
2724  *	Handles '*', '?', and '[]'.
2725  */
2726 static int
2727 compare(s1, s2, at_start)
2728 	char		*s1, *s2;
2729 	short		at_start;
2730 {
2731 	register char	c, *s;
2732 
2733 	s = s2;
2734 	while ((c = *s1) != NULL) {
2735 		if (c == '*') {
2736 			if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
2737 				return (0);
2738 			if (*++s1 == 0)
2739 				return (1);
2740 			while (*s2) {
2741 				if (compare(s1, s2, 0))
2742 					return (1);
2743 				if (error)
2744 					return (0);
2745 				s2++;
2746 			}
2747 		}
2748 		if (*s2 == 0)
2749 			return (0);
2750 		if (c == '\\') {
2751 			s1++;
2752 			goto compare_chars;
2753 		}
2754 		if (c == '?') {
2755 			if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
2756 				return (0);
2757 			s1++;
2758 			s2++;
2759 			continue;
2760 		}
2761 		if (c == '[') {
2762 			s1++;
2763 			if (*s2 >= *s1++) {
2764 				if (*s1++ != '-') {
2765 					printf("missing '-'\n");
2766 					error++;
2767 					return (0);
2768 				}
2769 				if (*s2 <= *s1++) {
2770 					if (*s1++ != ']') {
2771 						printf("missing ']'");
2772 						error++;
2773 						return (0);
2774 					}
2775 					s2++;
2776 					continue;
2777 				}
2778 			}
2779 		}
2780 compare_chars:
2781 		if (*s1++ == *s2++)
2782 			continue;
2783 		else
2784 			return (0);
2785 	}
2786 	if (*s1 == *s2)
2787 		return (1);
2788 	return (0);
2789 }
2790 
2791 /*
2792  * freemem - free the memory allocated to the filenames structure.
2793  */
2794 static void
2795 freemem(p, numb)
2796 	struct filenames	*p;
2797 	int			numb;
2798 {
2799 	register int		i, j;
2800 
2801 	if (numb == 0)
2802 		return;
2803 	for (i = 0; i < numb; i++, p++) {
2804 		for (j = 0; j <= p->len; j++)
2805 			free(p->fname[j]);
2806 		free((char *)p->fname);
2807 	}
2808 }
2809 
2810 /*
2811  * print_path - print the pathname held in p.
2812  */
2813 static void
2814 print_path(p, pntr)
2815 	char		*p[];
2816 	int		pntr;
2817 {
2818 	register int	i;
2819 
2820 	printf("/");
2821 	if (pntr >= 0) {
2822 		for (i = 0; i < pntr; i++)
2823 			printf("%s/", p[i]);
2824 		printf("%s", p[pntr]);
2825 	}
2826 }
2827 
2828 /*
2829  * fill - fill a section with a value or string.
2830  *	addr,count:fill=[value, "string"].
2831  */
2832 static void
2833 fill()
2834 {
2835 	register char	*cptr;
2836 	register int	i;
2837 	short		eof_flag, end = 0, eof = 0;
2838 	long		temp, tcount;
2839 	u_offset_t	taddr;
2840 
2841 	if (wrtflag == O_RDONLY) {
2842 		printf("not opened for write '-w'\n");
2843 		error++;
2844 		return;
2845 	}
2846 	temp = expr();
2847 	if (error)
2848 		return;
2849 	if ((cptr = getblk(addr)) == 0)
2850 		return;
2851 	if (type == NUMB)
2852 		eof_flag = 0;
2853 	else
2854 		eof_flag = 1;
2855 	taddr = addr;
2856 	switch (objsz) {
2857 	case LONG:
2858 		addr &= ~(LONG - 1);
2859 		break;
2860 	case SHORT:
2861 		addr &= ~(SHORT - 1);
2862 		temp &= 0177777L;
2863 		break;
2864 	case CHAR:
2865 		temp &= 0377;
2866 	}
2867 	cur_bytes -= taddr - addr;
2868 	cptr += blkoff(fs, addr);
2869 	tcount = check_addr(eof_flag, &end, &eof, 0);
2870 	for (i = 0; i < tcount; i++) {
2871 		switch (objsz) {
2872 		case LONG:
2873 			/*LINTED*/
2874 			*(long *)cptr = temp;
2875 			break;
2876 		case SHORT:
2877 			/*LINTED*/
2878 			*(short *)cptr = temp;
2879 			break;
2880 		case CHAR:
2881 			*cptr = temp;
2882 		}
2883 		cptr += objsz;
2884 	}
2885 	addr += (tcount - 1) * objsz;
2886 	cur_bytes += (tcount - 1) * objsz;
2887 	put((u_offset_t)temp, objsz);
2888 	if (eof) {
2889 		printf("end of file\n");
2890 		error++;
2891 	} else if (end) {
2892 		printf("end of block\n");
2893 		error++;
2894 	}
2895 }
2896 
2897 /*
2898  * get - read a byte, short or long from the file system.
2899  *	The entire block containing the desired item is read
2900  *	and the appropriate data is extracted and returned.
2901  */
2902 static offset_t
2903 get(lngth)
2904 	short		lngth;
2905 {
2906 
2907 	register char	*bptr;
2908 	u_offset_t	temp = addr;
2909 
2910 	objsz = lngth;
2911 	if (objsz == INODE || objsz == SHORT)
2912 		temp &= ~(SHORT - 1);
2913 	else if (objsz == DIRECTORY || objsz == LONG || objsz == SHADOW_DATA)
2914 		temp &= ~(LONG - 1);
2915 	if ((bptr = getblk(temp)) == 0)
2916 		return (-1);
2917 	bptr += blkoff(fs, temp);
2918 	switch (objsz) {
2919 	case CHAR:
2920 		return ((offset_t)*bptr);
2921 	case SHORT:
2922 	case INODE:
2923 		/*LINTED*/
2924 		return ((offset_t)(*(short *)bptr));
2925 	case LONG:
2926 	case DIRECTORY:
2927 	case SHADOW_DATA:
2928 		/*LINTED*/
2929 		return ((offset_t)(*(long *)bptr));
2930 	case U_OFFSET_T:
2931 		/*LINTED*/
2932 		return (*(offset_t *)bptr);
2933 	}
2934 	return (0);
2935 }
2936 
2937 /*
2938  * cgrp_check - make sure that we don't bump the cylinder group
2939  *	beyond the total number of cylinder groups or before the start.
2940  */
2941 static int
2942 cgrp_check(cgrp)
2943 	long		cgrp;
2944 {
2945 	if (cgrp < 0) {
2946 		if (objsz == CGRP)
2947 			printf("beginning of cylinder groups\n");
2948 		else
2949 			printf("beginning of super blocks\n");
2950 		error++;
2951 		return (0);
2952 	}
2953 	if (cgrp >= fs->fs_ncg) {
2954 		if (objsz == CGRP)
2955 			printf("end of cylinder groups\n");
2956 		else
2957 			printf("end of super blocks\n");
2958 		error++;
2959 		return (0);
2960 	}
2961 	if (objsz == CGRP)
2962 		return (cgtod(fs, cgrp) << FRGSHIFT);
2963 	else
2964 		return (cgsblock(fs, cgrp) << FRGSHIFT);
2965 }
2966 
2967 /*
2968  * icheck -  make sure we can read the block containing the inode
2969  *	and determine the filesize (0 if inode not allocated).  Return
2970  *	0 if error otherwise return the mode.
2971  */
2972 icheck(address)
2973 	u_offset_t		address;
2974 {
2975 	register char		*cptr;
2976 	register struct dinode	*ip;
2977 
2978 	if ((cptr = getblk(address)) == 0)
2979 		return (0);
2980 	cptr += blkoff(fs, address);
2981 	/*LINTED*/
2982 	ip = (struct dinode *)cptr;
2983 	if ((ip->di_mode & IFMT) == 0) {
2984 		if (!override) {
2985 			printf("inode not allocated\n");
2986 			error++;
2987 			return (0);
2988 		}
2989 		blocksize = filesize = 0;
2990 	} else {
2991 		trapped++;
2992 		filesize = ip->di_size;
2993 		blocksize = filesize * 2;
2994 	}
2995 	return (ip->di_mode);
2996 }
2997 
2998 /*
2999  * getdirslot - get the address of the directory slot desired.
3000  */
3001 static u_offset_t
3002 getdirslot(slot)
3003 	long			slot;
3004 {
3005 	register char		*cptr;
3006 	register struct direct	*dirp;
3007 	register short		i;
3008 	char			*string = &scratch[0];
3009 	short			bod = 0, mode, temp;
3010 
3011 	if (slot < 0) {
3012 		slot = 0;
3013 		bod++;
3014 	}
3015 	if (type != DIRECTORY) {
3016 		if (type == BLOCK)
3017 			string = "block";
3018 		else
3019 			string = "fragment";
3020 		addr = bod_addr;
3021 		if ((cptr = getblk(addr)) == 0)
3022 			return (0);
3023 		cptr += blkoff(fs, addr);
3024 		cur_bytes = 0;
3025 		/*LINTED*/
3026 		dirp = (struct direct *)cptr;
3027 		for (dirslot = 0; dirslot < slot; dirslot++) {
3028 			/*LINTED*/
3029 			dirp = (struct direct *)cptr;
3030 			if (blocksize > filesize) {
3031 				if (cur_bytes + (long)dirp->d_reclen >=
3032 								filesize) {
3033 					printf("end of file\n");
3034 					erraddr = addr;
3035 					errcur_bytes = cur_bytes;
3036 					stringsize = STRINGSIZE(dirp);
3037 					error++;
3038 					return (addr);
3039 				}
3040 			} else {
3041 				if (cur_bytes + (long)dirp->d_reclen >=
3042 								blocksize) {
3043 					printf("end of %s\n", string);
3044 					erraddr = addr;
3045 					errcur_bytes = cur_bytes;
3046 					stringsize = STRINGSIZE(dirp);
3047 					error++;
3048 					return (addr);
3049 				}
3050 			}
3051 			cptr += dirp->d_reclen;
3052 			addr += dirp->d_reclen;
3053 			cur_bytes += dirp->d_reclen;
3054 		}
3055 		if (bod) {
3056 			if (blocksize > filesize)
3057 				printf("beginning of file\n");
3058 			else
3059 				printf("beginning of %s\n", string);
3060 			erraddr = addr;
3061 			errcur_bytes = cur_bytes;
3062 			error++;
3063 		}
3064 		stringsize = STRINGSIZE(dirp);
3065 		return (addr);
3066 	} else {
3067 		addr = cur_ino;
3068 		if ((mode = icheck(addr)) == 0)
3069 			return (0);
3070 		if (!override && (mode & IFDIR) == 0) {
3071 			printf("inode is not a directory\n");
3072 			error++;
3073 			return (0);
3074 		}
3075 		temp = slot;
3076 		i = cur_bytes = 0;
3077 		for (;;) {
3078 			if (i == 0 || bcomp(addr)) {
3079 				error = 0;
3080 				if ((addr = (bmap((long)i++) << FRGSHIFT)) == 0)
3081 					break;
3082 				if ((cptr = getblk(addr)) == 0)
3083 					break;
3084 				cptr += blkoff(fs, addr);
3085 			}
3086 			/*LINTED*/
3087 			dirp = (struct direct *)cptr;
3088 			value = dirp->d_ino;
3089 			if (!temp--)
3090 				break;
3091 			if (cur_bytes + (long)dirp->d_reclen >= filesize) {
3092 				printf("end of file\n");
3093 				dirslot = slot - temp - 1;
3094 				objsz = DIRECTORY;
3095 				erraddr = addr;
3096 				errcur_bytes = cur_bytes;
3097 				stringsize = STRINGSIZE(dirp);
3098 				error++;
3099 				return (addr);
3100 			}
3101 			addr += dirp->d_reclen;
3102 			cptr += dirp->d_reclen;
3103 			cur_bytes += dirp->d_reclen;
3104 		}
3105 		dirslot = slot;
3106 		objsz = DIRECTORY;
3107 		if (bod) {
3108 			printf("beginning of file\n");
3109 			erraddr = addr;
3110 			errcur_bytes = cur_bytes;
3111 			error++;
3112 		}
3113 		stringsize = STRINGSIZE(dirp);
3114 		return (addr);
3115 	}
3116 }
3117 
3118 
3119 /*
3120  * getshadowslot - get the address of the shadow data desired
3121  */
3122 static int
3123 getshadowslot(shadow)
3124 	long		shadow;
3125 {
3126 	struct ufs_fsd		fsd;
3127 	short			bod = 0, mode;
3128 	long			taddr, tcurbytes;
3129 
3130 	if (shadow < 0) {
3131 		shadow = 0;
3132 		bod++;
3133 	}
3134 	if (type != SHADOW_DATA) {
3135 		if (shadow < cur_shad) {
3136 			printf("can't scan shadow data in reverse\n");
3137 			error++;
3138 			return (0);
3139 		}
3140 	} else {
3141 		addr = cur_ino;
3142 		if ((mode = icheck(addr)) == 0)
3143 			return (0);
3144 		if (!override && (mode & IFMT) != IFSHAD) {
3145 			printf("inode is not a shadow\n");
3146 			error++;
3147 			return (0);
3148 		}
3149 		cur_bytes = 0;
3150 		cur_shad = 0;
3151 		syncshadowscan(1);	/* force synchronization */
3152 	}
3153 
3154 	for (; cur_shad < shadow; cur_shad++) {
3155 		taddr = addr;
3156 		tcurbytes = cur_bytes;
3157 		getshadowdata((long *)&fsd, LONG + LONG);
3158 		addr = taddr;
3159 		cur_bytes = tcurbytes;
3160 		if (cur_bytes + (long)fsd.fsd_size > filesize) {
3161 			syncshadowscan(0);
3162 			printf("end of file\n");
3163 			erraddr = addr;
3164 			errcur_bytes = cur_bytes;
3165 			error++;
3166 			return (addr);
3167 		}
3168 		addr += fsd.fsd_size;
3169 		cur_bytes += fsd.fsd_size;
3170 		syncshadowscan(0);
3171 	}
3172 	if (type == SHADOW_DATA)
3173 		objsz = SHADOW_DATA;
3174 	if (bod) {
3175 		printf("beginning of file\n");
3176 		erraddr = addr;
3177 		errcur_bytes = cur_bytes;
3178 		error++;
3179 	}
3180 	return (addr);
3181 }
3182 
3183 static void
3184 getshadowdata(buf, len)
3185 	long	*buf;
3186 	int	len;
3187 {
3188 	long	tfsd;
3189 
3190 	len /= LONG;
3191 	for (tfsd = 0; tfsd < len; tfsd++) {
3192 		buf[tfsd] = get(SHADOW_DATA);
3193 		addr += LONG;
3194 		cur_bytes += LONG;
3195 		syncshadowscan(0);
3196 	}
3197 }
3198 
3199 static void
3200 syncshadowscan(force)
3201 	int	force;
3202 {
3203 	long	curblkoff;
3204 	if (type == SHADOW_DATA && (force ||
3205 	    lblkno(fs, addr) != (bhdr.fwd)->blkno)) {
3206 		curblkoff = blkoff(fs, cur_bytes);
3207 		addr = bmap(lblkno(fs, cur_bytes)) << FRGSHIFT;
3208 		addr += curblkoff;
3209 		cur_bytes += curblkoff;
3210 		(void) getblk(addr);
3211 		objsz = SHADOW_DATA;
3212 	}
3213 }
3214 
3215 
3216 
3217 /*
3218  * putf - print a byte as an ascii character if possible.
3219  *	The exceptions are tabs, newlines, backslashes
3220  *	and nulls which are printed as the standard C
3221  *	language escapes. Characters which are not
3222  *	recognized are printed as \?.
3223  */
3224 static void
3225 putf(c)
3226 	register	char  c;
3227 {
3228 
3229 	if (c <= 037 || c >= 0177 || c == '\\') {
3230 		printf("\\");
3231 		switch (c) {
3232 		case '\\':
3233 			printf("\\");
3234 			break;
3235 		case '\t':
3236 			printf("t");
3237 			break;
3238 		case '\n':
3239 			printf("n");
3240 			break;
3241 		case '\0':
3242 			printf("0");
3243 			break;
3244 		default:
3245 			printf("?");
3246 		}
3247 	} else {
3248 		printf("%c", c);
3249 		printf(" ");
3250 	}
3251 }
3252 
3253 /*
3254  * put - write an item into the buffer for the current address
3255  *	block.  The value is checked to make sure that it will
3256  *	fit in the size given without truncation.  If successful,
3257  *	the entire block is written back to the file system.
3258  */
3259 static void
3260 put(item, lngth)
3261 	u_offset_t	item;
3262 	short		lngth;
3263 {
3264 
3265 	register char	*bptr, *sbptr;
3266 	long		s_err, nbytes;
3267 	long		olditem;
3268 
3269 	if (wrtflag == O_RDONLY) {
3270 		printf("not opened for write '-w'\n");
3271 		error++;
3272 		return;
3273 	}
3274 	objsz = lngth;
3275 	if ((sbptr = getblk(addr)) == 0)
3276 		return;
3277 	bptr = sbptr + blkoff(fs, addr);
3278 	switch (objsz) {
3279 	case LONG:
3280 	case DIRECTORY:
3281 		/*LINTED*/
3282 		olditem = *(long *)bptr;
3283 		/*LINTED*/
3284 		*(long *)bptr = item;
3285 		break;
3286 	case SHORT:
3287 	case INODE:
3288 		/*LINTED*/
3289 		olditem = (long)*(short *)bptr;
3290 		item &= 0177777L;
3291 		/*LINTED*/
3292 		*(short *)bptr = item;
3293 		break;
3294 	case CHAR:
3295 		olditem = (long)*bptr;
3296 		item &= 0377;
3297 		*bptr = lobyte(loword(item));
3298 		break;
3299 	default:
3300 		error++;
3301 		return;
3302 	}
3303 	if ((s_err = llseek(fd, (offset_t)(addr & fs->fs_bmask), 0)) == -1) {
3304 		error++;
3305 		printf("seek error : %" PRIx64 "\n", addr);
3306 		return;
3307 	}
3308 	if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
3309 		error++;
3310 		printf("write error : addr   = %" PRIx64 "\n", addr);
3311 		printf("            : s_err  = %lx\n", s_err);
3312 		printf("            : nbytes = %lx\n", nbytes);
3313 		return;
3314 	}
3315 	if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
3316 		index(base);
3317 		print(olditem, 8, -8, 0);
3318 		printf("\t=\t");
3319 		print(item, 8, -8, 0);
3320 		printf("\n");
3321 	} else {
3322 		if (objsz == DIRECTORY) {
3323 			addr = cur_dir;
3324 			fprnt('?', 'd');
3325 		} else {
3326 			addr = cur_ino;
3327 			objsz = INODE;
3328 			fprnt('?', 'i');
3329 		}
3330 	}
3331 }
3332 
3333 /*
3334  * getblk - check if the desired block is in the file system.
3335  *	Search the incore buffers to see if the block is already
3336  *	available. If successful, unlink the buffer control block
3337  *	from its position in the buffer list and re-insert it at
3338  *	the head of the list.  If failure, use the last buffer
3339  *	in the list for the desired block. Again, this control
3340  *	block is placed at the head of the list. This process
3341  *	will leave commonly requested blocks in the in-core buffers.
3342  *	Finally, a pointer to the buffer is returned.
3343  */
3344 static char *
3345 getblk(address)
3346 	u_offset_t		address;
3347 {
3348 
3349 	register struct lbuf	*bp;
3350 	long			s_err, nbytes;
3351 	unsigned long		block;
3352 
3353 	read_requests++;
3354 	block = lblkno(fs, address);
3355 	if (block >= fragstoblks(fs, fs->fs_size)) {
3356 		printf("cannot read block %lu\n", block);
3357 		error++;
3358 		return (0);
3359 	}
3360 	for (bp = bhdr.fwd; bp != &bhdr; bp = bp->fwd)
3361 		if (bp->valid && bp->blkno == block)
3362 			goto xit;
3363 	actual_disk_reads++;
3364 	bp = bhdr.back;
3365 	bp->blkno = block;
3366 	bp->valid = 0;
3367 	if ((s_err = llseek(fd, (offset_t)(address & fs->fs_bmask), 0)) == -1) {
3368 		error++;
3369 		printf("seek error : %" PRIx64 "\n", address);
3370 		return (0);
3371 	}
3372 	if ((nbytes = read(fd, bp->blkaddr, BLKSIZE)) != BLKSIZE) {
3373 		error++;
3374 		printf("read error : addr   = %" PRIx64 "\n", address);
3375 		printf("           : s_err  = %lx\n", s_err);
3376 		printf("           : nbytes = %lx\n", nbytes);
3377 		return (0);
3378 	}
3379 	bp->valid++;
3380 xit:	bp->back->fwd = bp->fwd;
3381 	bp->fwd->back = bp->back;
3382 	insert(bp);
3383 	return (bp->blkaddr);
3384 }
3385 
3386 /*
3387  * insert - place the designated buffer control block
3388  *	at the head of the linked list of buffers.
3389  */
3390 static void
3391 insert(bp)
3392 	register struct lbuf	*bp;
3393 {
3394 
3395 	bp->back = &bhdr;
3396 	bp->fwd = bhdr.fwd;
3397 	bhdr.fwd->back = bp;
3398 	bhdr.fwd = bp;
3399 }
3400 
3401 /*
3402  * err - called on interrupts.  Set the current address
3403  *	back to the last address stored in erraddr. Reset all
3404  *	appropriate flags.  A reset call is made to return
3405  *	to the main loop;
3406  */
3407 #ifdef sun
3408 /*ARGSUSED*/
3409 static void
3410 err(sig)
3411 	int	sig;
3412 #else
3413 err()
3414 #endif /* sun */
3415 {
3416 	freemem(filenames, nfiles);
3417 	nfiles = 0;
3418 	(void) signal(2, err);
3419 	addr = erraddr;
3420 	cur_ino = errino;
3421 	cur_inum = errinum;
3422 	cur_bytes = errcur_bytes;
3423 	error = 0;
3424 	c_count = 0;
3425 	printf("\n?\n");
3426 	(void) fseek(stdin, 0L, 2);
3427 	longjmp(env, 0);
3428 }
3429 
3430 /*
3431  * devcheck - check that the given mode represents a
3432  *	special device. The IFCHR bit is on for both
3433  *	character and block devices.
3434  */
3435 static int
3436 devcheck(md)
3437 	register	short md;
3438 {
3439 	if (override)
3440 		return (0);
3441 	switch (md & IFMT) {
3442 	case IFCHR:
3443 	case IFBLK:
3444 		return (0);
3445 	}
3446 
3447 	printf("not character or block device\n");
3448 	error++;
3449 	return (1);
3450 }
3451 
3452 /*
3453  * nullblk - return error if address is zero.  This is done
3454  *	to prevent block 0 from being used as an indirect block
3455  *	for a large file or as a data block for a small file.
3456  */
3457 static int
3458 nullblk(bn)
3459 	long		bn;
3460 {
3461 	if (bn != 0)
3462 		return (0);
3463 	printf("non existent block\n");
3464 	error++;
3465 	return (1);
3466 }
3467 
3468 /*
3469  * puta - put ascii characters into a buffer.  The string
3470  *	terminates with a quote or newline.  The leading quote,
3471  *	which is optional for directory names, was stripped off
3472  *	by the assignment case in the main loop.
3473  */
3474 static void
3475 puta()
3476 {
3477 	register char		*cptr, c;
3478 	register int		i;
3479 	char			*sbptr;
3480 	short			terror = 0;
3481 	long			maxchars, s_err, nbytes, temp;
3482 	u_offset_t		taddr = addr;
3483 	long			tcount = 0, item, olditem = 0;
3484 
3485 	if (wrtflag == O_RDONLY) {
3486 		printf("not opened for write '-w'\n");
3487 		error++;
3488 		return;
3489 	}
3490 	if ((sbptr = getblk(addr)) == 0)
3491 		return;
3492 	cptr = sbptr + blkoff(fs, addr);
3493 	if (objsz == DIRECTORY) {
3494 		if (acting_on_directory)
3495 			maxchars = stringsize - 1;
3496 		else
3497 			maxchars = LONG;
3498 	} else if (objsz == INODE)
3499 		maxchars = objsz - (addr - cur_ino);
3500 	else
3501 		maxchars = min(blocksize - cur_bytes, filesize - cur_bytes);
3502 	while ((c = getachar()) != '"') {
3503 		if (tcount >= maxchars) {
3504 			printf("string too long\n");
3505 			if (objsz == DIRECTORY)
3506 				addr = cur_dir;
3507 			else if (acting_on_inode || objsz == INODE)
3508 				addr = cur_ino;
3509 			else
3510 				addr = taddr;
3511 			erraddr = addr;
3512 			errcur_bytes = cur_bytes;
3513 			terror++;
3514 			break;
3515 		}
3516 		tcount++;
3517 		if (c == '\n') {
3518 			ungetachar(c);
3519 			break;
3520 		}
3521 		temp = (long)*cptr;
3522 		olditem <<= BITSPERCHAR;
3523 		olditem += temp & 0xff;
3524 		if (c == '\\') {
3525 			switch (c = getachar()) {
3526 			case 't':
3527 				*cptr++ = '\t';
3528 				break;
3529 			case 'n':
3530 				*cptr++ = '\n';
3531 				break;
3532 			case '0':
3533 				*cptr++ = '\0';
3534 				break;
3535 			default:
3536 				*cptr++ = c;
3537 				break;
3538 			}
3539 		}
3540 		else
3541 			*cptr++ = c;
3542 	}
3543 	if (objsz == DIRECTORY && acting_on_directory)
3544 		for (i = tcount; i <= maxchars; i++)
3545 			*cptr++ = '\0';
3546 	if ((s_err = llseek(fd, (offset_t)(addr & fs->fs_bmask), 0)) == -1) {
3547 		error++;
3548 		printf("seek error : %" PRIx64 "\n", addr);
3549 		return;
3550 	}
3551 	if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
3552 		error++;
3553 		printf("write error : addr   = %" PRIx64 "\n", addr);
3554 		printf("            : s_err  = %lx\n", s_err);
3555 		printf("            : nbytes = %lx\n", nbytes);
3556 		return;
3557 	}
3558 	if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
3559 		addr += tcount;
3560 		cur_bytes += tcount;
3561 		taddr = addr;
3562 		if (objsz != CHAR) {
3563 			addr &= ~(objsz - 1);
3564 			cur_bytes -= taddr - addr;
3565 		}
3566 		if (addr == taddr) {
3567 			addr -= objsz;
3568 			taddr = addr;
3569 		}
3570 		tcount = LONG - (taddr - addr);
3571 		index(base);
3572 		if ((cptr = getblk(addr)) == 0)
3573 			return;
3574 		cptr += blkoff(fs, addr);
3575 		switch (objsz) {
3576 		case LONG:
3577 			/*LINTED*/
3578 			item = *(long *)cptr;
3579 			if (tcount < LONG) {
3580 				olditem <<= tcount * BITSPERCHAR;
3581 				temp = 1;
3582 				for (i = 0; i < (tcount*BITSPERCHAR); i++)
3583 					temp <<= 1;
3584 				olditem += item & (temp - 1);
3585 			}
3586 			break;
3587 		case SHORT:
3588 			/*LINTED*/
3589 			item = (long)*(short *)cptr;
3590 			if (tcount < SHORT) {
3591 				olditem <<= tcount * BITSPERCHAR;
3592 				temp = 1;
3593 				for (i = 0; i < (tcount * BITSPERCHAR); i++)
3594 					temp <<= 1;
3595 				olditem += item & (temp - 1);
3596 			}
3597 			olditem &= 0177777L;
3598 			break;
3599 		case CHAR:
3600 			item = (long)*cptr;
3601 			olditem &= 0377;
3602 		}
3603 		print(olditem, 8, -8, 0);
3604 		printf("\t=\t");
3605 		print(item, 8, -8, 0);
3606 		printf("\n");
3607 	} else {
3608 		if (objsz == DIRECTORY) {
3609 			addr = cur_dir;
3610 			fprnt('?', 'd');
3611 		} else {
3612 			addr = cur_ino;
3613 			objsz = INODE;
3614 			fprnt('?', 'i');
3615 		}
3616 	}
3617 	if (terror)
3618 		error++;
3619 }
3620 
3621 /*
3622  * fprnt - print data.  'count' elements are printed where '*' will
3623  *	print an entire blocks worth or up to the eof, whichever
3624  *	occurs first.  An error will occur if crossing a block boundary
3625  *	is attempted since consecutive blocks don't usually have
3626  *	meaning.  Current print types:
3627  *		/		b   - print as bytes (base sensitive)
3628  *				c   - print as characters
3629  *				o O - print as octal shorts (longs)
3630  *				d D - print as decimal shorts (longs)
3631  *				x X - print as hexadecimal shorts (longs)
3632  *		?		c   - print as cylinder groups
3633  *				d   - print as directories
3634  *				i   - print as inodes
3635  *				s   - print as super blocks
3636  *				S   - print as shadow data
3637  */
3638 static void
3639 fprnt(style, po)
3640 	register char		style, po;
3641 {
3642 	register int		i;
3643 	register struct fs	*sb;
3644 	register struct cg	*cg;
3645 	register struct direct	*dirp;
3646 	register struct dinode	*ip;
3647 	int			tbase;
3648 	char			c, *cptr, *p;
3649 	long			tinode, tcount, temp;
3650 	u_offset_t		taddr;
3651 	short			offset, mode, end = 0, eof = 0, eof_flag;
3652 	unsigned short		*sptr;
3653 	unsigned long		*lptr;
3654 	offset_t		curoff, curioff;
3655 
3656 	laststyle = style;
3657 	lastpo = po;
3658 	should_print = 0;
3659 	if (count != 1) {
3660 		if (clear) {
3661 			count = 1;
3662 			star = 0;
3663 			clear = 0;
3664 		} else
3665 			clear = 1;
3666 	}
3667 	tcount = count;
3668 	offset = blkoff(fs, addr);
3669 
3670 	if (style == '/') {
3671 		if (type == NUMB)
3672 			eof_flag = 0;
3673 		else
3674 			eof_flag = 1;
3675 		switch (po) {
3676 
3677 		case 'c': /* print as characters */
3678 		case 'b': /* or bytes */
3679 			if ((cptr = getblk(addr)) == 0)
3680 				return;
3681 			cptr += offset;
3682 			objsz = CHAR;
3683 			tcount = check_addr(eof_flag, &end, &eof, 0);
3684 			if (tcount) {
3685 				for (i = 0; tcount--; i++) {
3686 					if (i % 16 == 0) {
3687 						if (i)
3688 							printf("\n");
3689 						index(base);
3690 					}
3691 					if (po == 'c') {
3692 						putf(*cptr++);
3693 						if ((i + 1) % 16)
3694 							printf("  ");
3695 					} else {
3696 						if ((i + 1) % 16 == 0)
3697 							print(*cptr++ & 0377L,
3698 								2, -2, 0);
3699 						else
3700 							print(*cptr++ & 0377L,
3701 								4, -2, 0);
3702 					}
3703 					addr += CHAR;
3704 					cur_bytes += CHAR;
3705 				}
3706 				printf("\n");
3707 			}
3708 			addr -= CHAR;
3709 			erraddr = addr;
3710 			cur_bytes -= CHAR;
3711 			errcur_bytes = cur_bytes;
3712 			if (eof) {
3713 				printf("end of file\n");
3714 				error++;
3715 			} else if (end) {
3716 				if (type == BLOCK)
3717 					printf("end of block\n");
3718 				else
3719 					printf("end of fragment\n");
3720 				error++;
3721 			}
3722 			return;
3723 
3724 		case 'o': /* print as octal shorts */
3725 			tbase = OCTAL;
3726 			goto otx;
3727 		case 'd': /* print as decimal shorts */
3728 			tbase = DECIMAL;
3729 			goto otx;
3730 		case 'x': /* print as hex shorts */
3731 			tbase = HEX;
3732 otx:
3733 			if ((cptr = getblk(addr)) == 0)
3734 				return;
3735 			taddr = addr;
3736 			addr &= ~(SHORT - 1);
3737 			cur_bytes -= taddr - addr;
3738 			cptr += blkoff(fs, addr);
3739 			/*LINTED*/
3740 			sptr = (unsigned short *)cptr;
3741 			objsz = SHORT;
3742 			tcount = check_addr(eof_flag, &end, &eof, 0);
3743 			if (tcount) {
3744 				for (i = 0; tcount--; i++) {
3745 					sptr = (unsigned short *)print_check(
3746 							/*LINTED*/
3747 							(unsigned long *)sptr,
3748 							&tcount, tbase, i);
3749 					switch (po) {
3750 					case 'o':
3751 						printf("%06o ", *sptr++);
3752 						break;
3753 					case 'd':
3754 						printf("%05d  ", *sptr++);
3755 						break;
3756 					case 'x':
3757 						printf("%04x   ", *sptr++);
3758 					}
3759 					addr += SHORT;
3760 					cur_bytes += SHORT;
3761 				}
3762 				printf("\n");
3763 			}
3764 			addr -= SHORT;
3765 			erraddr = addr;
3766 			cur_bytes -= SHORT;
3767 			errcur_bytes = cur_bytes;
3768 			if (eof) {
3769 				printf("end of file\n");
3770 				error++;
3771 			} else if (end) {
3772 				if (type == BLOCK)
3773 					printf("end of block\n");
3774 				else
3775 					printf("end of fragment\n");
3776 				error++;
3777 			}
3778 			return;
3779 
3780 		case 'O': /* print as octal longs */
3781 			tbase = OCTAL;
3782 			goto OTX;
3783 		case 'D': /* print as decimal longs */
3784 			tbase = DECIMAL;
3785 			goto OTX;
3786 		case 'X': /* print as hex longs */
3787 			tbase = HEX;
3788 OTX:
3789 			if ((cptr = getblk(addr)) == 0)
3790 				return;
3791 			taddr = addr;
3792 			addr &= ~(LONG - 1);
3793 			cur_bytes -= taddr - addr;
3794 			cptr += blkoff(fs, addr);
3795 			/*LINTED*/
3796 			lptr = (unsigned long *)cptr;
3797 			objsz = LONG;
3798 			tcount = check_addr(eof_flag, &end, &eof, 0);
3799 			if (tcount) {
3800 				for (i = 0; tcount--; i++) {
3801 					lptr = print_check(lptr, &tcount,
3802 								tbase, i);
3803 					switch (po) {
3804 					case 'O':
3805 						printf("%011lo    ", *lptr++);
3806 						break;
3807 					case 'D':
3808 						printf("%010lu     ", *lptr++);
3809 						break;
3810 					case 'X':
3811 						printf("%08lx       ", *lptr++);
3812 					}
3813 					addr += LONG;
3814 					cur_bytes += LONG;
3815 				}
3816 				printf("\n");
3817 			}
3818 			addr -= LONG;
3819 			erraddr = addr;
3820 			cur_bytes -= LONG;
3821 			errcur_bytes = cur_bytes;
3822 			if (eof) {
3823 				printf("end of file\n");
3824 				error++;
3825 			} else if (end) {
3826 				if (type == BLOCK)
3827 					printf("end of block\n");
3828 				else
3829 					printf("end of fragment\n");
3830 				error++;
3831 			}
3832 			return;
3833 
3834 		default:
3835 			error++;
3836 			printf("no such print option\n");
3837 			return;
3838 		}
3839 	} else
3840 		switch (po) {
3841 
3842 		case 'c': /* print as cylinder group */
3843 			if (type != NUMB)
3844 				if (cur_cgrp + count > fs->fs_ncg) {
3845 					tcount = fs->fs_ncg - cur_cgrp;
3846 					if (!star)
3847 						end++;
3848 				}
3849 			addr &= ~(LONG - 1);
3850 			for (/* void */; tcount--; /* void */) {
3851 				erraddr = addr;
3852 				errcur_bytes = cur_bytes;
3853 				if (type != NUMB) {
3854 					addr = cgtod(fs, cur_cgrp)
3855 						<< FRGSHIFT;
3856 					cur_cgrp++;
3857 				}
3858 				if ((cptr = getblk(addr)) == 0) {
3859 					if (cur_cgrp)
3860 						cur_cgrp--;
3861 					return;
3862 				}
3863 				cptr += blkoff(fs, addr);
3864 				/*LINTED*/
3865 				cg = (struct cg *)cptr;
3866 				if (type == NUMB) {
3867 					cur_cgrp = cg->cg_cgx + 1;
3868 					type = objsz = CGRP;
3869 					if (cur_cgrp + count - 1 > fs->fs_ncg) {
3870 						tcount = fs->fs_ncg - cur_cgrp;
3871 						if (!star)
3872 							end++;
3873 					}
3874 				}
3875 				if (! override && !cg_chkmagic(cg)) {
3876 					printf("invalid cylinder group ");
3877 					printf("magic word\n");
3878 					if (cur_cgrp)
3879 						cur_cgrp--;
3880 					error++;
3881 					return;
3882 				}
3883 				printcg(cg);
3884 				if (tcount)
3885 					printf("\n");
3886 			}
3887 			cur_cgrp--;
3888 			if (end) {
3889 				printf("end of cylinder groups\n");
3890 				error++;
3891 			}
3892 			return;
3893 
3894 		case 'd': /* print as directories */
3895 			if ((cptr = getblk(addr)) == 0)
3896 				return;
3897 			if (type == NUMB) {
3898 				if (fragoff(fs, addr)) {
3899 					printf("address must be at the ");
3900 					printf("beginning of a fragment\n");
3901 					error++;
3902 					return;
3903 				}
3904 				bod_addr = addr;
3905 				type = FRAGMENT;
3906 				dirslot = 0;
3907 				cur_bytes = 0;
3908 				blocksize = FRGSIZE;
3909 				filesize = FRGSIZE * 2;
3910 			}
3911 			cptr += offset;
3912 			objsz = DIRECTORY;
3913 			while (tcount-- && cur_bytes < filesize &&
3914 				cur_bytes < blocksize && !bcomp(addr)) {
3915 				/*LINTED*/
3916 				dirp = (struct direct *)cptr;
3917 				tinode = dirp->d_ino;
3918 				printf("i#: ");
3919 				if (tinode == 0)
3920 					printf("free\t");
3921 				else
3922 					print(tinode, 12, -8, 0);
3923 				printf("%s\n", &dirp->d_name[0]);
3924 				erraddr = addr;
3925 				errcur_bytes = cur_bytes;
3926 				addr += dirp->d_reclen;
3927 				cptr += dirp->d_reclen;
3928 				cur_bytes += dirp->d_reclen;
3929 				dirslot++;
3930 				stringsize = STRINGSIZE(dirp);
3931 			}
3932 			addr = erraddr;
3933 			cur_dir = addr;
3934 			cur_bytes = errcur_bytes;
3935 			dirslot--;
3936 			if (tcount >= 0 && !star) {
3937 				switch (type) {
3938 				case FRAGMENT:
3939 					printf("end of fragment\n");
3940 					break;
3941 				case BLOCK:
3942 					printf("end of block\n");
3943 					break;
3944 				default:
3945 					printf("end of directory\n");
3946 				}
3947 				error++;
3948 			} else
3949 				error = 0;
3950 			return;
3951 
3952 		case 'i': /* print as inodes */
3953 			/*LINTED*/
3954 			if ((ip = (struct dinode *)getblk(addr)) == 0)
3955 				return;
3956 			for (i = 1; i < fs->fs_ncg; i++)
3957 				if (addr < (cgimin(fs, i) << FRGSHIFT))
3958 					break;
3959 			i--;
3960 			offset /= INODE;
3961 			temp = (addr - (cgimin(fs, i) << FRGSHIFT)) >> FRGSHIFT;
3962 			temp = (i * fs->fs_ipg) + fragstoblks(fs, temp) *
3963 							INOPB(fs) + offset;
3964 			if (count + offset > INOPB(fs)) {
3965 				tcount = INOPB(fs) - offset;
3966 				if (!star)
3967 					end++;
3968 			}
3969 			objsz = INODE;
3970 			ip += offset;
3971 			for (i = 0; tcount--; ip++, temp++) {
3972 				if ((mode = icheck(addr)) == 0)
3973 					if (!override)
3974 						continue;
3975 				p = " ugtrwxrwxrwx";
3976 
3977 				switch (mode & IFMT) {
3978 				case IFDIR:
3979 					c = 'd';
3980 					break;
3981 				case IFCHR:
3982 					c = 'c';
3983 					break;
3984 				case IFBLK:
3985 					c = 'b';
3986 					break;
3987 				case IFREG:
3988 					c = '-';
3989 					break;
3990 				case IFLNK:
3991 					c = 'l';
3992 					break;
3993 				case IFSOCK:
3994 					c = 's';
3995 					break;
3996 				case IFSHAD:
3997 					c = 'S';
3998 					break;
3999 				case IFATTRDIR:
4000 					c = 'A';
4001 					break;
4002 				default:
4003 					c = '?';
4004 					if (!override)
4005 						goto empty;
4006 
4007 				}
4008 				printf("i#: ");
4009 				print(temp, 12, -8, 0);
4010 				printf("   md: ");
4011 				printf("%c", c);
4012 				for (mode = mode << 4; *++p; mode = mode << 1) {
4013 					if (mode & IFREG)
4014 						printf("%c", *p);
4015 					else
4016 						printf("-");
4017 				}
4018 				printf("  uid: ");
4019 				print(ip->di_uid, 8, -4, 0);
4020 				printf("      gid: ");
4021 				print(ip->di_gid, 8, -4, 0);
4022 				printf("\n");
4023 				printf("ln: ");
4024 				print((long)ip->di_nlink, 8, -4, 0);
4025 				printf("       bs: ");
4026 				print(ip->di_blocks, 12, -8, 0);
4027 				printf("   sz : ");
4028 				printf("c_flags : ");
4029 				print(ip->di_cflags, 12, -8, 0);
4030 #ifdef _LARGEFILE64_SOURCE
4031 				printll(ip->di_size, 20, -16, 0);
4032 #else /* !_LARGEFILE64_SOURCE */
4033 				print(ip->di_size, 12, -8, 0);
4034 #endif /* _LARGEFILE64_SOURCE */
4035 				if (ip->di_shadow) {
4036 					printf("   si: ");
4037 					print(ip->di_shadow, 12, -8, 0);
4038 				}
4039 				printf("\n");
4040 				if (ip->di_oeftflag) {
4041 					printf("ai: ");
4042 					print(ip->di_oeftflag, 12, -8, 0);
4043 					printf("\n");
4044 				}
4045 				printf("\n");
4046 				switch (ip->di_mode & IFMT) {
4047 				case IFBLK:
4048 				case IFCHR:
4049 					printf("maj: ");
4050 					print(major(ip->di_ordev), 4, -2, 0);
4051 					printf("  min: ");
4052 					print(minor(ip->di_ordev), 4, -2, 0);
4053 					printf("\n");
4054 					break;
4055 				default:
4056 					/*
4057 					 * only display blocks below the
4058 					 * current file size
4059 					 */
4060 					curoff = 0LL;
4061 					for (i = 0; i < NDADDR; ) {
4062 						if (ip->di_size <= curoff)
4063 							break;
4064 						printf("db#%x: ", i);
4065 						print(ip->di_db[i], 11, -8, 0);
4066 
4067 						if (++i % 4 == 0)
4068 							printf("\n");
4069 						else
4070 							printf("  ");
4071 						curoff += fs->fs_bsize;
4072 					}
4073 					if (i % 4)
4074 						printf("\n");
4075 
4076 					/*
4077 					 * curioff keeps track of the number
4078 					 * of bytes covered by each indirect
4079 					 * pointer in the inode, and is added
4080 					 * to curoff each time to get the
4081 					 * actual offset into the file.
4082 					 */
4083 					curioff = fs->fs_bsize *
4084 					    (fs->fs_bsize / sizeof (daddr_t));
4085 					for (i = 0; i < NIADDR; i++) {
4086 						if (ip->di_size <= curoff)
4087 							break;
4088 						printf("ib#%x: ", i);
4089 						print(ip->di_ib[i], 11, -8, 0);
4090 						printf("  ");
4091 						curoff += curioff;
4092 						curioff *= (fs->fs_bsize /
4093 						    sizeof (daddr_t));
4094 					}
4095 					if (i)
4096 						printf("\n");
4097 					break;
4098 				}
4099 				if (count == 1) {
4100 					time_t t;
4101 
4102 					t = ip->di_atime;
4103 					printf("\taccessed: %s", ctime(&t));
4104 					t = ip->di_mtime;
4105 					printf("\tmodified: %s", ctime(&t));
4106 					t = ip->di_ctime;
4107 					printf("\tcreated : %s", ctime(&t));
4108 				}
4109 				if (tcount)
4110 					printf("\n");
4111 empty:
4112 				if (c == '?' && !override) {
4113 					printf("i#: ");
4114 					print(temp, 12, -8, 0);
4115 					printf("  is unallocated\n");
4116 					if (count != 1)
4117 						printf("\n");
4118 				}
4119 				cur_ino = erraddr = addr;
4120 				errcur_bytes = cur_bytes;
4121 				cur_inum++;
4122 				addr = addr + INODE;
4123 			}
4124 			addr = erraddr;
4125 			cur_bytes = errcur_bytes;
4126 			cur_inum--;
4127 			if (end) {
4128 				printf("end of block\n");
4129 				error++;
4130 			}
4131 			return;
4132 
4133 		case 's': /* print as super block */
4134 			if (cur_cgrp == -1) {
4135 				addr = SBLOCK * DEV_BSIZE;
4136 				type = NUMB;
4137 			}
4138 			addr &= ~(LONG - 1);
4139 			if (type != NUMB)
4140 				if (cur_cgrp + count > fs->fs_ncg) {
4141 					tcount = fs->fs_ncg - cur_cgrp;
4142 					if (!star)
4143 						end++;
4144 				}
4145 			for (/* void */; tcount--; /* void */) {
4146 				erraddr = addr;
4147 				cur_bytes = errcur_bytes;
4148 				if (type != NUMB) {
4149 					addr = cgsblock(fs, cur_cgrp)
4150 							<< FRGSHIFT;
4151 					cur_cgrp++;
4152 				}
4153 				if ((cptr = getblk(addr)) == 0) {
4154 					if (cur_cgrp)
4155 						cur_cgrp--;
4156 					return;
4157 				}
4158 				cptr += blkoff(fs, addr);
4159 				/*LINTED*/
4160 				sb = (struct fs *)cptr;
4161 				if (type == NUMB) {
4162 					for (i = 0; i < fs->fs_ncg; i++)
4163 						if (addr == cgsblock(fs, i) <<
4164 								FRGSHIFT)
4165 							break;
4166 					if (i == fs->fs_ncg)
4167 						cur_cgrp = 0;
4168 					else
4169 						cur_cgrp = i + 1;
4170 					type = objsz = SB;
4171 					if (cur_cgrp + count - 1 > fs->fs_ncg) {
4172 						tcount = fs->fs_ncg - cur_cgrp;
4173 						if (!star)
4174 							end++;
4175 					}
4176 				}
4177 				if ((sb->fs_magic != FS_MAGIC) &&
4178 				    (sb->fs_magic != MTB_UFS_MAGIC)) {
4179 					cur_cgrp = 0;
4180 					if (!override) {
4181 						printf("invalid super block ");
4182 						printf("magic word\n");
4183 						cur_cgrp--;
4184 						error++;
4185 						return;
4186 					}
4187 				}
4188 				if (sb->fs_magic == MTB_UFS_MAGIC &&
4189 				    (sb->fs_version > MTB_UFS_VERSION_1 ||
4190 				    sb->fs_version < MTB_UFS_VERSION_MIN)) {
4191 					cur_cgrp = 0;
4192 					if (!override) {
4193 						printf("invalid super block ");
4194 						printf("magic word\n");
4195 						cur_cgrp--;
4196 						error++;
4197 						return;
4198 					}
4199 				}
4200 				if (cur_cgrp == 0)
4201 					printf("\tsuper block:\n");
4202 				else {
4203 					printf("\tsuper block in cylinder ");
4204 					printf("group ");
4205 					print(cur_cgrp - 1, 0, 0, 0);
4206 					printf(":\n");
4207 				}
4208 				printsb(sb);
4209 				if (tcount)
4210 					printf("\n");
4211 			}
4212 			cur_cgrp--;
4213 			if (end) {
4214 				printf("end of super blocks\n");
4215 				error++;
4216 			}
4217 			return;
4218 
4219 		case 'S': /* print as shadow data */
4220 			if (type == NUMB) {
4221 				type = FRAGMENT;
4222 				cur_shad = 0;
4223 				cur_bytes = fragoff(fs, addr);
4224 				bod_addr = addr - cur_bytes;
4225 				/* no more than two fragments */
4226 				filesize = fragroundup(fs,
4227 				    bod_addr + FRGSIZE + 1);
4228 			}
4229 			objsz = SHADOW_DATA;
4230 			while (tcount-- &&
4231 			    (cur_bytes + SHADOW_DATA) <= filesize &&
4232 			    (type != SHADOW_DATA ||
4233 			    (cur_bytes + SHADOW_DATA)) <= blocksize) {
4234 				/*LINTED*/
4235 				struct ufs_fsd fsd;
4236 				long tcur_bytes;
4237 
4238 				taddr = addr;
4239 				tcur_bytes = cur_bytes;
4240 				index(base);
4241 				getshadowdata((long *)&fsd, LONG + LONG);
4242 				printf("  type: ");
4243 				print((long)fsd.fsd_type, 8, -8, 0);
4244 				printf("  size: ");
4245 				print((long)fsd.fsd_size, 8, -8, 0);
4246 				tbase = fsd.fsd_size - LONG - LONG;
4247 				if (tbase > 256)
4248 					tbase = 256;
4249 				for (i = 0; i < tbase; i++) {
4250 					if (i % LONG == 0) {
4251 						if (i % 16 == 0) {
4252 							printf("\n");
4253 							index(base);
4254 						} else
4255 							printf("  ");
4256 						getshadowdata(&temp, LONG);
4257 						p = (char *)&temp;
4258 					} else
4259 						printf(" ");
4260 					printf("%02x", (int)(*p++ & 0377L));
4261 				}
4262 				printf("\n");
4263 				addr = taddr;
4264 				cur_bytes = tcur_bytes;
4265 				erraddr = addr;
4266 				errcur_bytes = cur_bytes;
4267 				addr += FSD_RECSZ((&fsd), fsd.fsd_size);
4268 				cur_bytes += FSD_RECSZ((&fsd), fsd.fsd_size);
4269 				cur_shad++;
4270 				syncshadowscan(0);
4271 			}
4272 			addr = erraddr;
4273 			cur_bytes = errcur_bytes;
4274 			cur_shad--;
4275 			if (tcount >= 0 && !star) {
4276 				switch (type) {
4277 				case FRAGMENT:
4278 					printf("end of fragment\n");
4279 					break;
4280 				default:
4281 					printf("end of shadow data\n");
4282 				}
4283 				error++;
4284 			} else
4285 				error = 0;
4286 			return;
4287 		default:
4288 			error++;
4289 			printf("no such print option\n");
4290 			return;
4291 		}
4292 }
4293 
4294 /*
4295  * valid_addr - call check_addr to validate the current address.
4296  */
4297 static int
4298 valid_addr()
4299 {
4300 	short	end = 0, eof = 0;
4301 	long	tcount = count;
4302 
4303 	if (!trapped)
4304 		return (1);
4305 	if (cur_bytes < 0) {
4306 		cur_bytes = 0;
4307 		if (blocksize > filesize) {
4308 			printf("beginning of file\n");
4309 		} else {
4310 			if (type == BLOCK)
4311 				printf("beginning of block\n");
4312 			else
4313 				printf("beginning of fragment\n");
4314 		}
4315 		error++;
4316 		return (0);
4317 	}
4318 	count = 1;
4319 	(void) check_addr(1, &end, &eof, (filesize < blocksize));
4320 	count = tcount;
4321 	if (eof) {
4322 		printf("end of file\n");
4323 		error++;
4324 		return (0);
4325 	}
4326 	if (end == 2) {
4327 		if (erraddr > addr) {
4328 			if (type == BLOCK)
4329 				printf("beginning of block\n");
4330 			else
4331 				printf("beginning of fragment\n");
4332 			error++;
4333 			return (0);
4334 		}
4335 	}
4336 	if (end) {
4337 		if (type == BLOCK)
4338 			printf("end of block\n");
4339 		else
4340 			printf("end of fragment\n");
4341 		error++;
4342 		return (0);
4343 	}
4344 	return (1);
4345 }
4346 
4347 /*
4348  * check_addr - check if the address crosses the end of block or
4349  *	end of file.  Return the proper count.
4350  */
4351 static int
4352 check_addr(eof_flag, end, eof, keep_on)
4353 	short	eof_flag, *end, *eof, keep_on;
4354 {
4355 	long	temp, tcount = count, tcur_bytes = cur_bytes;
4356 	u_offset_t	taddr = addr;
4357 
4358 	if (bcomp(addr + count * objsz - 1) ||
4359 	    (keep_on && taddr < (bmap(cur_block) << FRGSHIFT))) {
4360 		error = 0;
4361 		addr = taddr;
4362 		cur_bytes = tcur_bytes;
4363 		if (keep_on) {
4364 			if (addr < erraddr) {
4365 				if (cur_bytes < 0) {
4366 					(*end) = 2;
4367 					return (0);	/* Value ignored */
4368 				}
4369 				temp = cur_block - lblkno(fs, cur_bytes);
4370 				cur_block -= temp;
4371 				if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
4372 					cur_block += temp;
4373 					return (0);	/* Value ignored */
4374 				}
4375 				temp = tcur_bytes - cur_bytes;
4376 				addr += temp;
4377 				cur_bytes += temp;
4378 				return (0);	/* Value ignored */
4379 			} else {
4380 				if (cur_bytes >= filesize) {
4381 					(*eof)++;
4382 					return (0);	/* Value ignored */
4383 				}
4384 				temp = lblkno(fs, cur_bytes) - cur_block;
4385 				cur_block += temp;
4386 				if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
4387 					cur_block -= temp;
4388 					return (0);	/* Value ignored */
4389 				}
4390 				temp = tcur_bytes - cur_bytes;
4391 				addr += temp;
4392 				cur_bytes += temp;
4393 				return (0);	/* Value ignored */
4394 			}
4395 		}
4396 		tcount = (blkroundup(fs, addr+1)-addr) / objsz;
4397 		if (!star)
4398 			(*end) = 2;
4399 	}
4400 	addr = taddr;
4401 	cur_bytes = tcur_bytes;
4402 	if (eof_flag) {
4403 		if (blocksize > filesize) {
4404 			if (cur_bytes >= filesize) {
4405 				tcount = 0;
4406 				(*eof)++;
4407 			} else if (tcount > (filesize - cur_bytes) / objsz) {
4408 				tcount = (filesize - cur_bytes) / objsz;
4409 				if (!star || tcount == 0)
4410 					(*eof)++;
4411 			}
4412 		} else {
4413 			if (cur_bytes >= blocksize) {
4414 				tcount = 0;
4415 				(*end)++;
4416 			} else if (tcount > (blocksize - cur_bytes) / objsz) {
4417 				tcount = (blocksize - cur_bytes) / objsz;
4418 				if (!star || tcount == 0)
4419 					(*end)++;
4420 			}
4421 		}
4422 	}
4423 	return (tcount);
4424 }
4425 
4426 /*
4427  * print_check - check if the index needs to be printed and delete
4428  *	rows of zeros from the output.
4429  */
4430 unsigned long *
4431 print_check(lptr, tcount, tbase, i)
4432 	unsigned long	*lptr;
4433 	long		*tcount;
4434 	short		tbase;
4435 	register int	i;
4436 {
4437 	register int	j, k, temp = BYTESPERLINE / objsz;
4438 	short		first_time = 0;
4439 	unsigned long	*tlptr;
4440 	unsigned short	*tsptr, *sptr;
4441 
4442 	sptr = (unsigned short *)lptr;
4443 	if (i == 0)
4444 		first_time = 1;
4445 	if (i % temp == 0) {
4446 		if (*tcount >= temp - 1) {
4447 			if (objsz == SHORT)
4448 				tsptr = sptr;
4449 			else
4450 				tlptr = lptr;
4451 			k = *tcount - 1;
4452 			for (j = i; k--; j++)
4453 				if (objsz == SHORT) {
4454 					if (*tsptr++ != 0)
4455 						break;
4456 				} else {
4457 					if (*tlptr++ != 0)
4458 						break;
4459 				}
4460 			if (j > (i + temp - 1)) {
4461 				j = (j - i) / temp;
4462 				while (j-- > 0) {
4463 					if (objsz == SHORT)
4464 						sptr += temp;
4465 					else
4466 						lptr += temp;
4467 					*tcount -= temp;
4468 					i += temp;
4469 					addr += BYTESPERLINE;
4470 					cur_bytes += BYTESPERLINE;
4471 				}
4472 				if (first_time)
4473 					printf("*");
4474 				else
4475 					printf("\n*");
4476 			}
4477 			if (i)
4478 				printf("\n");
4479 			index(tbase);
4480 		} else {
4481 			if (i)
4482 				printf("\n");
4483 			index(tbase);
4484 		}
4485 	}
4486 	if (objsz == SHORT)
4487 		/*LINTED*/
4488 		return ((unsigned long *)sptr);
4489 	else
4490 		return (lptr);
4491 }
4492 
4493 /*
4494  * index - print a byte index for the printout in base b
4495  *	with leading zeros.
4496  */
4497 static void
4498 index(b)
4499 	int	b;
4500 {
4501 	int	tbase = base;
4502 
4503 	base = b;
4504 	print(addr, 8, 8, 1);
4505 	printf(":\t");
4506 	base = tbase;
4507 }
4508 
4509 /*
4510  * print - print out the value to digits places with/without
4511  *	leading zeros and right/left justified in the current base.
4512  */
4513 static void
4514 #ifdef _LARGEFILE64_SOURCE
4515 printll(u_offset_t value, int fieldsz, int digits, int lead)
4516 #else /* !_LARGEFILE64_SOURCE */
4517 print(long value, int fieldsz, int digits, int lead)
4518 #endif /* _LARGEFILE64_SOURCE */
4519 {
4520 	register int	i, left = 0;
4521 	char		mode = BASE[base - OCTAL];
4522 	char		*string = &scratch[0];
4523 
4524 	if (digits < 0) {
4525 		left = 1;
4526 		digits *= -1;
4527 	}
4528 	if (base != HEX)
4529 		if (digits)
4530 			digits = digits + (digits - 1)/((base >> 1) - 1) + 1;
4531 		else
4532 			digits = 1;
4533 	if (lead) {
4534 		if (left)
4535 			(void) sprintf(string, "%%%c%d%d.%d"
4536 #ifdef _LARGEFILE64_SOURCE
4537 				"ll"
4538 #endif /* _LARGEFILE64_SOURCE */
4539 				"%c", '-', 0, digits, lead, mode);
4540 		else
4541 			(void) sprintf(string, "%%%d%d.%d"
4542 #ifdef _LARGEFILE64_SOURCE
4543 				"ll"
4544 #endif /* _LARGEFILE64_SOURCE */
4545 				"%c", 0, digits, lead, mode);
4546 	} else {
4547 		if (left)
4548 			(void) sprintf(string, "%%%c%d"
4549 #ifdef _LARGEFILE64_SOURCE
4550 				"ll"
4551 #endif /* _LARGEFILE64_SOURCE */
4552 				"%c", '-', digits, mode);
4553 		else
4554 			(void) sprintf(string, "%%%d"
4555 #ifdef _LARGEFILE64_SOURCE
4556 				"ll"
4557 #endif /* _LARGEFILE64_SOURCE */
4558 				"%c", digits, mode);
4559 	}
4560 	printf(string, value);
4561 	for (i = 0; i < fieldsz - digits; i++)
4562 		printf(" ");
4563 }
4564 
4565 /*
4566  * Print out the contents of a superblock.
4567  */
4568 static void
4569 printsb(fs)
4570 	struct fs *fs;
4571 {
4572 	int c, i, j, k, size;
4573 	caddr_t sip;
4574 	time_t t;
4575 
4576 	t = fs->fs_time;
4577 #ifdef FS_42POSTBLFMT
4578 	if (fs->fs_postblformat == FS_42POSTBLFMT)
4579 		fs->fs_nrpos = 8;
4580 	printf("magic\t%lx\tformat\t%s\ttime\t%s", fs->fs_magic,
4581 	    fs->fs_postblformat == FS_42POSTBLFMT ? "static" : "dynamic",
4582 	    ctime(&t));
4583 #else
4584 	printf("magic\t%x\ttime\t%s",
4585 	    fs->fs_magic, ctime(&t));
4586 #endif
4587 	if (fs->fs_magic == MTB_UFS_MAGIC)
4588 		printf("version\t%x\n", fs->fs_version);
4589 	printf("nbfree\t%ld\tndir\t%ld\tnifree\t%ld\tnffree\t%ld\n",
4590 	    fs->fs_cstotal.cs_nbfree, fs->fs_cstotal.cs_ndir,
4591 	    fs->fs_cstotal.cs_nifree, fs->fs_cstotal.cs_nffree);
4592 	printf("ncg\t%ld\tncyl\t%ld\tsize\t%ld\tblocks\t%ld\n",
4593 	    fs->fs_ncg, fs->fs_ncyl, fs->fs_size, fs->fs_dsize);
4594 	printf("bsize\t%ld\tshift\t%ld\tmask\t0x%08lx\n",
4595 	    fs->fs_bsize, fs->fs_bshift, fs->fs_bmask);
4596 	printf("fsize\t%ld\tshift\t%ld\tmask\t0x%08lx\n",
4597 	    fs->fs_fsize, fs->fs_fshift, fs->fs_fmask);
4598 	printf("frag\t%ld\tshift\t%ld\tfsbtodb\t%ld\n",
4599 	    fs->fs_frag, fs->fs_fragshift, fs->fs_fsbtodb);
4600 	printf("cpg\t%ld\tbpg\t%ld\tfpg\t%ld\tipg\t%ld\n",
4601 	    fs->fs_cpg, fs->fs_fpg / fs->fs_frag, fs->fs_fpg, fs->fs_ipg);
4602 	printf("minfree\t%ld%%\toptim\t%s\tmaxcontig %ld\tmaxbpg\t%ld\n",
4603 	    fs->fs_minfree, fs->fs_optim == FS_OPTSPACE ? "space" : "time",
4604 	    fs->fs_maxcontig, fs->fs_maxbpg);
4605 #ifdef FS_42POSTBLFMT
4606 #ifdef sun
4607 	printf("rotdelay %ldms\tfs_id[0] 0x%lx\tfs_id[1] 0x%lx\trps\t%ld\n",
4608 	    fs->fs_rotdelay, fs->fs_id[0], fs->fs_id[1], fs->fs_rps);
4609 #else
4610 	printf("rotdelay %dms\theadswitch %dus\ttrackseek %dus\trps\t%d\n",
4611 	    fs->fs_rotdelay, fs->fs_headswitch, fs->fs_trkseek, fs->fs_rps);
4612 #endif /* sun */
4613 	printf("ntrak\t%ld\tnsect\t%ld\tnpsect\t%ld\tspc\t%ld\n",
4614 	    fs->fs_ntrak, fs->fs_nsect, fs->fs_npsect, fs->fs_spc);
4615 	printf("trackskew %ld\n", fs->fs_trackskew);
4616 #else
4617 	printf("rotdelay %ldms\trps\t%ld\n",
4618 	    fs->fs_rotdelay, fs->fs_rps);
4619 	printf("ntrak\t%ld\tnsect\t%ld\tspc\t%ld\n",
4620 	    fs->fs_ntrak, fs->fs_nsect, fs->fs_spc);
4621 #endif
4622 	printf("si %ld\n", fs->fs_si);
4623 	printf("nindir\t%ld\tinopb\t%ld\tnspf\t%ld\n",
4624 	    fs->fs_nindir, fs->fs_inopb, fs->fs_nspf);
4625 	printf("sblkno\t%ld\tcblkno\t%ld\tiblkno\t%ld\tdblkno\t%ld\n",
4626 	    fs->fs_sblkno, fs->fs_cblkno, fs->fs_iblkno, fs->fs_dblkno);
4627 	printf("sbsize\t%ld\tcgsize\t%ld\tcgoffset %ld\tcgmask\t0x%08lx\n",
4628 	    fs->fs_sbsize, fs->fs_cgsize, fs->fs_cgoffset, fs->fs_cgmask);
4629 	printf("csaddr\t%ld\tcssize\t%ld\tshift\t%ld\tmask\t0x%08lx\n",
4630 	    fs->fs_csaddr, fs->fs_cssize, fs->fs_csshift, fs->fs_csmask);
4631 	printf("cgrotor\t%ld\tfmod\t%d\tronly\t%d\n",
4632 	    fs->fs_cgrotor, fs->fs_fmod, fs->fs_ronly);
4633 #ifdef FS_42POSTBLFMT
4634 	if (fs->fs_cpc != 0)
4635 		printf("blocks available in each of %ld rotational positions",
4636 			fs->fs_nrpos);
4637 	else
4638 		printf("insufficient space to maintain rotational tables\n");
4639 #endif
4640 	for (c = 0; c < fs->fs_cpc; c++) {
4641 		printf("\ncylinder number %d:", c);
4642 #ifdef FS_42POSTBLFMT
4643 		for (i = 0; i < fs->fs_nrpos; i++) {
4644 			/*LINTED*/
4645 			if (fs_postbl(fs, c)[i] == -1)
4646 				continue;
4647 			printf("\n   position %d:\t", i);
4648 			/*LINTED*/
4649 			for (j = fs_postbl(fs, c)[i], k = 1; /* void */;
4650 						j += fs_rotbl(fs)[j], k++) {
4651 				printf("%5d", j);
4652 				if (k % 12 == 0)
4653 					printf("\n\t\t");
4654 				if (fs_rotbl(fs)[j] == 0)
4655 					break;
4656 			}
4657 		}
4658 #else
4659 		for (i = 0; i < NRPOS; i++) {
4660 			if (fs->fs_postbl[c][i] == -1)
4661 				continue;
4662 			printf("\n   position %d:\t", i);
4663 			for (j = fs->fs_postbl[c][i], k = 1; /* void */;
4664 						j += fs->fs_rotbl[j], k++) {
4665 				printf("%5d", j);
4666 				if (k % 12 == 0)
4667 					printf("\n\t\t");
4668 				if (fs->fs_rotbl[j] == 0)
4669 					break;
4670 			}
4671 		}
4672 #endif
4673 	}
4674 	printf("\ncs[].cs_(nbfree, ndir, nifree, nffree):");
4675 	sip = calloc(1, fs->fs_cssize);
4676 	fs->fs_u.fs_csp = (struct csum *)sip;
4677 	for (i = 0, j = 0; i < fs->fs_cssize; i += fs->fs_bsize, j++) {
4678 		size = fs->fs_cssize - i < fs->fs_bsize ?
4679 		    fs->fs_cssize - i : fs->fs_bsize;
4680 		(void) llseek(fd,
4681 			(offset_t)fsbtodb(fs, (fs->fs_csaddr + j * fs->fs_frag))
4682 				* fs->fs_fsize / fsbtodb(fs, 1), 0);
4683 		if (read(fd, sip, size) != size) {
4684 			free(fs->fs_u.fs_csp);
4685 			return;
4686 		}
4687 		sip += size;
4688 	}
4689 	for (i = 0; i < fs->fs_ncg; i++) {
4690 		struct csum *cs = &fs->fs_cs(fs, i);
4691 		if (i % 4 == 0)
4692 			printf("\n     ");
4693 		printf("%d:(%ld,%ld,%ld,%ld) ", i, cs->cs_nbfree, cs->cs_ndir,
4694 						cs->cs_nifree, cs->cs_nffree);
4695 	}
4696 	free(fs->fs_u.fs_csp);
4697 	printf("\n");
4698 	if (fs->fs_ncyl % fs->fs_cpg) {
4699 		printf("cylinders in last group %d\n",
4700 		    i = fs->fs_ncyl % fs->fs_cpg);
4701 		printf("blocks in last group %ld\n",
4702 		    i * fs->fs_spc / NSPB(fs));
4703 	}
4704 }
4705 
4706 /*
4707  * Print out the contents of a cylinder group.
4708  */
4709 static void
4710 printcg(cg)
4711 	struct cg *cg;
4712 {
4713 	int i, j;
4714 	time_t t;
4715 
4716 	printf("\ncg %ld:\n", cg->cg_cgx);
4717 	t = cg->cg_time;
4718 #ifdef FS_42POSTBLFMT
4719 	printf("magic\t%lx\ttell\t%llx\ttime\t%s",
4720 	    fs->fs_postblformat == FS_42POSTBLFMT ?
4721 	    ((struct ocg *)cg)->cg_magic : cg->cg_magic,
4722 	    fsbtodb(fs, cgtod(fs, cg->cg_cgx)) * fs->fs_fsize / fsbtodb(fs, 1),
4723 	    ctime(&t));
4724 #else
4725 	printf("magic\t%x\ttell\t%llx\ttime\t%s",
4726 	    cg->cg_magic,
4727 	    fsbtodb(fs, cgtod(fs, cg->cg_cgx)) * fs->fs_fsize / fsbtodb(fs, 1),
4728 	    ctime(&t));
4729 #endif
4730 	printf("cgx\t%ld\tncyl\t%d\tniblk\t%d\tndblk\t%ld\n",
4731 	    cg->cg_cgx, cg->cg_ncyl, cg->cg_niblk, cg->cg_ndblk);
4732 	printf("nbfree\t%ld\tndir\t%ld\tnifree\t%ld\tnffree\t%ld\n",
4733 	    cg->cg_cs.cs_nbfree, cg->cg_cs.cs_ndir,
4734 	    cg->cg_cs.cs_nifree, cg->cg_cs.cs_nffree);
4735 	printf("rotor\t%ld\tirotor\t%ld\tfrotor\t%ld\nfrsum",
4736 	    cg->cg_rotor, cg->cg_irotor, cg->cg_frotor);
4737 	for (i = 1, j = 0; i < fs->fs_frag; i++) {
4738 		printf("\t%ld", cg->cg_frsum[i]);
4739 		j += i * cg->cg_frsum[i];
4740 	}
4741 	printf("\nsum of frsum: %d\niused:\t", j);
4742 	pbits((unsigned char *)cg_inosused(cg), fs->fs_ipg);
4743 	printf("free:\t");
4744 	pbits(cg_blksfree(cg), fs->fs_fpg);
4745 	printf("b:\n");
4746 	for (i = 0; i < fs->fs_cpg; i++) {
4747 		/*LINTED*/
4748 		if (cg_blktot(cg)[i] == 0)
4749 			continue;
4750 		/*LINTED*/
4751 		printf("   c%d:\t(%ld)\t", i, cg_blktot(cg)[i]);
4752 #ifdef FS_42POSTBLFMT
4753 		for (j = 0; j < fs->fs_nrpos; j++) {
4754 			if (fs->fs_cpc == 0 ||
4755 				/*LINTED*/
4756 			    fs_postbl(fs, i % fs->fs_cpc)[j] == -1)
4757 				continue;
4758 			/*LINTED*/
4759 			printf(" %d", cg_blks(fs, cg, i)[j]);
4760 		}
4761 #else
4762 		for (j = 0; j < NRPOS; j++) {
4763 			if (fs->fs_cpc == 0 ||
4764 			    fs->fs_postbl[i % fs->fs_cpc][j] == -1)
4765 				continue;
4766 			printf(" %d", cg->cg_b[i][j]);
4767 		}
4768 #endif
4769 		printf("\n");
4770 	}
4771 }
4772 
4773 /*
4774  * Print out the contents of a bit array.
4775  */
4776 static void
4777 pbits(cp, max)
4778 	register unsigned char *cp;
4779 	int max;
4780 {
4781 	register int i;
4782 	int count = 0, j;
4783 
4784 	for (i = 0; i < max; i++)
4785 		if (isset(cp, i)) {
4786 			if (count)
4787 				printf(",%s", count % 6 ? " " : "\n\t");
4788 			count++;
4789 			printf("%d", i);
4790 			j = i;
4791 			while ((i+1) < max && isset(cp, i+1))
4792 				i++;
4793 			if (i != j)
4794 				printf("-%d", i);
4795 		}
4796 	printf("\n");
4797 }
4798 
4799 /*
4800  * bcomp - used to check for block over/under flows when stepping through
4801  *	a file system.
4802  */
4803 static int
4804 bcomp(addr)
4805 	u_offset_t	addr;
4806 {
4807 	if (override)
4808 		return (0);
4809 
4810 	if (lblkno(fs, addr) == (bhdr.fwd)->blkno)
4811 		return (0);
4812 	error++;
4813 	return (1);
4814 }
4815 
4816 /*
4817  * bmap - maps the logical block number of a file into
4818  *	the corresponding physical block on the file
4819  *	system.
4820  */
4821 static long
4822 bmap(bn)
4823 	long			bn;
4824 {
4825 	register int		j;
4826 	register struct dinode	*ip;
4827 	int			sh;
4828 	long			nb;
4829 	register char		*cptr;
4830 
4831 	if ((cptr = getblk(cur_ino)) == 0)
4832 		return (0);
4833 
4834 	cptr += blkoff(fs, cur_ino);
4835 
4836 	/*LINTED*/
4837 	ip = (struct dinode *)cptr;
4838 
4839 	if (bn < NDADDR) {
4840 		nb = ip->di_db[bn];
4841 		return (nullblk(nb) ? 0L : nb);
4842 	}
4843 
4844 	sh = 1;
4845 	bn -= NDADDR;
4846 	for (j = NIADDR; j > 0; j--) {
4847 		sh *= NINDIR(fs);
4848 		if (bn < sh)
4849 			break;
4850 		bn -= sh;
4851 	}
4852 	if (j == 0) {
4853 		printf("file too big\n");
4854 		error++;
4855 		return (0L);
4856 	}
4857 	addr = (uintptr_t)&ip->di_ib[NIADDR - j];
4858 	nb = get(LONG);
4859 	if (nb == 0)
4860 		return (0L);
4861 	for (; j <= NIADDR; j++) {
4862 		sh /= NINDIR(fs);
4863 		addr = (nb << FRGSHIFT) + ((bn / sh) % NINDIR(fs)) * LONG;
4864 		if (nullblk(nb = get(LONG)))
4865 			return (0L);
4866 	}
4867 	return (nb);
4868 }
4869 
4870 #if defined(OLD_FSDB_COMPATIBILITY)
4871 
4872 /*
4873  * The following are "tacked on" to support the old fsdb functionality
4874  * of clearing an inode. (All together now...) "It's better to use clri".
4875  */
4876 
4877 #define	ISIZE	(sizeof (struct dinode))
4878 #define	NI	(MAXBSIZE/ISIZE)
4879 
4880 
4881 static struct	dinode	di_buf[NI];
4882 
4883 static union {
4884 	char		dummy[SBSIZE];
4885 	struct fs	sblk;
4886 } sb_un;
4887 
4888 #define	sblock sb_un.sblk
4889 
4890 static void
4891 old_fsdb(inum, special)
4892 	int	inum;
4893 	char *special;
4894 {
4895 	int		f;	/* File descriptor for "special" */
4896 	int		j;
4897 	int		status = 0;
4898 	u_offset_t	off;
4899 	long		gen;
4900 	time_t		t;
4901 
4902 	f = open(special, 2);
4903 	if (f < 0) {
4904 		perror("open");
4905 		printf("cannot open %s\n", special);
4906 		exit(31+4);
4907 	}
4908 	(void) llseek(f, (offset_t)SBLOCK * DEV_BSIZE, 0);
4909 	if (read(f, &sblock, SBSIZE) != SBSIZE) {
4910 		printf("cannot read %s\n", special);
4911 		exit(31+4);
4912 	}
4913 	if (sblock.fs_magic != FS_MAGIC) {
4914 		printf("bad super block magic number\n");
4915 		exit(31+4);
4916 	}
4917 	if (inum == 0) {
4918 		printf("%d: is zero\n", inum);
4919 		exit(31+1);
4920 	}
4921 	off = (u_offset_t)fsbtodb(&sblock, itod(&sblock, inum)) * DEV_BSIZE;
4922 	(void) llseek(f, off, 0);
4923 	if (read(f, (char *)di_buf, sblock.fs_bsize) != sblock.fs_bsize) {
4924 		printf("%s: read error\n", special);
4925 		status = 1;
4926 	}
4927 	if (status)
4928 		exit(31+status);
4929 
4930 	/*
4931 	 * Update the time in superblock, so fsck will check this filesystem.
4932 	 */
4933 	(void) llseek(f, (offset_t)(SBLOCK * DEV_BSIZE), 0);
4934 	(void) time(&t);
4935 	sblock.fs_time = (time32_t)t;
4936 	if (write(f, &sblock, SBSIZE) != SBSIZE) {
4937 		printf("cannot update %s\n", special);
4938 		exit(35);
4939 	}
4940 
4941 	printf("clearing %u\n", inum);
4942 	off = (u_offset_t)fsbtodb(&sblock, itod(&sblock, inum)) * DEV_BSIZE;
4943 	(void) llseek(f, off, 0);
4944 	read(f, (char *)di_buf, sblock.fs_bsize);
4945 	j = itoo(&sblock, inum);
4946 	gen = di_buf[j].di_gen;
4947 	(void) memset((caddr_t)&di_buf[j], 0, ISIZE);
4948 	di_buf[j].di_gen = gen + 1;
4949 	(void) llseek(f, off, 0);
4950 	write(f, (char *)di_buf, sblock.fs_bsize);
4951 	exit(31+status);
4952 }
4953 
4954 static int
4955 isnumber(s)
4956 	char *s;
4957 {
4958 	register int	c;
4959 
4960 	if (s == NULL)
4961 		return (0);
4962 	while ((c = *s++) != NULL)
4963 		if (c < '0' || c > '9')
4964 			return (0);
4965 	return (1);
4966 }
4967 #endif /* OLD_FSDB_COMPATIBILITY */
4968 
4969 enum boolean { True, False };
4970 extent_block_t	*log_eb;
4971 ml_odunit_t	*log_odi;
4972 int		lufs_tid;	/* last valid TID seen */
4973 
4974 /*
4975  * no single value is safe to use to indicate
4976  * lufs_tid being invalid so we need a
4977  * seperate variable.
4978  */
4979 enum boolean	lufs_tid_valid;
4980 
4981 /*
4982  * log_get_header_info - get the basic info of the logging filesystem
4983  */
4984 int
4985 log_get_header_info(void)
4986 {
4987 	char		*b;
4988 	int		nb;
4989 
4990 	/*
4991 	 * Mark the global tid as invalid everytime we're called to
4992 	 * prevent any false positive responses.
4993 	 */
4994 	lufs_tid_valid = False;
4995 
4996 	/*
4997 	 * See if we've already set up the header areas. The only problem
4998 	 * with this approach is we don't reread the on disk data though
4999 	 * it shouldn't matter since we don't operate on a live disk.
5000 	 */
5001 	if ((log_eb != NULL) && (log_odi != NULL))
5002 		return (1);
5003 
5004 	/*
5005 	 * Either logging is disabled or we've not running 2.7.
5006 	 */
5007 	if (fs->fs_logbno == 0) {
5008 		printf("Logging doesn't appear to be enabled on this disk\n");
5009 		return (0);
5010 	}
5011 
5012 	/*
5013 	 * To find the log we need to first pick up the block allocation
5014 	 * data. The block number for that data is fs_logbno in the
5015 	 * super block.
5016 	 */
5017 	if ((b = getblk((u_offset_t)ldbtob(logbtodb(fs, fs->fs_logbno))))
5018 	    == 0) {
5019 		printf("getblk() indicates an error with logging block\n");
5020 		return (0);
5021 	}
5022 
5023 	/*
5024 	 * Next we need to figure out how big the extent data structure
5025 	 * really is. It can't be more then fs_bsize and you could just
5026 	 * allocate that but, why get sloppy.
5027 	 * 1 is subtracted from nextents because extent_block_t contains
5028 	 * a single extent_t itself.
5029 	 */
5030 	log_eb = (extent_block_t *)b;
5031 	if (log_eb->type != LUFS_EXTENTS) {
5032 		printf("Extents block has invalid type (0x%x)\n",
5033 		    log_eb->type);
5034 		return (0);
5035 	}
5036 	nb = sizeof (extent_block_t) +
5037 	    (sizeof (extent_t) * (log_eb->nextents - 1));
5038 
5039 	log_eb = (extent_block_t *)malloc(nb);
5040 	if (log_eb == NULL) {
5041 		printf("Failed to allocate memory for extent block log\n");
5042 		return (0);
5043 	}
5044 	memcpy(log_eb, b, nb);
5045 
5046 	if (log_eb->nextbno != 0)
5047 		/*
5048 		 * Currently, as of 11-Dec-1997 the field nextbno isn't
5049 		 * implemented. If someone starts using this sucker we'd
5050 		 * better warn somebody.
5051 		 */
5052 		printf("WARNING: extent block field nextbno is non-zero!\n");
5053 
5054 	/*
5055 	 * Now read in the on disk log structure. This is always in the
5056 	 * first block of the first extent.
5057 	 */
5058 	b = getblk((u_offset_t)ldbtob(logbtodb(fs, log_eb->extents[0].pbno)));
5059 	log_odi = (ml_odunit_t *)malloc(sizeof (ml_odunit_t));
5060 	if (log_odi == NULL) {
5061 		free(log_eb);
5062 		log_eb = NULL;
5063 		printf("Failed to allocate memory for ondisk structure\n");
5064 		return (0);
5065 	}
5066 	memcpy(log_odi, b, sizeof (ml_odunit_t));
5067 
5068 	/*
5069 	 * Consistency checks.
5070 	 */
5071 	if (log_odi->od_version != LUFS_VERSION_LATEST) {
5072 		free(log_eb);
5073 		log_eb = NULL;
5074 		free(log_odi);
5075 		log_odi = NULL;
5076 		printf("Version mismatch in on-disk version of log data\n");
5077 		return (0);
5078 	} else if (log_odi->od_badlog) {
5079 		printf("WARNING: Log was marked as bad\n");
5080 	}
5081 
5082 	return (1);
5083 }
5084 
5085 log_display_header(void)
5086 {
5087 	int x;
5088 	if (!log_get_header_info())
5089 		/*
5090 		 * No need to display anything here. The previous routine
5091 		 * has already done so.
5092 		 */
5093 		return;
5094 
5095 	if (fs->fs_magic == FS_MAGIC)
5096 		printf("Log block number: 0x%x\n------------------\n",
5097 		    fs->fs_logbno);
5098 	else
5099 		printf("Log frag number: 0x%x\n------------------\n",
5100 		    fs->fs_logbno);
5101 	printf("Extent Info\n\t# Extents  : %d\n\t# Bytes    : 0x%x\n",
5102 	    log_eb->nextents, log_eb->nbytes);
5103 	printf("\tNext Block : 0x%x\n\tExtent List\n\t--------\n",
5104 	    log_eb->nextbno);
5105 	for (x = 0; x < log_eb->nextents; x++)
5106 		printf("\t  [%d] lbno 0x%08x pbno 0x%08x nbno 0x%08x\n",
5107 		    x, log_eb->extents[x].lbno, log_eb->extents[x].pbno,
5108 		    log_eb->extents[x].nbno);
5109 	for (x = 0; x < log_eb->nextents; x++)
5110 		printf("\t  [%d] lbno 0x%08x pbno 0x%08x nbno 0x%08x\n",
5111 		    x, log_eb->extents[x].lbno, log_eb->extents[x].pbno,
5112 		    log_eb->extents[x].nbno);
5113 	printf("\nOn Disk Info\n\tbol_lof    : 0x%08x\n\teol_lof    : 0x%08x\n",
5114 	    log_odi->od_bol_lof, log_odi->od_eol_lof);
5115 	printf("\tlog_size   : 0x%08x\n",
5116 	    log_odi->od_logsize);
5117 	printf("\thead_lof   : 0x%08x\tident : 0x%x\n",
5118 	    log_odi->od_head_lof, log_odi->od_head_ident);
5119 	printf("\ttail_lof   : 0x%08x\tident : 0x%x\n\thead_tid   : 0x%08x\n",
5120 	    log_odi->od_tail_lof, log_odi->od_tail_ident, log_odi->od_head_tid);
5121 	printf("\tcheck sum  : 0x%08x\n", log_odi->od_chksum);
5122 	if (log_odi->od_chksum !=
5123 	    (log_odi->od_head_ident + log_odi->od_tail_ident))
5124 		printf("bad checksum: found 0x%08x, should be 0x%08x\n",
5125 		    log_odi->od_chksum,
5126 		    log_odi->od_head_ident + log_odi->od_tail_ident);
5127 	if (log_odi->od_head_lof == log_odi->od_tail_lof)
5128 		printf("\t --- Log is empty ---\n");
5129 }
5130 
5131 /*
5132  * log_lodb -- logical log offset to disk block number
5133  */
5134 log_lodb(u_offset_t off, diskaddr_t *pblk)
5135 {
5136 	uint32_t	lblk = (uint32_t)btodb(off);
5137 	int	x;
5138 
5139 	for (x = 0; x < log_eb->nextents; x++)
5140 		if ((lblk >= log_eb->extents[x].lbno) &&
5141 		    (lblk < (log_eb->extents[x].lbno +
5142 			log_eb->extents[x].nbno))) {
5143 			*pblk = (diskaddr_t)lblk - log_eb->extents[x].lbno +
5144 				logbtodb(fs, log_eb->extents[x].pbno);
5145 			return (1);
5146 		}
5147 	return (0);
5148 }
5149 
5150 /*
5151  * String names for the enumerated types. These are only used
5152  * for display purposes.
5153  */
5154 char *dt_str[] = {
5155 	"DT_NONE", "DT_SB", "DT_CG", "DT_SI", "DT_AB",
5156 	"DT_ABZERO", "DT_DIR", "DT_INODE", "DT_FBI",
5157 	"DT_QR", "DT_COMMIT", "DT_CANCEL", "DT_BOT",
5158 	"DT_EOT", "DT_UD", "DT_SUD", "DT_SHAD", "DT_MAX"
5159 };
5160 
5161 /*
5162  * log_read_log -- transfer information from the log and adjust offset
5163  */
5164 log_read_log(u_offset_t *addr, caddr_t va, int nb, uint32_t *chk)
5165 {
5166 	int		xfer;
5167 	caddr_t		bp;
5168 	diskaddr_t	pblk;
5169 	sect_trailer_t	*st;
5170 
5171 	while (nb) {
5172 		if (!log_lodb(*addr, &pblk)) {
5173 			printf("Invalid log offset\n");
5174 			return (0);
5175 		}
5176 
5177 		/*
5178 		 * fsdb getblk() expects offsets not block number.
5179 		 */
5180 		if ((bp = getblk((u_offset_t)dbtob(pblk))) == NULL)
5181 			return (0);
5182 
5183 		xfer = MIN(NB_LEFT_IN_SECTOR(*addr), nb);
5184 		if (va != NULL) {
5185 			memcpy(va, bp + blkoff(fs, *addr), xfer);
5186 			va += xfer;
5187 		}
5188 		nb -= xfer;
5189 		*addr += xfer;
5190 
5191 		/*
5192 		 * If the log offset is now at a sector trailer
5193 		 * run the checks if requested.
5194 		 */
5195 		if (NB_LEFT_IN_SECTOR(*addr) == 0) {
5196 			if (chk != NULL) {
5197 				st = (sect_trailer_t *)
5198 				    (bp + blkoff(fs, *addr));
5199 				if (*chk != st->st_ident) {
5200 					printf(
5201 			"Expected sector trailer id 0x%08x, but saw 0x%08x\n",
5202 						*chk, st->st_ident);
5203 					return (0);
5204 				} else {
5205 					*chk = st->st_ident + 1;
5206 					/*
5207 					 * We update the on disk structure
5208 					 * transaction ID each time we see
5209 					 * one. By comparing this value
5210 					 * to the last valid DT_COMMIT record
5211 					 * we can determine if our log is
5212 					 * completely valid.
5213 					 */
5214 					log_odi->od_head_tid = st->st_tid;
5215 				}
5216 			}
5217 			*addr += sizeof (sect_trailer_t);
5218 		}
5219 		if ((int32_t)*addr == log_odi->od_eol_lof)
5220 			*addr = log_odi->od_bol_lof;
5221 	}
5222 	return (1);
5223 }
5224 
5225 u_offset_t
5226 log_nbcommit(u_offset_t a)
5227 {
5228 	/*
5229 	 * Comments are straight from ufs_log.c
5230 	 *
5231 	 * log is the offset following the commit header. However,
5232 	 * if the commit header fell on the end-of-sector, then lof
5233 	 * has already been advanced to the beginning of the next
5234 	 * sector. So do nothgin. Otherwise, return the remaining
5235 	 * bytes in the sector.
5236 	 */
5237 	if ((a & (DEV_BSIZE - 1)) == 0)
5238 		return (0);
5239 	else
5240 		return (NB_LEFT_IN_SECTOR(a));
5241 }
5242 
5243 /*
5244  * log_show --  pretty print the deltas. The number of which is determined
5245  *		by the log_enum arg. If LOG_ALLDELTAS the routine, as the
5246  *		name implies dumps everything. If LOG_NDELTAS, the routine
5247  *		will print out "count" deltas starting at "addr". If
5248  *		LOG_CHECKSCAN then run through the log checking the st_ident
5249  *		for valid data.
5250  */
5251 log_show(enum log_enum l)
5252 {
5253 	struct delta	d;
5254 	int32_t		bol, eol;
5255 	int		x = 0;
5256 	uint32_t	chk;
5257 
5258 	if (!log_get_header_info())
5259 		/*
5260 		 * No need to display any error messages here. The previous
5261 		 * routine has already done so.
5262 		 */
5263 		return;
5264 
5265 	bol = log_odi->od_head_lof;
5266 	eol = log_odi->od_tail_lof;
5267 	chk = log_odi->od_head_ident;
5268 
5269 	if (bol == eol) {
5270 		if ((l == LOG_ALLDELTAS) || (l == LOG_CHECKSCAN)) {
5271 			printf("Empty log.\n");
5272 			return;
5273 		} else
5274 			printf("WARNING: empty log. addr may generate bogus"
5275 			    " information");
5276 	}
5277 
5278 	/*
5279 	 * Only reset the "addr" if we've been requested to show all
5280 	 * deltas in the log.
5281 	 */
5282 	if ((l == LOG_ALLDELTAS) || (l == LOG_CHECKSCAN))
5283 		addr = (u_offset_t)bol;
5284 
5285 	if (l != LOG_CHECKSCAN) {
5286 		printf("       Log Offset       Delta       Count     Type\n");
5287 		printf("-----------------------------------------"
5288 			"-----------------\n");
5289 	}
5290 
5291 	while ((bol != eol) && ((l == LOG_ALLDELTAS) ||
5292 	    (l == LOG_CHECKSCAN) || count--)) {
5293 		if (!log_read_log(&addr, (caddr_t)&d, sizeof (d),
5294 		    ((l == LOG_ALLDELTAS) || (l == LOG_CHECKSCAN)) ?
5295 		    &chk : NULL))
5296 			/*
5297 			 * Two failures are possible. One from getblk()
5298 			 * which prints out a message or when we've hit
5299 			 * an invalid block which may or may not indicate
5300 			 * an error
5301 			 */
5302 			goto end_scan;
5303 
5304 		if ((uint32_t)d.d_nb > log_odi->od_logsize) {
5305 			printf("Bad delta entry. size out of bounds\n");
5306 			return;
5307 		}
5308 		if (l != LOG_CHECKSCAN)
5309 			printf("[%04d]  %08x  %08x.%08x %08x  %s\n", x++, bol,
5310 			    d.d_mof, d.d_nb,
5311 			    dt_str[d.d_typ >= DT_MAX ? DT_MAX : d.d_typ]);
5312 
5313 		switch (d.d_typ) {
5314 		case DT_CANCEL:
5315 		case DT_ABZERO:
5316 			/*
5317 			 * These two deltas don't have log space
5318 			 * associated with the entry even though
5319 			 * d_nb is non-zero.
5320 			 */
5321 			break;
5322 
5323 		case DT_COMMIT:
5324 			/*
5325 			 * Commit records have zero size yet, the
5326 			 * rest of the current disk block is avoided.
5327 			 */
5328 			addr += log_nbcommit(addr);
5329 			lufs_tid = log_odi->od_head_tid;
5330 			lufs_tid_valid = True;
5331 			break;
5332 
5333 		default:
5334 			if (!log_read_log(&addr, NULL, d.d_nb,
5335 			    ((l == LOG_ALLDELTAS) ||
5336 			    (l == LOG_CHECKSCAN)) ? &chk : NULL))
5337 				goto end_scan;
5338 			break;
5339 		}
5340 		bol = (int32_t)addr;
5341 	}
5342 
5343 end_scan:
5344 	if (lufs_tid_valid == True) {
5345 		if (lufs_tid == log_odi->od_head_tid)
5346 			printf("scan -- okay\n");
5347 		else
5348 			printf("scan -- some transactions have been lost\n");
5349 	} else {
5350 		printf("scan -- failed to find a single valid transaction\n");
5351 		printf("        (possibly due to an empty log)\n");
5352 	}
5353 }
5354