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