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