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