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