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