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