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