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