root/usr/src/cmd/fs.d/ufs/fsdb/fsdb.c
/*
 * Copyright 2015 Gary Mills
 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Computer Consoles Inc.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that: (1) source distributions retain this entire copyright
 * notice and comment, and (2) distributions including binaries display
 * the following acknowledgement:  ``This product includes software
 * developed by the University of California, Berkeley and its contributors''
 * in the documentation or other materials provided with the distribution
 * and in all advertising materials mentioning features or use of this
 * software. Neither the name of the University nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 *  fsdb - file system debugger
 *
 *  usage: fsdb [-o suboptions] special
 *  options/suboptions:
 *      -o
 *              ?               display usage
 *              o               override some error conditions
 *              p="string"      set prompt to string
 *              w               open for write
 */

#include <sys/param.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <inttypes.h>
#include <sys/sysmacros.h>

#ifdef sun
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mntent.h>
#include <sys/wait.h>
#include <sys/fs/ufs_fsdir.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_acl.h>
#include <sys/fs/ufs_log.h>
#else
#include <sys/dir.h>
#include <ufs/fs.h>
#include <ufs/dinode.h>
#include <paths.h>
#endif /* sun */

#include <stdio.h>
#include <setjmp.h>

#define OLD_FSDB_COMPATIBILITY  /* To support the obsoleted "-z" option */

#ifndef _PATH_BSHELL
#define _PATH_BSHELL    "/bin/sh"
#endif /* _PATH_BSHELL */
/*
 * Defines from the 4.3-tahoe file system, for systems with the 4.2 or 4.3
 * file system.
 */
#ifndef FS_42POSTBLFMT
#define cg_blktot(cgp) (((cgp))->cg_btot)
#define cg_blks(fs, cgp, cylno) (((cgp))->cg_b[cylno])
#define cg_inosused(cgp) (((cgp))->cg_iused)
#define cg_blksfree(cgp) (((cgp))->cg_free)
#define cg_chkmagic(cgp) ((cgp)->cg_magic == CG_MAGIC)
#endif

/*
 * Never changing defines.
 */
#define OCTAL           8               /* octal base */
#define DECIMAL         10              /* decimal base */
#define HEX             16              /* hexadecimal base */

/*
 * Adjustable defines.
 */
#define NBUF            10              /* number of cache buffers */
#define PROMPTSIZE      80              /* size of user definable prompt */
#define MAXFILES        40000           /* max number of files ls can handle */
#define FIRST_DEPTH     10              /* default depth for find and ls */
#define SECOND_DEPTH    100             /* second try at depth (maximum) */
#define INPUTBUFFER     1040            /* size of input buffer */
#define BYTESPERLINE    16              /* bytes per line of /dxo output */
#define NREG            36              /* number of save registers */

#define DEVPREFIX       "/dev/"         /* Uninteresting part of "special" */

#if defined(OLD_FSDB_COMPATIBILITY)
#define FSDB_OPTIONS    "o:wp:z:"
#else
#define FSDB_OPTIONS    "o:wp:"
#endif /* OLD_FSDB_COMPATIBILITY */


/*
 * Values dependent on sizes of structs and such.
 */
#define NUMB            3                       /* these three are arbitrary, */
#define BLOCK           5                       /* but must be different from */
#define FRAGMENT        7                       /* the rest (hence odd). */
#define BITSPERCHAR     8                       /* couldn't find it anywhere  */
#define CHAR            (sizeof (char))
#define SHORT           (sizeof (short))
#define LONG            (sizeof (long))
#define U_OFFSET_T      (sizeof (u_offset_t))   /* essentially "long long" */
#define INODE           (sizeof (struct dinode))
#define DIRECTORY       (sizeof (struct direct))
#define CGRP            (sizeof (struct cg))
#define SB              (sizeof (struct fs))
#define BLKSIZE         (fs->fs_bsize)          /* for clarity */
#define FRGSIZE         (fs->fs_fsize)
#define BLKSHIFT        (fs->fs_bshift)
#define FRGSHIFT        (fs->fs_fshift)
#define SHADOW_DATA     (sizeof (struct ufs_fsd))

/*
 * Messy macros that would otherwise clutter up such glamorous code.
 */
#define itob(i)         (((u_offset_t)itod(fs, (i)) << \
        (u_offset_t)FRGSHIFT) + (u_offset_t)itoo(fs, (i)) * (u_offset_t)INODE)
#define min(x, y)       ((x) < (y) ? (x) : (y))
#define STRINGSIZE(d)   ((long)d->d_reclen - \
                                ((long)&d->d_name[0] - (long)&d->d_ino))
#define letter(c)       ((((c) >= 'a')&&((c) <= 'z')) ||\
                                (((c) >= 'A')&&((c) <= 'Z')))
#define digit(c)        (((c) >= '0') && ((c) <= '9'))
#define HEXLETTER(c)    (((c) >= 'A') && ((c) <= 'F'))
#define hexletter(c)    (((c) >= 'a') && ((c) <= 'f'))
#define octaldigit(c)   (((c) >= '0') && ((c) <= '7'))
#define uppertolower(c) ((c) - 'A' + 'a')
#define hextodigit(c)   ((c) - 'a' + 10)
#define numtodigit(c)   ((c) - '0')

#if !defined(loword)
#define loword(X)       (((ushort_t *)&X)[1])
#endif /* loword */

#if !defined(lobyte)
#define lobyte(X)       (((unsigned char *)&X)[1])
#endif /* lobyte */

/*
 * buffer cache structure.
 */
static struct lbuf {
        struct  lbuf  *fwd;
        struct  lbuf  *back;
        char    *blkaddr;
        short   valid;
        u_offset_t      blkno;
} lbuf[NBUF], bhdr;

/*
 * used to hold save registers (see '<' and '>').
 */
struct  save_registers {
        u_offset_t      sv_addr;
        u_offset_t      sv_value;
        long            sv_objsz;
} regs[NREG];

/*
 * cd, find, and ls use this to hold filenames.  Each filename is broken
 * up by a slash.  In other words, /usr/src/adm would have a len field
 * of 2 (starting from 0), and filenames->fname[0-2] would hold usr,
 * src, and adm components of the pathname.
 */
static struct filenames {
        ino_t   ino;            /* inode */
        long    len;            /* number of components */
        char    flag;           /* flag if using SECOND_DEPTH allocator */
        char    find;           /* flag if found by find */
        char    **fname;        /* hold components of pathname */
} *filenames, *top;

enum log_enum { LOG_NDELTAS, LOG_ALLDELTAS, LOG_CHECKSCAN };
#ifdef sun
struct fs       *fs;
static union {
        struct fs       un_filesystem;
        char            un_sbsize[SBSIZE];
} fs_un;
#define filesystem      fs_un.un_filesystem
#else
struct fs filesystem, *fs;      /* super block */
#endif /* sun */

/*
 * Global data.
 */
static char             *input_path[MAXPATHLEN];
static char             *stack_path[MAXPATHLEN];
static char             *current_path[MAXPATHLEN];
static char             input_buffer[INPUTBUFFER];
static char             *prompt;
static char             *buffers;
static char             scratch[64];
static char             BASE[] = "o u     x";
static char             PROMPT[PROMPTSIZE];
static char             laststyle = '/';
static char             lastpo = 'x';
static short            input_pointer;
static short            current_pathp;
static short            stack_pathp;
static short            input_pathp;
static short            cmp_level;
static int              nfiles;
static short            type = NUMB;
static short            dirslot;
static short            fd;
static short            c_count;
static short            error;
static short            paren;
static short            trapped;
static short            doing_cd;
static short            doing_find;
static short            find_by_name;
static short            find_by_inode;
static short            long_list;
static short            recursive;
static short            objsz = SHORT;
static short            override = 0;
static short            wrtflag = O_RDONLY;
static short            base = HEX;
static short            acting_on_inode;
static short            acting_on_directory;
static short            should_print = 1;
static short            clear;
static short            star;
static u_offset_t       addr;
static u_offset_t       bod_addr;
static u_offset_t       value;
static u_offset_t       erraddr;
static long             errcur_bytes;
static u_offset_t       errino;
static long             errinum;
static long             cur_cgrp;
static u_offset_t       cur_ino;
static long             cur_inum;
static u_offset_t       cur_dir;
static long             cur_block;
static long             cur_bytes;
static long             find_ino;
static u_offset_t       filesize;
static u_offset_t       blocksize;
static long             stringsize;
static long             count = 1;
static long             commands;
static long             read_requests;
static long             actual_disk_reads;
static jmp_buf          env;
static long             maxfiles;
static long             cur_shad;

#ifndef sun
extern char     *malloc(), *calloc();
#endif
static char             getachar();
static char             *getblk(), *fmtentry();

static offset_t         get(short);
static long             bmap();
static long             expr();
static long             term();
static long             getnumb();
static u_offset_t       getdirslot();
static unsigned long    *print_check(unsigned long *, long *, short, int);

static void             usage(char *);
static void             ungetachar(char);
static void             getnextinput();
static void             eat_spaces();
static void             restore_inode(ino_t);
static void             find();
static void             ls(struct filenames *, struct filenames *, short);
static void             formatf(struct filenames *, struct filenames *);
static void             parse();
static void             follow_path(long, long);
static void             getname();
static void             freemem(struct filenames *, int);
static void             print_path(char **, int);
static void             fill();
static void             put(u_offset_t, short);
static void             insert(struct lbuf *);
static void             puta();
static void             fprnt(char, char);
static void             index();
#ifdef _LARGEFILE64_SOURCE
static void             printll
        (u_offset_t value, int fieldsz, int digits, int lead);
#define print(value, fieldsz, digits, lead) \
        printll((u_offset_t)value, fieldsz, digits, lead)
#else /* !_LARGEFILE64_SOURCE */
static void             print(long value, int fieldsz, int digits, int lead);
#endif /* _LARGEFILE64_SOURCE */
static void             printsb(struct fs *);
static void             printcg(struct cg *);
static void             pbits(unsigned char *, int);
static void             old_fsdb(int, char *) __NORETURN;       /* For old fsdb functionality */

static int              isnumber(char *);
static int              icheck(u_offset_t);
static int              cgrp_check(long);
static int              valid_addr();
static int              match(char *, int);
static int              devcheck(short);
static int              bcomp();
static int              compare(char *, char *, short);
static int              check_addr(short, short *, short *, short);
static int              fcmp();
static int              ffcmp();

static int              getshadowslot(long);
static void             getshadowdata(long *, int);
static void             syncshadowscan(int);
static void             log_display_header(void);
static void             log_show(enum log_enum);

#ifdef sun
static void             err();
#else
static int              err();
#endif /* sun */

/* Suboption vector */
static char *subopt_v[] = {
#define OVERRIDE        0
        "o",
#define NEW_PROMPT      1
        "p",
#define WRITE_ENABLED   2
        "w",
#define ALT_PROMPT      3
        "prompt",
        NULL
};

/*
 * main - lines are read up to the unprotected ('\') newline and
 *      held in an input buffer.  Characters may be read from the
 *      input buffer using getachar() and unread using ungetachar().
 *      Reading the whole line ahead allows the use of debuggers
 *      which would otherwise be impossible since the debugger
 *      and fsdb could not share stdin.
 */

int
main(int argc, char *argv[])
{

        char            c, *cptr;
        short           i;
        struct direct   *dirp;
        struct lbuf     *bp;
        char            *progname;
        volatile short  colon;
        short           mode;
        long            temp;

        /* Options/Suboptions processing */
        int     opt;
        char    *subopts;
        char    *optval;

        /*
         * The following are used to support the old fsdb functionality
         * of clearing an inode. It's better to use 'clri'.
         */
        int                     inum;   /* Inode number to clear */
        char                    *special;

        setbuf(stdin, NULL);
        progname = argv[0];
        prompt = &PROMPT[0];
        /*
         * Parse options.
         */
        while ((opt = getopt(argc, argv, FSDB_OPTIONS)) != EOF) {
                switch (opt) {
#if defined(OLD_FSDB_COMPATIBILITY)
                case 'z':       /* Hack - Better to use clri */
                        (void) fprintf(stderr, "%s\n%s\n%s\n%s\n",
"Warning: The '-z' option of 'fsdb_ufs' has been declared obsolete",
"and may not be supported in a future version of Solaris.",
"While this functionality is currently still supported, the",
"recommended procedure to clear an inode is to use clri(8).");
                        if (isnumber(optarg)) {
                                inum = atoi(optarg);
                                special = argv[optind];
                                /* Doesn't return */
                                old_fsdb(inum, special);
                        } else {
                                usage(progname);
                                exit(31+1);
                        }
                        /* Should exit() before here */
                        /*NOTREACHED*/
#endif /* OLD_FSDB_COMPATIBILITY */
                case 'o':
                        /* UFS Specific Options */
                        subopts = optarg;
                        while (*subopts != '\0') {
                                switch (getsubopt(&subopts, subopt_v,
                                                                &optval)) {
                                case OVERRIDE:
                                        printf("error checking off\n");
                                        override = 1;
                                        break;

                                /*
                                 * Change the "-o prompt=foo" option to
                                 * "-o p=foo" to match documentation.
                                 * ALT_PROMPT continues support for the
                                 * undocumented "-o prompt=foo" option so
                                 * that we don't break anyone.
                                 */
                                case NEW_PROMPT:
                                case ALT_PROMPT:
                                        if (optval == NULL) {
                                                (void) fprintf(stderr,
                                                        "No prompt string\n");
                                                usage(progname);
                                        }
                                        (void) strncpy(PROMPT, optval,
                                                                PROMPTSIZE);
                                        break;

                                case WRITE_ENABLED:
                                        /* suitable for open */
                                        wrtflag = O_RDWR;
                                        break;

                                default:
                                        usage(progname);
                                        /* Should exit here */
                                }
                        }
                        break;

                default:
                        usage(progname);
                }
        }

        if ((argc - optind) != 1) {     /* Should just have "special" left */
                usage(progname);
        }
        special = argv[optind];

        /*
         * Unless it's already been set, the default prompt includes the
         * name of the special device.
         */
        if (*prompt == '\0')
                (void) sprintf(prompt, "%s > ", special);

        /*
         * Attempt to open the special file.
         */
        if ((fd = open(special, wrtflag)) < 0) {
                perror(special);
                exit(1);
        }
        /*
         * Read in the super block and validate (not too picky).
         */
        if (llseek(fd, (offset_t)(SBLOCK * DEV_BSIZE), 0) == -1) {
                perror(special);
                exit(1);
        }

#ifdef sun
        if (read(fd, &filesystem, SBSIZE) != SBSIZE) {
                printf("%s: cannot read superblock\n", special);
                exit(1);
        }
#else
        if (read(fd, &filesystem, sizeof (filesystem)) != sizeof (filesystem)) {
                printf("%s: cannot read superblock\n", special);
                exit(1);
        }
#endif /* sun */

        fs = &filesystem;
        if ((fs->fs_magic != FS_MAGIC) && (fs->fs_magic != MTB_UFS_MAGIC)) {
                if (!override) {
                        printf("%s: Bad magic number in file system\n",
                                                                special);
                        exit(1);
                }

                printf("WARNING: Bad magic number in file system. ");
                printf("Continue? (y/n): ");
                (void) fflush(stdout);
                if (gets(input_buffer) == NULL) {
                        exit(1);
                }

                if (*input_buffer != 'y' && *input_buffer != 'Y') {
                        exit(1);
                }
        }

        if ((fs->fs_magic == FS_MAGIC &&
            (fs->fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
            fs->fs_version != UFS_VERSION_MIN)) ||
            (fs->fs_magic == MTB_UFS_MAGIC &&
            (fs->fs_version > MTB_UFS_VERSION_1 ||
            fs->fs_version < MTB_UFS_VERSION_MIN))) {
                if (!override) {
                        printf("%s: Unrecognized UFS version number: %d\n",
                            special, fs->fs_version);
                        exit(1);
                }

                printf("WARNING: Unrecognized UFS version number. ");
                printf("Continue? (y/n): ");
                (void) fflush(stdout);
                if (gets(input_buffer) == NULL) {
                        exit(1);
                }

                if (*input_buffer != 'y' && *input_buffer != 'Y') {
                        exit(1);
                }
        }
#ifdef FS_42POSTBLFMT
        if (fs->fs_postblformat == FS_42POSTBLFMT)
                fs->fs_nrpos = 8;
#endif
        printf("fsdb of %s %s -- last mounted on %s\n",
                special,
                (wrtflag == O_RDWR) ? "(Opened for write)" : "(Read only)",
                &fs->fs_fsmnt[0]);
#ifdef sun
        printf("fs_clean is currently set to ");
        switch (fs->fs_clean) {

        case FSACTIVE:
                printf("FSACTIVE\n");
                break;
        case FSCLEAN:
                printf("FSCLEAN\n");
                break;
        case FSSTABLE:
                printf("FSSTABLE\n");
                break;
        case FSBAD:
                printf("FSBAD\n");
                break;
        case FSSUSPEND:
                printf("FSSUSPEND\n");
                break;
        case FSLOG:
                printf("FSLOG\n");
                break;
        case FSFIX:
                printf("FSFIX\n");
                if (!override) {
                        printf("%s: fsck may be running on this file system\n",
                                                                special);
                        exit(1);
                }

                printf("WARNING: fsck may be running on this file system. ");
                printf("Continue? (y/n): ");
                (void) fflush(stdout);
                if (gets(input_buffer) == NULL) {
                        exit(1);
                }

                if (*input_buffer != 'y' && *input_buffer != 'Y') {
                        exit(1);
                }
                break;
        default:
                printf("an unknown value (0x%x)\n", fs->fs_clean);
                break;
        }

        if (fs->fs_state == (FSOKAY - fs->fs_time)) {
                printf("fs_state consistent (fs_clean CAN be trusted)\n");
        } else {
                printf("fs_state inconsistent (fs_clean CAN'T trusted)\n");
        }
#endif /* sun */
        /*
         * Malloc buffers and set up cache.
         */
        buffers = malloc(NBUF * BLKSIZE);
        bhdr.fwd = bhdr.back = &bhdr;
        for (i = 0; i < NBUF; i++) {
                bp = &lbuf[i];
                bp->blkaddr = buffers + (i * BLKSIZE);
                bp->valid = 0;
                insert(bp);
        }
        /*
         * Malloc filenames structure.  The space for the actual filenames
         * is allocated as it needs it. We estimate the size based on the
         * number of inodes(objects) in the filesystem and the number of
         * directories.  The number of directories are padded by 3 because
         * each directory traversed during a "find" or "ls -R" needs 3
         * entries.
         */
        maxfiles = (long)((((u_offset_t)fs->fs_ncg * (u_offset_t)fs->fs_ipg) -
            (u_offset_t)fs->fs_cstotal.cs_nifree) +
            ((u_offset_t)fs->fs_cstotal.cs_ndir * (u_offset_t)3));

        filenames = (struct filenames *)calloc(maxfiles,
            sizeof (struct filenames));
        if (filenames == NULL) {
                /*
                 * If we could not allocate memory for all of files
                 * in the filesystem then, back off to the old fixed
                 * value.
                 */
                maxfiles = MAXFILES;
                filenames = (struct filenames *)calloc(maxfiles,
                    sizeof (struct filenames));
                if (filenames == NULL) {
                        printf("out of memory\n");
                        exit(1);
                }
        }

        restore_inode(2);
        /*
         * Malloc a few filenames (needed by pwd for example).
         */
        for (i = 0; i < MAXPATHLEN; i++) {
                input_path[i] = calloc(1, MAXNAMLEN);
                stack_path[i] = calloc(1, MAXNAMLEN);
                current_path[i] = calloc(1, MAXNAMLEN);
                if (current_path[i] == NULL) {
                        printf("out of memory\n");
                        exit(1);
                }
        }
        current_pathp = -1;

        (void) signal(2, err);
        (void) setjmp(env);

        getnextinput();
        /*
         * Main loop and case statement.  If an error condition occurs
         * initialization and recovery is attempted.
         */
        for (;;) {
                if (error) {
                        freemem(filenames, nfiles);
                        nfiles = 0;
                        c_count = 0;
                        count = 1;
                        star = 0;
                        error = 0;
                        paren = 0;
                        acting_on_inode = 0;
                        acting_on_directory = 0;
                        should_print = 1;
                        addr = erraddr;
                        cur_ino = errino;
                        cur_inum = errinum;
                        cur_bytes = errcur_bytes;
                        printf("?\n");
                        getnextinput();
                        if (error)
                                continue;
                }
                c_count++;

                switch (c = getachar()) {

                case '\n': /* command end */
                        freemem(filenames, nfiles);
                        nfiles = 0;
                        if (should_print && laststyle == '=') {
                                ungetachar(c);
                                goto calc;
                        }
                        if (c_count == 1) {
                                clear = 0;
                                should_print = 1;
                                erraddr = addr;
                                errino = cur_ino;
                                errinum = cur_inum;
                                errcur_bytes = cur_bytes;
                                switch (objsz) {
                                case DIRECTORY:
                                        if ((addr = getdirslot(
                                                        (long)dirslot+1)) == 0)
                                                should_print = 0;
                                        if (error) {
                                                ungetachar(c);
                                                continue;
                                        }
                                        break;
                                case INODE:
                                        cur_inum++;
                                        addr = itob(cur_inum);
                                        if (!icheck(addr)) {
                                                cur_inum--;
                                                should_print = 0;
                                        }
                                        break;
                                case CGRP:
                                case SB:
                                        cur_cgrp++;
                                        addr = cgrp_check(cur_cgrp);
                                        if (addr == 0) {
                                                cur_cgrp--;
                                                continue;
                                        }
                                        break;
                                case SHADOW_DATA:
                                        if ((addr = getshadowslot(
                                            (long)cur_shad + 1)) == 0)
                                                should_print = 0;
                                        if (error) {
                                                ungetachar(c);
                                                continue;
                                        }
                                        break;
                                default:
                                        addr += objsz;
                                        cur_bytes += objsz;
                                        if (valid_addr() == 0)
                                                continue;
                                }
                        }
                        if (type == NUMB)
                                trapped = 0;
                        if (should_print)
                                switch (objsz) {
                                case DIRECTORY:
                                        fprnt('?', 'd');
                                        break;
                                case INODE:
                                        fprnt('?', 'i');
                                        if (!error)
                                                cur_ino = addr;
                                        break;
                                case CGRP:
                                        fprnt('?', 'c');
                                        break;
                                case SB:
                                        fprnt('?', 's');
                                        break;
                                case SHADOW_DATA:
                                        fprnt('?', 'S');
                                        break;
                                case CHAR:
                                case SHORT:
                                case LONG:
                                        fprnt(laststyle, lastpo);
                                }
                        if (error) {
                                ungetachar(c);
                                continue;
                        }
                        c_count = colon = acting_on_inode = 0;
                        acting_on_directory = 0;
                        should_print = 1;
                        getnextinput();
                        if (error)
                                continue;
                        erraddr = addr;
                        errino = cur_ino;
                        errinum = cur_inum;
                        errcur_bytes = cur_bytes;
                        continue;

                case '(': /* numeric expression or unknown command */
                default:
                        colon = 0;
                        if (digit(c) || c == '(') {
                                ungetachar(c);
                                addr = expr();
                                type = NUMB;
                                value = addr;
                                continue;
                        }
                        printf("unknown command or bad syntax\n");
                        error++;
                        continue;

                case '?': /* general print facilities */
                case '/':
                        fprnt(c, getachar());
                        continue;

                case ';': /* command separator and . */
                case '\t':
                case ' ':
                case '.':
                        continue;

                case ':': /* command indicator */
                        colon++;
                        commands++;
                        should_print = 0;
                        stringsize = 0;
                        trapped = 0;
                        continue;

                case ',': /* count indicator */
                        colon = star = 0;
                        if ((c = getachar()) == '*') {
                                star = 1;
                                count = BLKSIZE;
                        } else {
                                ungetachar(c);
                                count = expr();
                                if (error)
                                        continue;
                                if (!count)
                                        count = 1;
                        }
                        clear = 0;
                        continue;

                case '+': /* address addition */
                        colon = 0;
                        c = getachar();
                        ungetachar(c);
                        if (c == '\n')
                                temp = 1;
                        else {
                                temp = expr();
                                if (error)
                                        continue;
                        }
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        switch (objsz) {
                        case DIRECTORY:
                                addr = getdirslot((long)(dirslot + temp));
                                if (error)
                                        continue;
                                break;
                        case INODE:
                                cur_inum += temp;
                                addr = itob(cur_inum);
                                if (!icheck(addr)) {
                                        cur_inum -= temp;
                                        continue;
                                }
                                break;
                        case CGRP:
                        case SB:
                                cur_cgrp += temp;
                                if ((addr = cgrp_check(cur_cgrp)) == 0) {
                                        cur_cgrp -= temp;
                                        continue;
                                }
                                break;
                        case SHADOW_DATA:
                                addr = getshadowslot((long)(cur_shad + temp));
                                if (error)
                                    continue;
                                break;

                        default:
                                laststyle = '/';
                                addr += temp * objsz;
                                cur_bytes += temp * objsz;
                                if (valid_addr() == 0)
                                        continue;
                        }
                        value = get(objsz);
                        continue;

                case '-': /* address subtraction */
                        colon = 0;
                        c = getachar();
                        ungetachar(c);
                        if (c == '\n')
                                temp = 1;
                        else {
                                temp = expr();
                                if (error)
                                        continue;
                        }
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        switch (objsz) {
                        case DIRECTORY:
                                addr = getdirslot((long)(dirslot - temp));
                                if (error)
                                        continue;
                                break;
                        case INODE:
                                cur_inum -= temp;
                                addr = itob(cur_inum);
                                if (!icheck(addr)) {
                                        cur_inum += temp;
                                        continue;
                                }
                                break;
                        case CGRP:
                        case SB:
                                cur_cgrp -= temp;
                                if ((addr = cgrp_check(cur_cgrp)) == 0) {
                                        cur_cgrp += temp;
                                        continue;
                                }
                                break;
                        case SHADOW_DATA:
                                addr = getshadowslot((long)(cur_shad - temp));
                                if (error)
                                        continue;
                                break;
                        default:
                                laststyle = '/';
                                addr -= temp * objsz;
                                cur_bytes -= temp * objsz;
                                if (valid_addr() == 0)
                                        continue;
                        }
                        value = get(objsz);
                        continue;

                case '*': /* address multiplication */
                        colon = 0;
                        temp = expr();
                        if (error)
                                continue;
                        if (objsz != INODE && objsz != DIRECTORY)
                                laststyle = '/';
                        addr *= temp;
                        value = get(objsz);
                        continue;

                case '%': /* address division */
                        colon = 0;
                        temp = expr();
                        if (error)
                                continue;
                        if (!temp) {
                                printf("divide by zero\n");
                                error++;
                                continue;
                        }
                        if (objsz != INODE && objsz != DIRECTORY)
                                laststyle = '/';
                        addr /= temp;
                        value = get(objsz);
                        continue;

                case '=': { /* assignment operation */
                        short tbase;
calc:
                        tbase = base;

                        c = getachar();
                        if (c == '\n') {
                                ungetachar(c);
                                c = lastpo;
                                if (acting_on_inode == 1) {
                                        if (c != 'o' && c != 'd' && c != 'x' &&
                                            c != 'O' && c != 'D' && c != 'X') {
                                                switch (objsz) {
                                                case LONG:
                                                        c = lastpo = 'X';
                                                        break;
                                                case SHORT:
                                                        c = lastpo = 'x';
                                                        break;
                                                case CHAR:
                                                        c = lastpo = 'c';
                                                }
                                        }
                                } else {
                                        if (acting_on_inode == 2)
                                                c = lastpo = 't';
                                }
                        } else if (acting_on_inode)
                                lastpo = c;
                        should_print = star = 0;
                        count = 1;
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        switch (c) {
                        case '"': /* character string */
                                if (type == NUMB) {
                                        blocksize = BLKSIZE;
                                        filesize = BLKSIZE * 2;
                                        cur_bytes = blkoff(fs, addr);
                                        if (objsz == DIRECTORY ||
                                                                objsz == INODE)
                                                lastpo = 'X';
                                }
                                puta();
                                continue;
                        case '+': /* =+ operator */
                                temp = expr();
                                value = get(objsz);
                                if (!error)
                                        put(value+temp, objsz);
                                continue;
                        case '-': /* =- operator */
                                temp = expr();
                                value = get(objsz);
                                if (!error)
                                        put(value-temp, objsz);
                                continue;
                        case 'b':
                        case 'c':
                                if (objsz == CGRP)
                                        fprnt('?', c);
                                else
                                        fprnt('/', c);
                                continue;
                        case 'i':
                                addr = cur_ino;
                                fprnt('?', 'i');
                                continue;
                        case 's':
                                fprnt('?', 's');
                                continue;
                        case 't':
                        case 'T':
                                laststyle = '=';
                                printf("\t\t");
                                {
                                        /*
                                         * Truncation is intentional so
                                         * ctime is happy.
                                         */
                                        time_t tvalue = (time_t)value;
                                        printf("%s", ctime(&tvalue));
                                }
                                continue;
                        case 'o':
                                base = OCTAL;
                                goto otx;
                        case 'd':
                                if (objsz == DIRECTORY) {
                                        addr = cur_dir;
                                        fprnt('?', 'd');
                                        continue;
                                }
                                base = DECIMAL;
                                goto otx;
                        case 'x':
                                base = HEX;
otx:
                                laststyle = '=';
                                printf("\t\t");
                                if (acting_on_inode)
                                        print(value & 0177777L, 12, -8, 0);
                                else
                                        print(addr & 0177777L, 12, -8, 0);
                                printf("\n");
                                base = tbase;
                                continue;
                        case 'O':
                                base = OCTAL;
                                goto OTX;
                        case 'D':
                                base = DECIMAL;
                                goto OTX;
                        case 'X':
                                base = HEX;
OTX:
                                laststyle = '=';
                                printf("\t\t");
                                if (acting_on_inode)
                                        print(value, 12, -8, 0);
                                else
                                        print(addr, 12, -8, 0);
                                printf("\n");
                                base = tbase;
                                continue;
                        default: /* regular assignment */
                                ungetachar(c);
                                value = expr();
                                if (error)
                                        printf("syntax error\n");
                                else
                                        put(value, objsz);
                                continue;
                        }
                }

                case '>': /* save current address */
                        colon = 0;
                        should_print = 0;
                        c = getachar();
                        if (!letter(c) && !digit(c)) {
                                printf("invalid register specification, ");
                                printf("must be letter or digit\n");
                                error++;
                                continue;
                        }
                        if (letter(c)) {
                                if (c < 'a')
                                        c = uppertolower(c);
                                c = hextodigit(c);
                        } else
                                c = numtodigit(c);
                        regs[c].sv_addr = addr;
                        regs[c].sv_value = value;
                        regs[c].sv_objsz = objsz;
                        continue;

                case '<': /* restore saved address */
                        colon = 0;
                        should_print = 0;
                        c = getachar();
                        if (!letter(c) && !digit(c)) {
                                printf("invalid register specification, ");
                                printf("must be letter or digit\n");
                                error++;
                                continue;
                        }
                        if (letter(c)) {
                                if (c < 'a')
                                        c = uppertolower(c);
                                c = hextodigit(c);
                        } else
                                c = numtodigit(c);
                        addr = regs[c].sv_addr;
                        value = regs[c].sv_value;
                        objsz = regs[c].sv_objsz;
                        continue;

                case 'a':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("at", 2)) {           /* access time */
                                acting_on_inode = 2;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_atime;
                                value = get(LONG);
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'b':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("block", 2)) {        /* block conversion */
                                if (type == NUMB) {
                                        value = addr;
                                        cur_bytes = 0;
                                        blocksize = BLKSIZE;
                                        filesize = BLKSIZE * 2;
                                }
                                addr = value << FRGSHIFT;
                                bod_addr = addr;
                                value = get(LONG);
                                type = BLOCK;
                                dirslot = 0;
                                trapped++;
                                continue;
                        }
                        if (match("bs", 2)) {           /* block size */
                                acting_on_inode = 1;
                                should_print = 1;
                                if (icheck(cur_ino) == 0)
                                        continue;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_blocks;
                                value = get(LONG);
                                type = 0;
                                continue;
                        }
                        if (match("base", 2)) {         /* change/show base */
showbase:
                                if ((c = getachar()) == '\n') {
                                        ungetachar(c);
                                        printf("base =\t\t");
                                        switch (base) {
                                        case OCTAL:
                                                printf("OCTAL\n");
                                                continue;
                                        case DECIMAL:
                                                printf("DECIMAL\n");
                                                continue;
                                        case HEX:
                                                printf("HEX\n");
                                                continue;
                                        }
                                }
                                if (c != '=') {
                                        printf("missing '='\n");
                                        error++;
                                        continue;
                                }
                                value = expr();
                                switch (value) {
                                default:
                                        printf("invalid base\n");
                                        error++;
                                        break;
                                case OCTAL:
                                case DECIMAL:
                                case HEX:
                                        base = (short)value;
                                }
                                goto showbase;
                        }
                        goto bad_syntax;

                case 'c':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("cd", 2)) {           /* change directory */
                                top = filenames - 1;
                                eat_spaces();
                                if ((c = getachar()) == '\n') {
                                        ungetachar(c);
                                        current_pathp = -1;
                                        restore_inode(2);
                                        continue;
                                }
                                ungetachar(c);
                                temp = cur_inum;
                                doing_cd = 1;
                                parse();
                                doing_cd = 0;
                                if (nfiles != 1) {
                                        restore_inode((ino_t)temp);
                                        if (!error) {
                                                print_path(input_path,
                                                        (int)input_pathp);
                                                if (nfiles == 0)
                                                        printf(" not found\n");
                                                else
                                                        printf(" ambiguous\n");
                                                error++;
                                        }
                                        continue;
                                }
                                restore_inode(filenames->ino);
                                if ((mode = icheck(addr)) == 0)
                                        continue;
                                if ((mode & IFMT) != IFDIR) {
                                        restore_inode((ino_t)temp);
                                        print_path(input_path,
                                                        (int)input_pathp);
                                        printf(" not a directory\n");
                                        error++;
                                        continue;
                                }
                                for (i = 0; i <= top->len; i++)
                                        (void) strcpy(current_path[i],
                                                top->fname[i]);
                                current_pathp = top->len;
                                continue;
                        }
                        if (match("cg", 2)) {           /* cylinder group */
                                if (type == NUMB)
                                        value = addr;
                                if (value > fs->fs_ncg - 1) {
                                        printf("maximum cylinder group is ");
                                        print(fs->fs_ncg - 1, 8, -8, 0);
                                        printf("\n");
                                        error++;
                                        continue;
                                }
                                type = objsz = CGRP;
                                cur_cgrp = (long)value;
                                addr = cgtod(fs, cur_cgrp) << FRGSHIFT;
                                continue;
                        }
                        if (match("ct", 2)) {           /* creation time */
                                acting_on_inode = 2;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_ctime;
                                value = get(LONG);
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'd':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("directory", 2)) {    /* directory offsets */
                                if (type == NUMB)
                                        value = addr;
                                objsz = DIRECTORY;
                                type = DIRECTORY;
                                addr = (u_offset_t)getdirslot((long)value);
                                continue;
                        }
                        if (match("db", 2)) {           /* direct block */
                                acting_on_inode = 1;
                                should_print = 1;
                                if (type == NUMB)
                                        value = addr;
                                if (value >= NDADDR) {
                                        printf("direct blocks are 0 to ");
                                        print(NDADDR - 1, 0, 0, 0);
                                        printf("\n");
                                        error++;
                                        continue;
                                }
                                addr = cur_ino;
                                if (!icheck(addr))
                                        continue;
                                addr = (long)
                                        &((struct dinode *)(uintptr_t)cur_ino)->
                                                                di_db[value];
                                bod_addr = addr;
                                cur_bytes = (value) * BLKSIZE;
                                cur_block = (long)value;
                                type = BLOCK;
                                dirslot = 0;
                                value = get(LONG);
                                if (!value && !override) {
                                        printf("non existent block\n");
                                        error++;
                                }
                                continue;
                        }
                        goto bad_syntax;

                case 'f':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("find", 3)) {         /* find command */
                                find();
                                continue;
                        }
                        if (match("fragment", 2)) {     /* fragment conv. */
                                if (type == NUMB) {
                                        value = addr;
                                        cur_bytes = 0;
                                        blocksize = FRGSIZE;
                                        filesize = FRGSIZE * 2;
                                }
                                if (min(blocksize, filesize) - cur_bytes >
                                                        FRGSIZE) {
                                        blocksize = cur_bytes + FRGSIZE;
                                        filesize = blocksize * 2;
                                }
                                addr = value << FRGSHIFT;
                                bod_addr = addr;
                                value = get(LONG);
                                type = FRAGMENT;
                                dirslot = 0;
                                trapped++;
                                continue;
                        }
                        if (match("file", 4)) {         /* access as file */
                                acting_on_inode = 1;
                                should_print = 1;
                                if (type == NUMB)
                                        value = addr;
                                addr = cur_ino;
                                if ((mode = icheck(addr)) == 0)
                                        continue;
                                if (!override) {
                                        switch (mode & IFMT) {
                                        case IFCHR:
                                        case IFBLK:
                                            printf("special device\n");
                                            error++;
                                            continue;
                                        }
                                }
                                if ((addr = (u_offset_t)
                                    (bmap((long)value) << FRGSHIFT)) == 0)
                                        continue;
                                cur_block = (long)value;
                                bod_addr = addr;
                                type = BLOCK;
                                dirslot = 0;
                                continue;
                        }
                        if (match("fill", 4)) {         /* fill */
                                if (getachar() != '=') {
                                        printf("missing '='\n");
                                        error++;
                                        continue;
                                }
                                if (objsz == INODE || objsz == DIRECTORY ||
                                    objsz == SHADOW_DATA) {
                                        printf(
                                            "can't fill inode or directory\n");
                                        error++;
                                        continue;
                                }
                                fill();
                                continue;
                        }
                        goto bad_syntax;

                case 'g':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("gid", 1)) {          /* group id */
                                acting_on_inode = 1;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_gid;
                                value = get(SHORT);
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'i':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("inode", 2)) { /* i# to inode conversion */
                                if (c_count == 2) {
                                        addr = cur_ino;
                                        value = get(INODE);
                                        type = 0;
                                        laststyle = '=';
                                        lastpo = 'i';
                                        should_print = 1;
                                        continue;
                                }
                                if (type == NUMB)
                                        value = addr;
                                addr = itob(value);
                                if (!icheck(addr))
                                        continue;
                                cur_ino = addr;
                                cur_inum = (long)value;
                                value = get(INODE);
                                type = 0;
                                continue;
                        }
                        if (match("ib", 2)) {   /* indirect block */
                                acting_on_inode = 1;
                                should_print = 1;
                                if (type == NUMB)
                                        value = addr;
                                if (value >= NIADDR) {
                                        printf("indirect blocks are 0 to ");
                                        print(NIADDR - 1, 0, 0, 0);
                                        printf("\n");
                                        error++;
                                        continue;
                                }
                                addr = (long)&((struct dinode *)(uintptr_t)
                                                cur_ino)->di_ib[value];
                                cur_bytes = (NDADDR - 1) * BLKSIZE;
                                temp = 1;
                                for (i = 0; i < value; i++) {
                                        temp *= NINDIR(fs) * BLKSIZE;
                                        cur_bytes += temp;
                                }
                                type = BLOCK;
                                dirslot = 0;
                                value = get(LONG);
                                if (!value && !override) {
                                        printf("non existent block\n");
                                        error++;
                                }
                                continue;
                        }
                        goto bad_syntax;

                case 'l':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("log_head", 8)) {
                                log_display_header();
                                should_print = 0;
                                continue;
                        }
                        if (match("log_delta", 9)) {
                                log_show(LOG_NDELTAS);
                                should_print = 0;
                                continue;
                        }
                        if (match("log_show", 8)) {
                                log_show(LOG_ALLDELTAS);
                                should_print = 0;
                                continue;
                        }
                        if (match("log_chk", 7)) {
                                log_show(LOG_CHECKSCAN);
                                should_print = 0;
                                continue;
                        }
                        if (match("log_otodb", 9)) {
                                if (log_lodb((u_offset_t)addr, &temp)) {
                                        addr = temp;
                                        should_print = 1;
                                        laststyle = '=';
                                } else
                                        error++;
                                continue;
                        }
                        if (match("ls", 2)) {           /* ls command */
                                temp = cur_inum;
                                recursive = long_list = 0;
                                top = filenames - 1;
                                for (;;) {
                                        eat_spaces();
                                        if ((c = getachar()) == '-') {
                                                if ((c = getachar()) == 'R') {
                                                        recursive = 1;
                                                        continue;
                                                } else if (c == 'l') {
                                                        long_list = 1;
                                                } else {
                                                        printf(
                                                            "unknown option ");
                                                        printf("'%c'\n", c);
                                                        error++;
                                                        break;
                                                }
                                        } else
                                                ungetachar(c);
                                        if ((c = getachar()) == '\n') {
                                                if (c_count != 2) {
                                                        ungetachar(c);
                                                        break;
                                                }
                                        }
                                        c_count++;
                                        ungetachar(c);
                                        parse();
                                        restore_inode((ino_t)temp);
                                        if (error)
                                                break;
                                }
                                recursive = 0;
                                if (error || nfiles == 0) {
                                        if (!error) {
                                                print_path(input_path,
                                                        (int)input_pathp);
                                                printf(" not found\n");
                                        }
                                        continue;
                                }
                                if (nfiles) {
                                    cmp_level = 0;
                                    qsort((char *)filenames, nfiles,
                                        sizeof (struct filenames), ffcmp);
                                    ls(filenames, filenames + (nfiles - 1), 0);
                                } else {
                                    printf("no match\n");
                                    error++;
                                }
                                restore_inode((ino_t)temp);
                                continue;
                        }
                        if (match("ln", 2)) {           /* link count */
                                acting_on_inode = 1;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_nlink;
                                value = get(SHORT);
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'm':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        addr = cur_ino;
                        if ((mode = icheck(addr)) == 0)
                                continue;
                        if (match("mt", 2)) {           /* modification time */
                                acting_on_inode = 2;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_mtime;
                                value = get(LONG);
                                type = 0;
                                continue;
                        }
                        if (match("md", 2)) {           /* mode */
                                acting_on_inode = 1;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_mode;
                                value = get(SHORT);
                                type = 0;
                                continue;
                        }
                        if (match("maj", 2)) {  /* major device number */
                                acting_on_inode = 1;
                                should_print = 1;
                                if (devcheck(mode))
                                        continue;
                                addr = (uintptr_t)&((struct dinode *)(uintptr_t)
                                                        cur_ino)->di_ordev;
                                {
                                        long    dvalue;
                                        dvalue = get(LONG);
                                        value = major(dvalue);
                                }
                                type = 0;
                                continue;
                        }
                        if (match("min", 2)) {  /* minor device number */
                                acting_on_inode = 1;
                                should_print = 1;
                                if (devcheck(mode))
                                        continue;
                                addr = (uintptr_t)&((struct dinode *)(uintptr_t)
                                                        cur_ino)->di_ordev;
                                {
                                        long    dvalue;
                                        dvalue = (long)get(LONG);
                                        value = minor(dvalue);
                                }
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'n':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("nm", 1)) {           /* directory name */
                                objsz = DIRECTORY;
                                acting_on_directory = 1;
                                cur_dir = addr;
                                if ((cptr = getblk(addr)) == 0)
                                        continue;
                                /*LINTED*/
                                dirp = (struct direct *)(cptr+blkoff(fs, addr));
                                stringsize = (long)dirp->d_reclen -
                                                ((long)&dirp->d_name[0] -
                                                        (long)&dirp->d_ino);
                                addr = (long)&((struct direct *)
                                                (uintptr_t)addr)->d_name[0];
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'o':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("override", 1)) {     /* override flip flop */
                                override = !override;
                                if (override)
                                        printf("error checking off\n");
                                else
                                        printf("error checking on\n");
                                continue;
                        }
                        goto bad_syntax;

                case 'p':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("pwd", 2)) {          /* print working dir */
                                print_path(current_path, (int)current_pathp);
                                printf("\n");
                                continue;
                        }
                        if (match("prompt", 2)) {       /* change prompt */
                                if ((c = getachar()) != '=') {
                                        printf("missing '='\n");
                                        error++;
                                        continue;
                                }
                                if ((c = getachar()) != '"') {
                                        printf("missing '\"'\n");
                                        error++;
                                        continue;
                                }
                                i = 0;
                                prompt = &prompt[0];
                                while ((c = getachar()) != '"' && c != '\n') {
                                        prompt[i++] = c;
                                        if (i >= PROMPTSIZE) {
                                                printf("string too long\n");
                                                error++;
                                                break;
                                        }
                                }
                                prompt[i] = '\0';
                                continue;
                        }
                        goto bad_syntax;

                case 'q':
                        if (!colon)
                                goto no_colon;
                        if (match("quit", 1)) {         /* quit */
                                if ((c = getachar()) != '\n') {
                                        error++;
                                        continue;
                                }
                                exit(0);
                        }
                        goto bad_syntax;

                case 's':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("sb", 2)) {           /* super block */
                                if (c_count == 2) {
                                        cur_cgrp = -1;
                                        type = objsz = SB;
                                        laststyle = '=';
                                        lastpo = 's';
                                        should_print = 1;
                                        continue;
                                }
                                if (type == NUMB)
                                        value = addr;
                                if (value > fs->fs_ncg - 1) {
                                        printf("maximum super block is ");
                                        print(fs->fs_ncg - 1, 8, -8, 0);
                                        printf("\n");
                                        error++;
                                        continue;
                                }
                                type = objsz = SB;
                                cur_cgrp = (long)value;
                                addr = cgsblock(fs, cur_cgrp) << FRGSHIFT;
                                continue;
                        }
                        if (match("shadow", 2)) {       /* shadow inode data */
                                if (type == NUMB)
                                        value = addr;
                                objsz = SHADOW_DATA;
                                type = SHADOW_DATA;
                                addr = getshadowslot(value);
                                continue;
                        }
                        if (match("si", 2)) {   /* shadow inode field */
                                acting_on_inode = 1;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_shadow;
                                value = get(LONG);
                                type = 0;
                                continue;
                        }

                        if (match("sz", 2)) {           /* file size */
                                acting_on_inode = 1;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_size;
                                value = get(U_OFFSET_T);
                                type = 0;
                                objsz = U_OFFSET_T;
                                laststyle = '=';
                                lastpo = 'X';
                                continue;
                        }
                        goto bad_syntax;

                case 'u':
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        if (match("uid", 1)) {          /* user id */
                                acting_on_inode = 1;
                                should_print = 1;
                                addr = (long)&((struct dinode *)
                                                (uintptr_t)cur_ino)->di_uid;
                                value = get(SHORT);
                                type = 0;
                                continue;
                        }
                        goto bad_syntax;

                case 'F': /* buffer status (internal use only) */
                        if (colon)
                                colon = 0;
                        else
                                goto no_colon;
                        for (bp = bhdr.fwd; bp != &bhdr; bp = bp->fwd)
                                printf("%8" PRIx64 " %d\n",
                                    bp->blkno, bp->valid);
                        printf("\n");
                        printf("# commands\t\t%ld\n", commands);
                        printf("# read requests\t\t%ld\n", read_requests);
                        printf("# actual disk reads\t%ld\n", actual_disk_reads);
                        continue;
no_colon:
                printf("a colon should precede a command\n");
                error++;
                continue;
bad_syntax:
                printf("more letters needed to distinguish command\n");
                error++;
                continue;
                }
        }
}

/*
 * usage - print usage and exit
 */
static void
usage(char *progname)
{
        printf("usage:   %s [options] special\n", progname);
        printf("options:\n");
        printf("\t-o            Specify ufs filesystem sepcific options\n");
        printf("                Available suboptions are:\n");
        printf("\t\t?           display usage\n");
        printf("\t\to           override some error conditions\n");
        printf("\t\tp=\"string\"        set prompt to string\n");
        printf("\t\tw           open for write\n");
        exit(1);
}

/*
 * getachar - get next character from input buffer.
 */
static char
getachar()
{
        return (input_buffer[input_pointer++]);
}

/*
 * ungetachar - return character to input buffer.
 */
static void
ungetachar(char c)
{
        if (input_pointer == 0) {
                printf("internal problem maintaining input buffer\n");
                error++;
                return;
        }
        input_buffer[--input_pointer] = c;
}

/*
 * getnextinput - display the prompt and read an input line.
 *      An input line is up to 128 characters terminated by the newline
 *      character.  Handle overflow, shell escape, and eof.
 */
static void
getnextinput()
{
        int     i;
        char    c;
        short   pid, rpid;
        int     retcode;

newline:
        i = 0;
        printf("%s", prompt);
ignore_eol:
        while ((c = getc(stdin)) != '\n' && !(c == '!' && i == 0) &&
                                        !feof(stdin) && i <= INPUTBUFFER - 2)
                input_buffer[i++] = c;
        if (i > 0 && input_buffer[i - 1] == '\\') {
                input_buffer[i++] = c;
                goto ignore_eol;
        }
        if (feof(stdin)) {
                printf("\n");
                exit(0);
        }
        if (c == '!') {
                if ((pid = fork()) == 0) {
                        (void) execl(_PATH_BSHELL, "sh", "-t", 0);
                        error++;
                        return;
                }
                while ((rpid = wait(&retcode)) != pid && rpid != -1)
                        ;
                printf("!\n");
                goto newline;
        }
        if (c != '\n')
                printf("input truncated to 128 characters\n");
        input_buffer[i] = '\n';
        input_pointer = 0;
}

/*
 * eat_spaces - read extraneous spaces.
 */
static void
eat_spaces()
{
        char    c;

        while ((c = getachar()) == ' ')
                ;
        ungetachar(c);
}

/*
 * restore_inode - set up all inode indicators so inum is now
 *      the current inode.
 */
static void
restore_inode(ino_t inum)
{
        errinum = cur_inum = inum;
        addr = errino = cur_ino = itob(inum);
}

/*
 * match - return false if the input does not match string up to
 *      upto letters.   Then proceed to chew up extraneous letters.
 */
static int
match(char *string, int upto)
{
        int     i, length = strlen(string) - 1;
        char    c;
        int     save_upto = upto;

        while (--upto) {
                string++;
                if ((c = getachar()) != *string) {
                        for (i = save_upto - upto; i; i--) {
                                ungetachar(c);
                                c = *--string;
                        }
                        return (0);
                }
                length--;
        }
        while (length--) {
                string++;
                if ((c = getachar()) != *string) {
                        ungetachar(c);
                        return (1);
                }
        }
        return (1);
}

/*
 * expr - expression evaluator.  Will evaluate expressions from
 *      left to right with no operator precedence.  Parentheses may
 *      be used.
 */
static long
expr()
{
        long    numb = 0, temp;
        char    c;

        numb = term();
        for (;;) {
                if (error)
                        return (~0);    /* error is set so value is ignored */
                c = getachar();
                switch (c) {

                case '+':
                        numb += term();
                        continue;

                case '-':
                        numb -= term();
                        continue;

                case '*':
                        numb *= term();
                        continue;

                case '%':
                        temp = term();
                        if (!temp) {
                                printf("divide by zero\n");
                                error++;
                                return (~0);
                        }
                        numb /= temp;
                        continue;

                case ')':
                        paren--;
                        return (numb);

                default:
                        ungetachar(c);
                        if (paren && !error) {
                                printf("missing ')'\n");
                                error++;
                        }
                        return (numb);
                }
        }
}

/*
 * term - used by expression evaluator to get an operand.
 */
static long
term()
{
        char    c;

        switch (c = getachar()) {

        default:
                ungetachar(c);
                /*FALLTHRU*/
        case '+':
                return (getnumb());

        case '-':
                return (-getnumb());

        case '(':
                paren++;
                return (expr());
        }
}

/*
 * getnumb - read a number from the input stream.  A leading
 *      zero signifies octal interpretation, a leading '0x'
 *      signifies hexadecimal, and a leading '0t' signifies
 *      decimal.  If the first character is a character,
 *      return an error.
 */
static long
getnumb()
{

        char            c, savec;
        long            number = 0, tbase, num;
        extern short    error;

        c = getachar();
        if (!digit(c)) {
                error++;
                ungetachar(c);
                return (-1);
        }
        if (c == '0') {
                tbase = OCTAL;
                if ((c = getachar()) == 'x')
                        tbase = HEX;
                else if (c == 't')
                        tbase = DECIMAL;
                else ungetachar(c);
        } else {
                tbase = base;
                ungetachar(c);
        }
        for (;;) {
                num = tbase;
                c = savec = getachar();
                if (HEXLETTER(c))
                        c = uppertolower(c);
                switch (tbase) {
                case HEX:
                        if (hexletter(c)) {
                                num = hextodigit(c);
                                break;
                        }
                        /*FALLTHRU*/
                case DECIMAL:
                        if (digit(c))
                                num = numtodigit(c);
                        break;
                case OCTAL:
                        if (octaldigit(c))
                                num = numtodigit(c);
                }
                if (num == tbase)
                        break;
                number = number * tbase + num;
        }
        ungetachar(savec);
        return (number);
}

/*
 * find - the syntax is almost identical to the unix command.
 *              find dir [-name pattern] [-inum number]
 *      Note:  only one of -name or -inum may be used at a time.
 *             Also, the -print is not needed (implied).
 */
static void
find()
{
        struct filenames        *fn;
        char                    c;
        long                    temp;
        short                   mode;

        eat_spaces();
        temp = cur_inum;
        top = filenames - 1;
        doing_cd = 1;
        parse();
        doing_cd = 0;
        if (nfiles != 1) {
                restore_inode((ino_t)temp);
                if (!error) {
                        print_path(input_path, (int)input_pathp);
                        if (nfiles == 0)
                                printf(" not found\n");
                        else
                                printf(" ambiguous\n");
                        error++;
                        return;
                }
        }
        restore_inode(filenames->ino);
        freemem(filenames, nfiles);
        nfiles = 0;
        top = filenames - 1;
        if ((mode = icheck(addr)) == 0)
                return;
        if ((mode & IFMT) != IFDIR) {
                print_path(input_path, (int)input_pathp);
                printf(" not a directory\n");
                error++;
                return;
        }
        eat_spaces();
        if ((c = getachar()) != '-') {
                restore_inode((ino_t)temp);
                printf("missing '-'\n");
                error++;
                return;
        }
        find_by_name = find_by_inode = 0;
        c = getachar();
        if (match("name", 4)) {
                eat_spaces();
                find_by_name = 1;
        } else if (match("inum", 4)) {
                eat_spaces();
                find_ino = expr();
                if (error) {
                        restore_inode((ino_t)temp);
                        return;
                }
                while ((c = getachar()) != '\n')
                        ;
                ungetachar(c);
                find_by_inode = 1;
        } else {
                restore_inode((ino_t)temp);
                printf("use -name or -inum with find\n");
                error++;
                return;
        }
        doing_find = 1;
        parse();
        doing_find = 0;
        if (error) {
                restore_inode((ino_t)temp);
                return;
        }
        for (fn = filenames; fn <= top; fn++) {
                if (fn->find == 0)
                        continue;
                printf("i#: ");
                print(fn->ino, 12, -8, 0);
                print_path(fn->fname, (int)fn->len);
                printf("\n");
        }
        restore_inode((ino_t)temp);
}

/*
 * ls - do an ls.  Should behave exactly as ls(1).
 *      Only -R and -l is supported and -l gives different results.
 */
static void
ls(struct filenames *fn0, struct filenames *fnlast, short level)
{
        struct filenames        *fn, *fnn;

        fn = fn0;
        for (;;) {
                fn0 = fn;
                if (fn0->len) {
                        cmp_level = level;
                        qsort((char *)fn0, fnlast - fn0 + 1,
                                sizeof (struct filenames), fcmp);
                }
                for (fnn = fn, fn++; fn <= fnlast; fnn = fn, fn++) {
                        if (fnn->len != fn->len && level == fnn->len - 1)
                                break;
                        if (fnn->len == 0)
                                continue;
                        if (strcmp(fn->fname[level], fnn->fname[level]))
                                break;
                }
                if (fn0->len && level != fn0->len - 1)
                        ls(fn0, fnn, level + 1);
                else {
                        if (fn0 != filenames)
                                printf("\n");
                        print_path(fn0->fname, (int)(fn0->len - 1));
                        printf(":\n");
                        if (fn0->len == 0)
                                cmp_level = level;
                        else
                                cmp_level = level + 1;
                        qsort((char *)fn0, fnn - fn0 + 1,
                                sizeof (struct filenames), fcmp);
                        formatf(fn0, fnn);
                        nfiles -= fnn - fn0 + 1;
                }
                if (fn > fnlast)
                        return;
        }
}

/*
 * formatf - code lifted from ls.
 */
static void
formatf(struct filenames *fn0, struct filenames *fnlast)
{
        struct filenames        *fn;
        int                     width = 0, w, nentry = fnlast - fn0 + 1;
        int                     i, j, columns, lines;
        char                    *cp;

        if (long_list) {
                columns = 1;
        } else {
                for (fn = fn0; fn <= fnlast; fn++) {
                        int len = strlen(fn->fname[cmp_level]) + 2;

                        if (len > width)
                                width = len;
                }
                width = (width + 8) &~ 7;
                columns = 80 / width;
                if (columns == 0)
                        columns = 1;
        }
        lines = (nentry + columns - 1) / columns;
        for (i = 0; i < lines; i++) {
                for (j = 0; j < columns; j++) {
                        fn = fn0 + j * lines + i;
                        if (long_list) {
                                printf("i#: ");
                                print(fn->ino, 12, -8, 0);
                        }
                        if ((cp = fmtentry(fn)) == NULL) {
                                printf("cannot read inode %ld\n", fn->ino);
                                return;
                        }
                        printf("%s", cp);
                        if (fn + lines > fnlast) {
                                printf("\n");
                                break;
                        }
                        w = strlen(cp);
                        while (w < width) {
                                w = (w + 8) &~ 7;
                                (void) putchar('\t');
                        }
                }
        }
}

/*
 * fmtentry - code lifted from ls.
 */
static char *
fmtentry(struct filenames *fn)
{
        static char     fmtres[BUFSIZ];
        struct dinode   *ip;
        char            *cptr, *cp, *dp;

        dp = &fmtres[0];
        for (cp = fn->fname[cmp_level]; *cp; cp++) {
                if (*cp < ' ' || *cp >= 0177)
                        *dp++ = '?';
                else
                        *dp++ = *cp;
        }
        addr = itob(fn->ino);
        if ((cptr = getblk(addr)) == 0)
                return (NULL);
        cptr += blkoff(fs, addr);
        /*LINTED*/
        ip = (struct dinode *)cptr;
        switch (ip->di_mode & IFMT) {
        case IFDIR:
                *dp++ = '/';
                break;
        case IFLNK:
                *dp++ = '@';
                break;
        case IFSOCK:
                *dp++ = '=';
                break;
#ifdef IFIFO
        case IFIFO:
                *dp++ = 'p';
                break;
#endif
        case IFCHR:
        case IFBLK:
        case IFREG:
                if (ip->di_mode & 0111)
                        *dp++ = '*';
                else
                        *dp++ = ' ';
                break;
        default:
                *dp++ = '?';

        }
        *dp++ = 0;
        return (fmtres);
}

/*
 * fcmp - routine used by qsort.  Will sort first by name, then
 *      then by pathname length if names are equal.  Uses global
 *      cmp_level to tell what component of the path name we are comparing.
 */
static int
fcmp(struct filenames *f1, struct filenames *f2)
{
        int value;

        if ((value = strcmp(f1->fname[cmp_level], f2->fname[cmp_level])))
                return (value);
        return (f1->len - f2->len);
}

/*
 * ffcmp - routine used by qsort.  Sort only by pathname length.
 */
static int
ffcmp(struct filenames *f1, struct filenames *f2)
{
        return (f1->len - f2->len);
}

/*
 * parse - set up the call to follow_path.
 */
static void
parse()
{
        int     i;
        char    c;

        stack_pathp = input_pathp = -1;
        if ((c = getachar()) == '/') {
                while ((c = getachar()) == '/')
                        ;
                ungetachar(c);
                cur_inum = 2;
                c = getachar();
                if ((c == '\n') || ((doing_cd) && (c == ' '))) {
                        ungetachar(c);
                        if (doing_cd) {
                                top++;
                                top->ino = 2;
                                top->len = -1;
                                nfiles = 1;
                                return;
                        }
                } else
                        ungetachar(c);
        } else {
                ungetachar(c);
                stack_pathp = current_pathp;
                if (!doing_find)
                        input_pathp = current_pathp;
                for (i = 0; i <= current_pathp; i++) {
                        if (!doing_find)
                                (void) strcpy(input_path[i], current_path[i]);
                        (void) strcpy(stack_path[i], current_path[i]);
                }
        }
        getname();
        follow_path((long)(stack_pathp + 1), cur_inum);
}

/*
 * follow_path - called by cd, find, and ls.
 *      input_path holds the name typed by the user.
 *      stack_path holds the name at the current depth.
 */
static void
follow_path(long level, long inum)
{
        struct direct           *dirp;
        char                    **ccptr, *cptr;
        int                     i;
        struct filenames        *tos, *bos, *fn, *fnn, *fnnn;
        long                    block;
        short                   mode;

        tos = top + 1;
        restore_inode((ino_t)inum);
        if ((mode = icheck(addr)) == 0)
                return;
        if ((mode & IFMT) != IFDIR)
            return;
        block = cur_bytes = 0;
        while (cur_bytes < filesize) {
            if (block == 0 || bcomp(addr)) {
                error = 0;
                if ((addr = ((u_offset_t)bmap(block++) <<
                                (u_offset_t)FRGSHIFT)) == 0)
                    break;
                if ((cptr = getblk(addr)) == 0)
                    break;
                cptr += blkoff(fs, addr);
            }
                /*LINTED*/
            dirp = (struct direct *)cptr;
            if (dirp->d_ino) {
                if (level > input_pathp || doing_find ||
                        compare(input_path[level], &dirp->d_name[0], 1)) {
                    if ((doing_find) &&
                        ((strcmp(dirp->d_name, ".") == 0 ||
                                        strcmp(dirp->d_name, "..") == 0)))
                        goto duplicate;
                    if (++top - filenames >= maxfiles) {
                        printf("too many files\n");
                        error++;
                        return;
                    }
                    top->fname = (char **)calloc(FIRST_DEPTH, sizeof (char **));
                    top->flag = 0;
                    if (top->fname == 0) {
                        printf("out of memory\n");
                        error++;
                        return;
                    }
                    nfiles++;
                    top->ino = dirp->d_ino;
                    top->len = stack_pathp;
                    top->find = 0;
                    if (doing_find) {
                        if (find_by_name) {
                            if (compare(input_path[0], &dirp->d_name[0], 1))
                                top->find = 1;
                        } else if (find_by_inode)
                            if (find_ino == dirp->d_ino)
                                top->find = 1;
                    }
                    if (top->len + 1 >= FIRST_DEPTH && top->flag == 0) {
                        ccptr = (char **)calloc(SECOND_DEPTH, sizeof (char **));
                        if (ccptr == 0) {
                            printf("out of memory\n");
                            error++;
                            return;
                        }
                        for (i = 0; i < FIRST_DEPTH; i++)
                                ccptr[i] = top->fname[i];
                        free((char *)top->fname);
                        top->fname = ccptr;
                        top->flag = 1;
                    }
                    if (top->len >= SECOND_DEPTH) {
                        printf("maximum depth exceeded, try to cd lower\n");
                        error++;
                        return;
                    }
                        /*
                         * Copy current depth.
                         */
                    for (i = 0; i <= stack_pathp; i++) {
                        top->fname[i] = calloc(1, strlen(stack_path[i])+1);
                        if (top->fname[i] == 0) {
                            printf("out of memory\n");
                            error++;
                            return;
                        }
                        (void) strcpy(top->fname[i], stack_path[i]);
                    }
                        /*
                         * Check for '.' or '..' typed.
                         */
                    if ((level <= input_pathp) &&
                                (strcmp(input_path[level], ".") == 0 ||
                                        strcmp(input_path[level], "..") == 0)) {
                        if (strcmp(input_path[level], "..") == 0 &&
                                                        top->len >= 0) {
                            free(top->fname[top->len]);
                            top->len -= 1;
                        }
                    } else {
                        /*
                         * Check for duplicates.
                         */
                        if (!doing_cd && !doing_find) {
                            for (fn = filenames; fn < top; fn++) {
                                if (fn->ino == dirp->d_ino &&
                                            fn->len == stack_pathp + 1) {
                                    for (i = 0; i < fn->len; i++)
                                        if (strcmp(fn->fname[i], stack_path[i]))
                                            break;
                                    if (i != fn->len ||
                                            strcmp(fn->fname[i], dirp->d_name))
                                        continue;
                                    freemem(top, 1);
                                    if (top == filenames)
                                        top = NULL;
                                    else
                                        top--;
                                    nfiles--;
                                    goto duplicate;
                                }
                            }
                        }
                        top->len += 1;
                        top->fname[top->len] = calloc(1,
                                                strlen(&dirp->d_name[0])+1);
                        if (top->fname[top->len] == 0) {
                            printf("out of memory\n");
                            error++;
                            return;
                        }
                        (void) strcpy(top->fname[top->len], &dirp->d_name[0]);
                    }
                }
            }
duplicate:
            addr += dirp->d_reclen;
            cptr += dirp->d_reclen;
            cur_bytes += dirp->d_reclen;
        }
        if (top < filenames)
            return;
        if ((doing_cd && level == input_pathp) ||
                (!recursive && !doing_find && level > input_pathp))
            return;
        bos = top;
        /*
         * Check newly added entries to determine if further expansion
         * is required.
         */
        for (fn = tos; fn <= bos; fn++) {
                /*
                 * Avoid '.' and '..' if beyond input.
                 */
            if ((recursive || doing_find) && (level > input_pathp) &&
                (strcmp(fn->fname[fn->len], ".") == 0 ||
                        strcmp(fn->fname[fn->len], "..") == 0))
                continue;
            restore_inode(fn->ino);
            if ((mode = icheck(cur_ino)) == 0)
                return;
            if ((mode & IFMT) == IFDIR || level < input_pathp) {
                /*
                 * Set up current depth, remove current entry and
                 * continue recursion.
                 */
                for (i = 0; i <= fn->len; i++)
                    (void) strcpy(stack_path[i], fn->fname[i]);
                stack_pathp = fn->len;
                if (!doing_find &&
                        (!recursive || (recursive && level <= input_pathp))) {
                        /*
                         * Remove current entry by moving others up.
                         */
                    freemem(fn, 1);
                    fnn = fn;
                    for (fnnn = fnn, fnn++; fnn <= top; fnnn = fnn, fnn++) {
                        fnnn->ino = fnn->ino;
                        fnnn->len = fnn->len;
                        if (fnnn->len + 1 < FIRST_DEPTH) {
                            fnnn->fname = (char **)calloc(FIRST_DEPTH,
                                                        sizeof (char **));
                            fnnn->flag = 0;
                        } else if (fnnn->len < SECOND_DEPTH) {
                            fnnn->fname = (char **)calloc(SECOND_DEPTH,
                                                        sizeof (char **));
                            fnnn->flag = 1;
                        } else {
                            printf("maximum depth exceeded, ");
                            printf("try to cd lower\n");
                            error++;
                            return;
                        }
                        for (i = 0; i <= fnn->len; i++)
                            fnnn->fname[i] = fnn->fname[i];
                    }
                    if (fn == tos)
                        fn--;
                    top--;
                    bos--;
                    nfiles--;
                }
                follow_path(level + 1, cur_inum);
                if (error)
                        return;
            }
        }
}

/*
 * getname - break up the pathname entered by the user into components.
 */
static void
getname()
{
        int     i;
        char    c;

        if ((c = getachar()) == '\n') {
            ungetachar(c);
            return;
        }
        ungetachar(c);
        input_pathp++;
clear:
        for (i = 0; i < MAXNAMLEN; i++)
            input_path[input_pathp][i] = '\0';
        for (;;) {
            c = getachar();
            if (c == '\\') {
                if ((int)strlen(input_path[input_pathp]) + 1 >= MAXNAMLEN) {
                    printf("maximum name length exceeded, ");
                    printf("truncating\n");
                    return;
                }
                input_path[input_pathp][strlen(input_path[input_pathp])] = c;
                input_path[input_pathp][strlen(input_path[input_pathp])] =
                                                getachar();
                continue;
            }
            if (c == ' ' || c == '\n') {
                ungetachar(c);
                return;
            }
            if (!doing_find && c == '/') {
                if (++input_pathp >= MAXPATHLEN) {
                    printf("maximum path length exceeded, ");
                    printf("truncating\n");
                    input_pathp--;
                    return;
                }
                goto clear;
            }
            if ((int)strlen(input_path[input_pathp]) >= MAXNAMLEN) {
                printf("maximum name length exceeded, truncating\n");
                return;
            }
            input_path[input_pathp][strlen(input_path[input_pathp])] = c;
        }
}

/*
 * compare - check if a filename matches the pattern entered by the user.
 *      Handles '*', '?', and '[]'.
 */
static int
compare(char *s1, char *s2, short at_start)
{
        char    c, *s;

        s = s2;
        while ((c = *s1) != '\0') {
                if (c == '*') {
                        if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
                                return (0);
                        if (*++s1 == 0)
                                return (1);
                        while (*s2) {
                                if (compare(s1, s2, 0))
                                        return (1);
                                if (error)
                                        return (0);
                                s2++;
                        }
                }
                if (*s2 == 0)
                        return (0);
                if (c == '\\') {
                        s1++;
                        goto compare_chars;
                }
                if (c == '?') {
                        if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
                                return (0);
                        s1++;
                        s2++;
                        continue;
                }
                if (c == '[') {
                        s1++;
                        if (*s2 >= *s1++) {
                                if (*s1++ != '-') {
                                        printf("missing '-'\n");
                                        error++;
                                        return (0);
                                }
                                if (*s2 <= *s1++) {
                                        if (*s1++ != ']') {
                                                printf("missing ']'");
                                                error++;
                                                return (0);
                                        }
                                        s2++;
                                        continue;
                                }
                        }
                }
compare_chars:
                if (*s1++ == *s2++)
                        continue;
                else
                        return (0);
        }
        if (*s1 == *s2)
                return (1);
        return (0);
}

/*
 * freemem - free the memory allocated to the filenames structure.
 */
static void
freemem(struct filenames *p, int numb)
{
        int     i, j;

        if (numb == 0)
                return;
        for (i = 0; i < numb; i++, p++) {
                for (j = 0; j <= p->len; j++)
                        free(p->fname[j]);
                free((char *)p->fname);
        }
}

/*
 * print_path - print the pathname held in p.
 */
static void
print_path(char *p[], int pntr)
{
        int     i;

        printf("/");
        if (pntr >= 0) {
                for (i = 0; i < pntr; i++)
                        printf("%s/", p[i]);
                printf("%s", p[pntr]);
        }
}

/*
 * fill - fill a section with a value or string.
 *      addr,count:fill=[value, "string"].
 */
static void
fill()
{
        char            *cptr;
        int             i;
        short           eof_flag, end = 0, eof = 0;
        long            temp, tcount;
        u_offset_t      taddr;

        if (wrtflag == O_RDONLY) {
                printf("not opened for write '-w'\n");
                error++;
                return;
        }
        temp = expr();
        if (error)
                return;
        if ((cptr = getblk(addr)) == 0)
                return;
        if (type == NUMB)
                eof_flag = 0;
        else
                eof_flag = 1;
        taddr = addr;
        switch (objsz) {
        case LONG:
                addr &= ~(LONG - 1);
                break;
        case SHORT:
                addr &= ~(SHORT - 1);
                temp &= 0177777L;
                break;
        case CHAR:
                temp &= 0377;
        }
        cur_bytes -= taddr - addr;
        cptr += blkoff(fs, addr);
        tcount = check_addr(eof_flag, &end, &eof, 0);
        for (i = 0; i < tcount; i++) {
                switch (objsz) {
                case LONG:
                        /*LINTED*/
                        *(long *)cptr = temp;
                        break;
                case SHORT:
                        /*LINTED*/
                        *(short *)cptr = temp;
                        break;
                case CHAR:
                        *cptr = temp;
                }
                cptr += objsz;
        }
        addr += (tcount - 1) * objsz;
        cur_bytes += (tcount - 1) * objsz;
        put((u_offset_t)temp, objsz);
        if (eof) {
                printf("end of file\n");
                error++;
        } else if (end) {
                printf("end of block\n");
                error++;
        }
}

/*
 * get - read a byte, short or long from the file system.
 *      The entire block containing the desired item is read
 *      and the appropriate data is extracted and returned.
 */
static offset_t
get(short lngth)
{

        char            *bptr;
        u_offset_t      temp = addr;

        objsz = lngth;
        if (objsz == INODE || objsz == SHORT)
                temp &= ~(SHORT - 1);
        else if (objsz == DIRECTORY || objsz == LONG || objsz == SHADOW_DATA)
                temp &= ~(LONG - 1);
        if ((bptr = getblk(temp)) == 0)
                return (-1);
        bptr += blkoff(fs, temp);
        switch (objsz) {
        case CHAR:
                return ((offset_t)*bptr);
        case SHORT:
        case INODE:
                /*LINTED*/
                return ((offset_t)(*(short *)bptr));
        case LONG:
        case DIRECTORY:
        case SHADOW_DATA:
                /*LINTED*/
                return ((offset_t)(*(long *)bptr));
        case U_OFFSET_T:
                /*LINTED*/
                return (*(offset_t *)bptr);
        }
        return (0);
}

/*
 * cgrp_check - make sure that we don't bump the cylinder group
 *      beyond the total number of cylinder groups or before the start.
 */
static int
cgrp_check(long cgrp)
{
        if (cgrp < 0) {
                if (objsz == CGRP)
                        printf("beginning of cylinder groups\n");
                else
                        printf("beginning of super blocks\n");
                error++;
                return (0);
        }
        if (cgrp >= fs->fs_ncg) {
                if (objsz == CGRP)
                        printf("end of cylinder groups\n");
                else
                        printf("end of super blocks\n");
                error++;
                return (0);
        }
        if (objsz == CGRP)
                return (cgtod(fs, cgrp) << FRGSHIFT);
        else
                return (cgsblock(fs, cgrp) << FRGSHIFT);
}

/*
 * icheck -  make sure we can read the block containing the inode
 *      and determine the filesize (0 if inode not allocated).  Return
 *      0 if error otherwise return the mode.
 */
int
icheck(u_offset_t address)
{
        char            *cptr;
        struct dinode   *ip;

        if ((cptr = getblk(address)) == 0)
                return (0);
        cptr += blkoff(fs, address);
        /*LINTED*/
        ip = (struct dinode *)cptr;
        if ((ip->di_mode & IFMT) == 0) {
                if (!override) {
                        printf("inode not allocated\n");
                        error++;
                        return (0);
                }
                blocksize = filesize = 0;
        } else {
                trapped++;
                filesize = ip->di_size;
                blocksize = filesize * 2;
        }
        return (ip->di_mode);
}

/*
 * getdirslot - get the address of the directory slot desired.
 */
static u_offset_t
getdirslot(long slot)
{
        char            *cptr;
        struct direct   *dirp;
        short           i;
        char            *string = &scratch[0];
        short           bod = 0, mode, temp;

        if (slot < 0) {
                slot = 0;
                bod++;
        }
        if (type != DIRECTORY) {
                if (type == BLOCK)
                        string = "block";
                else
                        string = "fragment";
                addr = bod_addr;
                if ((cptr = getblk(addr)) == 0)
                        return (0);
                cptr += blkoff(fs, addr);
                cur_bytes = 0;
                /*LINTED*/
                dirp = (struct direct *)cptr;
                for (dirslot = 0; dirslot < slot; dirslot++) {
                        /*LINTED*/
                        dirp = (struct direct *)cptr;
                        if (blocksize > filesize) {
                                if (cur_bytes + (long)dirp->d_reclen >=
                                                                filesize) {
                                        printf("end of file\n");
                                        erraddr = addr;
                                        errcur_bytes = cur_bytes;
                                        stringsize = STRINGSIZE(dirp);
                                        error++;
                                        return (addr);
                                }
                        } else {
                                if (cur_bytes + (long)dirp->d_reclen >=
                                                                blocksize) {
                                        printf("end of %s\n", string);
                                        erraddr = addr;
                                        errcur_bytes = cur_bytes;
                                        stringsize = STRINGSIZE(dirp);
                                        error++;
                                        return (addr);
                                }
                        }
                        cptr += dirp->d_reclen;
                        addr += dirp->d_reclen;
                        cur_bytes += dirp->d_reclen;
                }
                if (bod) {
                        if (blocksize > filesize)
                                printf("beginning of file\n");
                        else
                                printf("beginning of %s\n", string);
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        error++;
                }
                stringsize = STRINGSIZE(dirp);
                return (addr);
        } else {
                addr = cur_ino;
                if ((mode = icheck(addr)) == 0)
                        return (0);
                if (!override && (mode & IFDIR) == 0) {
                        printf("inode is not a directory\n");
                        error++;
                        return (0);
                }
                temp = slot;
                i = cur_bytes = 0;
                for (;;) {
                        if (i == 0 || bcomp(addr)) {
                                error = 0;
                                if ((addr = (bmap((long)i++) << FRGSHIFT)) == 0)
                                        break;
                                if ((cptr = getblk(addr)) == 0)
                                        break;
                                cptr += blkoff(fs, addr);
                        }
                        /*LINTED*/
                        dirp = (struct direct *)cptr;
                        value = dirp->d_ino;
                        if (!temp--)
                                break;
                        if (cur_bytes + (long)dirp->d_reclen >= filesize) {
                                printf("end of file\n");
                                dirslot = slot - temp - 1;
                                objsz = DIRECTORY;
                                erraddr = addr;
                                errcur_bytes = cur_bytes;
                                stringsize = STRINGSIZE(dirp);
                                error++;
                                return (addr);
                        }
                        addr += dirp->d_reclen;
                        cptr += dirp->d_reclen;
                        cur_bytes += dirp->d_reclen;
                }
                dirslot = slot;
                objsz = DIRECTORY;
                if (bod) {
                        printf("beginning of file\n");
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        error++;
                }
                stringsize = STRINGSIZE(dirp);
                return (addr);
        }
}


/*
 * getshadowslot - get the address of the shadow data desired
 */
static int
getshadowslot(long shadow)
{
        struct ufs_fsd          fsd;
        short                   bod = 0, mode;
        long                    taddr, tcurbytes;

        if (shadow < 0) {
                shadow = 0;
                bod++;
        }
        if (type != SHADOW_DATA) {
                if (shadow < cur_shad) {
                        printf("can't scan shadow data in reverse\n");
                        error++;
                        return (0);
                }
        } else {
                addr = cur_ino;
                if ((mode = icheck(addr)) == 0)
                        return (0);
                if (!override && (mode & IFMT) != IFSHAD) {
                        printf("inode is not a shadow\n");
                        error++;
                        return (0);
                }
                cur_bytes = 0;
                cur_shad = 0;
                syncshadowscan(1);      /* force synchronization */
        }

        for (; cur_shad < shadow; cur_shad++) {
                taddr = addr;
                tcurbytes = cur_bytes;
                getshadowdata((long *)&fsd, LONG + LONG);
                addr = taddr;
                cur_bytes = tcurbytes;
                if (cur_bytes + (long)fsd.fsd_size > filesize) {
                        syncshadowscan(0);
                        printf("end of file\n");
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        error++;
                        return (addr);
                }
                addr += fsd.fsd_size;
                cur_bytes += fsd.fsd_size;
                syncshadowscan(0);
        }
        if (type == SHADOW_DATA)
                objsz = SHADOW_DATA;
        if (bod) {
                printf("beginning of file\n");
                erraddr = addr;
                errcur_bytes = cur_bytes;
                error++;
        }
        return (addr);
}

static void
getshadowdata(long *buf, int len)
{
        long    tfsd;

        len /= LONG;
        for (tfsd = 0; tfsd < len; tfsd++) {
                buf[tfsd] = get(SHADOW_DATA);
                addr += LONG;
                cur_bytes += LONG;
                syncshadowscan(0);
        }
}

static void
syncshadowscan(int force)
{
        long    curblkoff;
        if (type == SHADOW_DATA && (force ||
            lblkno(fs, addr) != (bhdr.fwd)->blkno)) {
                curblkoff = blkoff(fs, cur_bytes);
                addr = bmap(lblkno(fs, cur_bytes)) << FRGSHIFT;
                addr += curblkoff;
                cur_bytes += curblkoff;
                (void) getblk(addr);
                objsz = SHADOW_DATA;
        }
}



/*
 * putf - print a byte as an ascii character if possible.
 *      The exceptions are tabs, newlines, backslashes
 *      and nulls which are printed as the standard C
 *      language escapes. Characters which are not
 *      recognized are printed as \?.
 */
static void
putf(char c)
{

        if (c <= 037 || c >= 0177 || c == '\\') {
                printf("\\");
                switch (c) {
                case '\\':
                        printf("\\");
                        break;
                case '\t':
                        printf("t");
                        break;
                case '\n':
                        printf("n");
                        break;
                case '\0':
                        printf("0");
                        break;
                default:
                        printf("?");
                }
        } else {
                printf("%c", c);
                printf(" ");
        }
}

/*
 * put - write an item into the buffer for the current address
 *      block.  The value is checked to make sure that it will
 *      fit in the size given without truncation.  If successful,
 *      the entire block is written back to the file system.
 */
static void
put(u_offset_t item, short lngth)
{

        char    *bptr, *sbptr;
        long    s_err, nbytes;
        long    olditem;

        if (wrtflag == O_RDONLY) {
                printf("not opened for write '-w'\n");
                error++;
                return;
        }
        objsz = lngth;
        if ((sbptr = getblk(addr)) == 0)
                return;
        bptr = sbptr + blkoff(fs, addr);
        switch (objsz) {
        case LONG:
        case DIRECTORY:
                /*LINTED*/
                olditem = *(long *)bptr;
                /*LINTED*/
                *(long *)bptr = item;
                break;
        case SHORT:
        case INODE:
                /*LINTED*/
                olditem = (long)*(short *)bptr;
                item &= 0177777L;
                /*LINTED*/
                *(short *)bptr = item;
                break;
        case CHAR:
                olditem = (long)*bptr;
                item &= 0377;
                *bptr = lobyte(loword(item));
                break;
        default:
                error++;
                return;
        }
        if ((s_err = llseek(fd, (offset_t)(addr & fs->fs_bmask), 0)) == -1) {
                error++;
                printf("seek error : %" PRIx64 "\n", addr);
                return;
        }
        if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
                error++;
                printf("write error : addr   = %" PRIx64 "\n", addr);
                printf("            : s_err  = %lx\n", s_err);
                printf("            : nbytes = %lx\n", nbytes);
                return;
        }
        if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
                index(base);
                print(olditem, 8, -8, 0);
                printf("\t=\t");
                print(item, 8, -8, 0);
                printf("\n");
        } else {
                if (objsz == DIRECTORY) {
                        addr = cur_dir;
                        fprnt('?', 'd');
                } else {
                        addr = cur_ino;
                        objsz = INODE;
                        fprnt('?', 'i');
                }
        }
}

/*
 * getblk - check if the desired block is in the file system.
 *      Search the incore buffers to see if the block is already
 *      available. If successful, unlink the buffer control block
 *      from its position in the buffer list and re-insert it at
 *      the head of the list.  If failure, use the last buffer
 *      in the list for the desired block. Again, this control
 *      block is placed at the head of the list. This process
 *      will leave commonly requested blocks in the in-core buffers.
 *      Finally, a pointer to the buffer is returned.
 */
static char *
getblk(u_offset_t address)
{

        struct lbuf     *bp;
        long            s_err, nbytes;
        unsigned long   block;

        read_requests++;
        block = lblkno(fs, address);
        if (block >= fragstoblks(fs, fs->fs_size)) {
                printf("cannot read block %lu\n", block);
                error++;
                return (0);
        }
        for (bp = bhdr.fwd; bp != &bhdr; bp = bp->fwd)
                if (bp->valid && bp->blkno == block)
                        goto xit;
        actual_disk_reads++;
        bp = bhdr.back;
        bp->blkno = block;
        bp->valid = 0;
        if ((s_err = llseek(fd, (offset_t)(address & fs->fs_bmask), 0)) == -1) {
                error++;
                printf("seek error : %" PRIx64 "\n", address);
                return (0);
        }
        if ((nbytes = read(fd, bp->blkaddr, BLKSIZE)) != BLKSIZE) {
                error++;
                printf("read error : addr   = %" PRIx64 "\n", address);
                printf("           : s_err  = %lx\n", s_err);
                printf("           : nbytes = %lx\n", nbytes);
                return (0);
        }
        bp->valid++;
xit:    bp->back->fwd = bp->fwd;
        bp->fwd->back = bp->back;
        insert(bp);
        return (bp->blkaddr);
}

/*
 * insert - place the designated buffer control block
 *      at the head of the linked list of buffers.
 */
static void
insert(struct lbuf *bp)
{

        bp->back = &bhdr;
        bp->fwd = bhdr.fwd;
        bhdr.fwd->back = bp;
        bhdr.fwd = bp;
}

/*
 * err - called on interrupts.  Set the current address
 *      back to the last address stored in erraddr. Reset all
 *      appropriate flags.  A reset call is made to return
 *      to the main loop;
 */
#ifdef sun
/*ARGSUSED*/
static void
err(int sig)
#else
err()
#endif /* sun */
{
        freemem(filenames, nfiles);
        nfiles = 0;
        (void) signal(2, err);
        addr = erraddr;
        cur_ino = errino;
        cur_inum = errinum;
        cur_bytes = errcur_bytes;
        error = 0;
        c_count = 0;
        printf("\n?\n");
        (void) fseek(stdin, 0L, 2);
        longjmp(env, 0);
}

/*
 * devcheck - check that the given mode represents a
 *      special device. The IFCHR bit is on for both
 *      character and block devices.
 */
static int
devcheck(short md)
{
        if (override)
                return (0);
        switch (md & IFMT) {
        case IFCHR:
        case IFBLK:
                return (0);
        }

        printf("not character or block device\n");
        error++;
        return (1);
}

/*
 * nullblk - return error if address is zero.  This is done
 *      to prevent block 0 from being used as an indirect block
 *      for a large file or as a data block for a small file.
 */
static int
nullblk(long bn)
{
        if (bn != 0)
                return (0);
        printf("non existent block\n");
        error++;
        return (1);
}

/*
 * puta - put ascii characters into a buffer.  The string
 *      terminates with a quote or newline.  The leading quote,
 *      which is optional for directory names, was stripped off
 *      by the assignment case in the main loop.
 */
static void
puta()
{
        char            *cptr, c;
        int             i;
        char            *sbptr;
        short           terror = 0;
        long            maxchars, s_err, nbytes, temp;
        u_offset_t      taddr = addr;
        long            tcount = 0, item, olditem = 0;

        if (wrtflag == O_RDONLY) {
                printf("not opened for write '-w'\n");
                error++;
                return;
        }
        if ((sbptr = getblk(addr)) == 0)
                return;
        cptr = sbptr + blkoff(fs, addr);
        if (objsz == DIRECTORY) {
                if (acting_on_directory)
                        maxchars = stringsize - 1;
                else
                        maxchars = LONG;
        } else if (objsz == INODE)
                maxchars = objsz - (addr - cur_ino);
        else
                maxchars = min(blocksize - cur_bytes, filesize - cur_bytes);
        while ((c = getachar()) != '"') {
                if (tcount >= maxchars) {
                        printf("string too long\n");
                        if (objsz == DIRECTORY)
                                addr = cur_dir;
                        else if (acting_on_inode || objsz == INODE)
                                addr = cur_ino;
                        else
                                addr = taddr;
                        erraddr = addr;
                        errcur_bytes = cur_bytes;
                        terror++;
                        break;
                }
                tcount++;
                if (c == '\n') {
                        ungetachar(c);
                        break;
                }
                temp = (long)*cptr;
                olditem <<= BITSPERCHAR;
                olditem += temp & 0xff;
                if (c == '\\') {
                        switch (c = getachar()) {
                        case 't':
                                *cptr++ = '\t';
                                break;
                        case 'n':
                                *cptr++ = '\n';
                                break;
                        case '0':
                                *cptr++ = '\0';
                                break;
                        default:
                                *cptr++ = c;
                                break;
                        }
                }
                else
                        *cptr++ = c;
        }
        if (objsz == DIRECTORY && acting_on_directory)
                for (i = tcount; i <= maxchars; i++)
                        *cptr++ = '\0';
        if ((s_err = llseek(fd, (offset_t)(addr & fs->fs_bmask), 0)) == -1) {
                error++;
                printf("seek error : %" PRIx64 "\n", addr);
                return;
        }
        if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
                error++;
                printf("write error : addr   = %" PRIx64 "\n", addr);
                printf("            : s_err  = %lx\n", s_err);
                printf("            : nbytes = %lx\n", nbytes);
                return;
        }
        if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
                addr += tcount;
                cur_bytes += tcount;
                taddr = addr;
                if (objsz != CHAR) {
                        addr &= ~(objsz - 1);
                        cur_bytes -= taddr - addr;
                }
                if (addr == taddr) {
                        addr -= objsz;
                        taddr = addr;
                }
                tcount = LONG - (taddr - addr);
                index(base);
                if ((cptr = getblk(addr)) == 0)
                        return;
                cptr += blkoff(fs, addr);
                switch (objsz) {
                case LONG:
                        /*LINTED*/
                        item = *(long *)cptr;
                        if (tcount < LONG) {
                                olditem <<= tcount * BITSPERCHAR;
                                temp = 1;
                                for (i = 0; i < (tcount*BITSPERCHAR); i++)
                                        temp <<= 1;
                                olditem += item & (temp - 1);
                        }
                        break;
                case SHORT:
                        /*LINTED*/
                        item = (long)*(short *)cptr;
                        if (tcount < SHORT) {
                                olditem <<= tcount * BITSPERCHAR;
                                temp = 1;
                                for (i = 0; i < (tcount * BITSPERCHAR); i++)
                                        temp <<= 1;
                                olditem += item & (temp - 1);
                        }
                        olditem &= 0177777L;
                        break;
                case CHAR:
                        item = (long)*cptr;
                        olditem &= 0377;
                }
                print(olditem, 8, -8, 0);
                printf("\t=\t");
                print(item, 8, -8, 0);
                printf("\n");
        } else {
                if (objsz == DIRECTORY) {
                        addr = cur_dir;
                        fprnt('?', 'd');
                } else {
                        addr = cur_ino;
                        objsz = INODE;
                        fprnt('?', 'i');
                }
        }
        if (terror)
                error++;
}

/*
 * fprnt - print data.  'count' elements are printed where '*' will
 *      print an entire blocks worth or up to the eof, whichever
 *      occurs first.  An error will occur if crossing a block boundary
 *      is attempted since consecutive blocks don't usually have
 *      meaning.  Current print types:
 *              /               b   - print as bytes (base sensitive)
 *                              c   - print as characters
 *                              o O - print as octal shorts (longs)
 *                              d D - print as decimal shorts (longs)
 *                              x X - print as hexadecimal shorts (longs)
 *              ?               c   - print as cylinder groups
 *                              d   - print as directories
 *                              i   - print as inodes
 *                              s   - print as super blocks
 *                              S   - print as shadow data
 */
static void
fprnt(char style, char po)
{
        int             i;
        struct fs       *sb;
        struct cg       *cg;
        struct direct   *dirp;
        struct dinode   *ip;
        int             tbase;
        char            c, *cptr, *p;
        long            tinode, tcount, temp;
        u_offset_t      taddr;
        short           offset, mode, end = 0, eof = 0, eof_flag;
        unsigned short  *sptr;
        unsigned long   *lptr;
        offset_t        curoff, curioff;

        laststyle = style;
        lastpo = po;
        should_print = 0;
        if (count != 1) {
                if (clear) {
                        count = 1;
                        star = 0;
                        clear = 0;
                } else
                        clear = 1;
        }
        tcount = count;
        offset = blkoff(fs, addr);

        if (style == '/') {
                if (type == NUMB)
                        eof_flag = 0;
                else
                        eof_flag = 1;
                switch (po) {

                case 'c': /* print as characters */
                case 'b': /* or bytes */
                        if ((cptr = getblk(addr)) == 0)
                                return;
                        cptr += offset;
                        objsz = CHAR;
                        tcount = check_addr(eof_flag, &end, &eof, 0);
                        if (tcount) {
                                for (i = 0; tcount--; i++) {
                                        if (i % 16 == 0) {
                                                if (i)
                                                        printf("\n");
                                                index(base);
                                        }
                                        if (po == 'c') {
                                                putf(*cptr++);
                                                if ((i + 1) % 16)
                                                        printf("  ");
                                        } else {
                                                if ((i + 1) % 16 == 0)
                                                        print(*cptr++ & 0377L,
                                                                2, -2, 0);
                                                else
                                                        print(*cptr++ & 0377L,
                                                                4, -2, 0);
                                        }
                                        addr += CHAR;
                                        cur_bytes += CHAR;
                                }
                                printf("\n");
                        }
                        addr -= CHAR;
                        erraddr = addr;
                        cur_bytes -= CHAR;
                        errcur_bytes = cur_bytes;
                        if (eof) {
                                printf("end of file\n");
                                error++;
                        } else if (end) {
                                if (type == BLOCK)
                                        printf("end of block\n");
                                else
                                        printf("end of fragment\n");
                                error++;
                        }
                        return;

                case 'o': /* print as octal shorts */
                        tbase = OCTAL;
                        goto otx;
                case 'd': /* print as decimal shorts */
                        tbase = DECIMAL;
                        goto otx;
                case 'x': /* print as hex shorts */
                        tbase = HEX;
otx:
                        if ((cptr = getblk(addr)) == 0)
                                return;
                        taddr = addr;
                        addr &= ~(SHORT - 1);
                        cur_bytes -= taddr - addr;
                        cptr += blkoff(fs, addr);
                        /*LINTED*/
                        sptr = (unsigned short *)cptr;
                        objsz = SHORT;
                        tcount = check_addr(eof_flag, &end, &eof, 0);
                        if (tcount) {
                                for (i = 0; tcount--; i++) {
                                        sptr = (unsigned short *)print_check(
                                                        /*LINTED*/
                                                        (unsigned long *)sptr,
                                                        &tcount, tbase, i);
                                        switch (po) {
                                        case 'o':
                                                printf("%06o ", *sptr++);
                                                break;
                                        case 'd':
                                                printf("%05d  ", *sptr++);
                                                break;
                                        case 'x':
                                                printf("%04x   ", *sptr++);
                                        }
                                        addr += SHORT;
                                        cur_bytes += SHORT;
                                }
                                printf("\n");
                        }
                        addr -= SHORT;
                        erraddr = addr;
                        cur_bytes -= SHORT;
                        errcur_bytes = cur_bytes;
                        if (eof) {
                                printf("end of file\n");
                                error++;
                        } else if (end) {
                                if (type == BLOCK)
                                        printf("end of block\n");
                                else
                                        printf("end of fragment\n");
                                error++;
                        }
                        return;

                case 'O': /* print as octal longs */
                        tbase = OCTAL;
                        goto OTX;
                case 'D': /* print as decimal longs */
                        tbase = DECIMAL;
                        goto OTX;
                case 'X': /* print as hex longs */
                        tbase = HEX;
OTX:
                        if ((cptr = getblk(addr)) == 0)
                                return;
                        taddr = addr;
                        addr &= ~(LONG - 1);
                        cur_bytes -= taddr - addr;
                        cptr += blkoff(fs, addr);
                        /*LINTED*/
                        lptr = (unsigned long *)cptr;
                        objsz = LONG;
                        tcount = check_addr(eof_flag, &end, &eof, 0);
                        if (tcount) {
                                for (i = 0; tcount--; i++) {
                                        lptr = print_check(lptr, &tcount,
                                                                tbase, i);
                                        switch (po) {
                                        case 'O':
                                                printf("%011lo    ", *lptr++);
                                                break;
                                        case 'D':
                                                printf("%010lu     ", *lptr++);
                                                break;
                                        case 'X':
                                                printf("%08lx       ", *lptr++);
                                        }
                                        addr += LONG;
                                        cur_bytes += LONG;
                                }
                                printf("\n");
                        }
                        addr -= LONG;
                        erraddr = addr;
                        cur_bytes -= LONG;
                        errcur_bytes = cur_bytes;
                        if (eof) {
                                printf("end of file\n");
                                error++;
                        } else if (end) {
                                if (type == BLOCK)
                                        printf("end of block\n");
                                else
                                        printf("end of fragment\n");
                                error++;
                        }
                        return;

                default:
                        error++;
                        printf("no such print option\n");
                        return;
                }
        } else
                switch (po) {

                case 'c': /* print as cylinder group */
                        if (type != NUMB)
                                if (cur_cgrp + count > fs->fs_ncg) {
                                        tcount = fs->fs_ncg - cur_cgrp;
                                        if (!star)
                                                end++;
                                }
                        addr &= ~(LONG - 1);
                        for (/* void */; tcount--; /* void */) {
                                erraddr = addr;
                                errcur_bytes = cur_bytes;
                                if (type != NUMB) {
                                        addr = cgtod(fs, cur_cgrp)
                                                << FRGSHIFT;
                                        cur_cgrp++;
                                }
                                if ((cptr = getblk(addr)) == 0) {
                                        if (cur_cgrp)
                                                cur_cgrp--;
                                        return;
                                }
                                cptr += blkoff(fs, addr);
                                /*LINTED*/
                                cg = (struct cg *)cptr;
                                if (type == NUMB) {
                                        cur_cgrp = cg->cg_cgx + 1;
                                        type = objsz = CGRP;
                                        if (cur_cgrp + count - 1 > fs->fs_ncg) {
                                                tcount = fs->fs_ncg - cur_cgrp;
                                                if (!star)
                                                        end++;
                                        }
                                }
                                if (! override && !cg_chkmagic(cg)) {
                                        printf("invalid cylinder group ");
                                        printf("magic word\n");
                                        if (cur_cgrp)
                                                cur_cgrp--;
                                        error++;
                                        return;
                                }
                                printcg(cg);
                                if (tcount)
                                        printf("\n");
                        }
                        cur_cgrp--;
                        if (end) {
                                printf("end of cylinder groups\n");
                                error++;
                        }
                        return;

                case 'd': /* print as directories */
                        if ((cptr = getblk(addr)) == 0)
                                return;
                        if (type == NUMB) {
                                if (fragoff(fs, addr)) {
                                        printf("address must be at the ");
                                        printf("beginning of a fragment\n");
                                        error++;
                                        return;
                                }
                                bod_addr = addr;
                                type = FRAGMENT;
                                dirslot = 0;
                                cur_bytes = 0;
                                blocksize = FRGSIZE;
                                filesize = FRGSIZE * 2;
                        }
                        cptr += offset;
                        objsz = DIRECTORY;
                        while (tcount-- && cur_bytes < filesize &&
                                cur_bytes < blocksize && !bcomp(addr)) {
                                /*LINTED*/
                                dirp = (struct direct *)cptr;
                                tinode = dirp->d_ino;
                                printf("i#: ");
                                if (tinode == 0)
                                        printf("free\t");
                                else
                                        print(tinode, 12, -8, 0);
                                printf("%s\n", &dirp->d_name[0]);
                                erraddr = addr;
                                errcur_bytes = cur_bytes;
                                addr += dirp->d_reclen;
                                cptr += dirp->d_reclen;
                                cur_bytes += dirp->d_reclen;
                                dirslot++;
                                stringsize = STRINGSIZE(dirp);
                        }
                        addr = erraddr;
                        cur_dir = addr;
                        cur_bytes = errcur_bytes;
                        dirslot--;
                        if (tcount >= 0 && !star) {
                                switch (type) {
                                case FRAGMENT:
                                        printf("end of fragment\n");
                                        break;
                                case BLOCK:
                                        printf("end of block\n");
                                        break;
                                default:
                                        printf("end of directory\n");
                                }
                                error++;
                        } else
                                error = 0;
                        return;

                case 'i': /* print as inodes */
                        /*LINTED*/
                        if ((ip = (struct dinode *)getblk(addr)) == 0)
                                return;
                        for (i = 1; i < fs->fs_ncg; i++)
                                if (addr < (cgimin(fs, i) << FRGSHIFT))
                                        break;
                        i--;
                        offset /= INODE;
                        temp = (addr - (cgimin(fs, i) << FRGSHIFT)) >> FRGSHIFT;
                        temp = (i * fs->fs_ipg) + fragstoblks(fs, temp) *
                                                        INOPB(fs) + offset;
                        if (count + offset > INOPB(fs)) {
                                tcount = INOPB(fs) - offset;
                                if (!star)
                                        end++;
                        }
                        objsz = INODE;
                        ip += offset;
                        for (i = 0; tcount--; ip++, temp++) {
                                if ((mode = icheck(addr)) == 0)
                                        if (!override)
                                                continue;
                                p = " ugtrwxrwxrwx";

                                switch (mode & IFMT) {
                                case IFDIR:
                                        c = 'd';
                                        break;
                                case IFCHR:
                                        c = 'c';
                                        break;
                                case IFBLK:
                                        c = 'b';
                                        break;
                                case IFREG:
                                        c = '-';
                                        break;
                                case IFLNK:
                                        c = 'l';
                                        break;
                                case IFSOCK:
                                        c = 's';
                                        break;
                                case IFSHAD:
                                        c = 'S';
                                        break;
                                case IFATTRDIR:
                                        c = 'A';
                                        break;
                                default:
                                        c = '?';
                                        if (!override)
                                                goto empty;

                                }
                                printf("i#: ");
                                print(temp, 12, -8, 0);
                                printf("   md: ");
                                printf("%c", c);
                                for (mode = mode << 4; *++p; mode = mode << 1) {
                                        if (mode & IFREG)
                                                printf("%c", *p);
                                        else
                                                printf("-");
                                }
                                printf("  uid: ");
                                print(ip->di_uid, 8, -4, 0);
                                printf("      gid: ");
                                print(ip->di_gid, 8, -4, 0);
                                printf("\n");
                                printf("ln: ");
                                print((long)ip->di_nlink, 8, -4, 0);
                                printf("       bs: ");
                                print(ip->di_blocks, 12, -8, 0);
                                printf("c_flags : ");
                                print(ip->di_cflags, 12, -8, 0);
                                printf("   sz : ");
#ifdef _LARGEFILE64_SOURCE
                                printll(ip->di_size, 20, -16, 0);
#else /* !_LARGEFILE64_SOURCE */
                                print(ip->di_size, 12, -8, 0);
#endif /* _LARGEFILE64_SOURCE */
                                if (ip->di_shadow) {
                                        printf("   si: ");
                                        print(ip->di_shadow, 12, -8, 0);
                                }
                                printf("\n");
                                if (ip->di_oeftflag) {
                                        printf("ai: ");
                                        print(ip->di_oeftflag, 12, -8, 0);
                                        printf("\n");
                                }
                                printf("\n");
                                switch (ip->di_mode & IFMT) {
                                case IFBLK:
                                case IFCHR:
                                        printf("maj: ");
                                        print(major(ip->di_ordev), 4, -2, 0);
                                        printf("  min: ");
                                        print(minor(ip->di_ordev), 4, -2, 0);
                                        printf("\n");
                                        break;
                                default:
                                        /*
                                         * only display blocks below the
                                         * current file size
                                         */
                                        curoff = 0LL;
                                        for (i = 0; i < NDADDR; ) {
                                                if (ip->di_size <= curoff)
                                                        break;
                                                printf("db#%x: ", i);
                                                print(ip->di_db[i], 11, -8, 0);

                                                if (++i % 4 == 0)
                                                        printf("\n");
                                                else
                                                        printf("  ");
                                                curoff += fs->fs_bsize;
                                        }
                                        if (i % 4)
                                                printf("\n");

                                        /*
                                         * curioff keeps track of the number
                                         * of bytes covered by each indirect
                                         * pointer in the inode, and is added
                                         * to curoff each time to get the
                                         * actual offset into the file.
                                         */
                                        curioff = fs->fs_bsize *
                                            (fs->fs_bsize / sizeof (daddr_t));
                                        for (i = 0; i < NIADDR; i++) {
                                                if (ip->di_size <= curoff)
                                                        break;
                                                printf("ib#%x: ", i);
                                                print(ip->di_ib[i], 11, -8, 0);
                                                printf("  ");
                                                curoff += curioff;
                                                curioff *= (fs->fs_bsize /
                                                    sizeof (daddr_t));
                                        }
                                        if (i)
                                                printf("\n");
                                        break;
                                }
                                if (count == 1) {
                                        time_t t;

                                        t = ip->di_atime;
                                        printf("\taccessed: %s", ctime(&t));
                                        t = ip->di_mtime;
                                        printf("\tmodified: %s", ctime(&t));
                                        t = ip->di_ctime;
                                        printf("\tcreated : %s", ctime(&t));
                                }
                                if (tcount)
                                        printf("\n");
empty:
                                if (c == '?' && !override) {
                                        printf("i#: ");
                                        print(temp, 12, -8, 0);
                                        printf("  is unallocated\n");
                                        if (count != 1)
                                                printf("\n");
                                }
                                cur_ino = erraddr = addr;
                                errcur_bytes = cur_bytes;
                                cur_inum++;
                                addr = addr + INODE;
                        }
                        addr = erraddr;
                        cur_bytes = errcur_bytes;
                        cur_inum--;
                        if (end) {
                                printf("end of block\n");
                                error++;
                        }
                        return;

                case 's': /* print as super block */
                        if (cur_cgrp == -1) {
                                addr = SBLOCK * DEV_BSIZE;
                                type = NUMB;
                        }
                        addr &= ~(LONG - 1);
                        if (type != NUMB)
                                if (cur_cgrp + count > fs->fs_ncg) {
                                        tcount = fs->fs_ncg - cur_cgrp;
                                        if (!star)
                                                end++;
                                }
                        for (/* void */; tcount--; /* void */) {
                                erraddr = addr;
                                cur_bytes = errcur_bytes;
                                if (type != NUMB) {
                                        addr = cgsblock(fs, cur_cgrp)
                                                        << FRGSHIFT;
                                        cur_cgrp++;
                                }
                                if ((cptr = getblk(addr)) == 0) {
                                        if (cur_cgrp)
                                                cur_cgrp--;
                                        return;
                                }
                                cptr += blkoff(fs, addr);
                                /*LINTED*/
                                sb = (struct fs *)cptr;
                                if (type == NUMB) {
                                        for (i = 0; i < fs->fs_ncg; i++)
                                                if (addr == cgsblock(fs, i) <<
                                                                FRGSHIFT)
                                                        break;
                                        if (i == fs->fs_ncg)
                                                cur_cgrp = 0;
                                        else
                                                cur_cgrp = i + 1;
                                        type = objsz = SB;
                                        if (cur_cgrp + count - 1 > fs->fs_ncg) {
                                                tcount = fs->fs_ncg - cur_cgrp;
                                                if (!star)
                                                        end++;
                                        }
                                }
                                if ((sb->fs_magic != FS_MAGIC) &&
                                    (sb->fs_magic != MTB_UFS_MAGIC)) {
                                        cur_cgrp = 0;
                                        if (!override) {
                                                printf("invalid super block ");
                                                printf("magic word\n");
                                                cur_cgrp--;
                                                error++;
                                                return;
                                        }
                                }
                                if (sb->fs_magic == FS_MAGIC &&
                                    (sb->fs_version !=
                                        UFS_EFISTYLE4NONEFI_VERSION_2 &&
                                    sb->fs_version != UFS_VERSION_MIN)) {
                                        cur_cgrp = 0;
                                        if (!override) {
                                                printf("invalid super block ");
                                                printf("version number\n");
                                                cur_cgrp--;
                                                error++;
                                                return;
                                        }
                                }
                                if (sb->fs_magic == MTB_UFS_MAGIC &&
                                    (sb->fs_version > MTB_UFS_VERSION_1 ||
                                    sb->fs_version < MTB_UFS_VERSION_MIN)) {
                                        cur_cgrp = 0;
                                        if (!override) {
                                                printf("invalid super block ");
                                                printf("version number\n");
                                                cur_cgrp--;
                                                error++;
                                                return;
                                        }
                                }
                                if (cur_cgrp == 0)
                                        printf("\tsuper block:\n");
                                else {
                                        printf("\tsuper block in cylinder ");
                                        printf("group ");
                                        print(cur_cgrp - 1, 0, 0, 0);
                                        printf(":\n");
                                }
                                printsb(sb);
                                if (tcount)
                                        printf("\n");
                        }
                        cur_cgrp--;
                        if (end) {
                                printf("end of super blocks\n");
                                error++;
                        }
                        return;

                case 'S': /* print as shadow data */
                        if (type == NUMB) {
                                type = FRAGMENT;
                                cur_shad = 0;
                                cur_bytes = fragoff(fs, addr);
                                bod_addr = addr - cur_bytes;
                                /* no more than two fragments */
                                filesize = fragroundup(fs,
                                    bod_addr + FRGSIZE + 1);
                        }
                        objsz = SHADOW_DATA;
                        while (tcount-- &&
                            (cur_bytes + SHADOW_DATA) <= filesize &&
                            (type != SHADOW_DATA ||
                            (cur_bytes + SHADOW_DATA)) <= blocksize) {
                                /*LINTED*/
                                struct ufs_fsd fsd;
                                long tcur_bytes;

                                taddr = addr;
                                tcur_bytes = cur_bytes;
                                index(base);
                                getshadowdata((long *)&fsd, LONG + LONG);
                                printf("  type: ");
                                print((long)fsd.fsd_type, 8, -8, 0);
                                printf("  size: ");
                                print((long)fsd.fsd_size, 8, -8, 0);
                                tbase = fsd.fsd_size - LONG - LONG;
                                if (tbase > 256)
                                        tbase = 256;
                                for (i = 0; i < tbase; i++) {
                                        if (i % LONG == 0) {
                                                if (i % 16 == 0) {
                                                        printf("\n");
                                                        index(base);
                                                } else
                                                        printf("  ");
                                                getshadowdata(&temp, LONG);
                                                p = (char *)&temp;
                                        } else
                                                printf(" ");
                                        printf("%02x", (int)(*p++ & 0377L));
                                }
                                printf("\n");
                                addr = taddr;
                                cur_bytes = tcur_bytes;
                                erraddr = addr;
                                errcur_bytes = cur_bytes;
                                addr += FSD_RECSZ((&fsd), fsd.fsd_size);
                                cur_bytes += FSD_RECSZ((&fsd), fsd.fsd_size);
                                cur_shad++;
                                syncshadowscan(0);
                        }
                        addr = erraddr;
                        cur_bytes = errcur_bytes;
                        cur_shad--;
                        if (tcount >= 0 && !star) {
                                switch (type) {
                                case FRAGMENT:
                                        printf("end of fragment\n");
                                        break;
                                default:
                                        printf("end of shadow data\n");
                                }
                                error++;
                        } else
                                error = 0;
                        return;
                default:
                        error++;
                        printf("no such print option\n");
                        return;
                }
}

/*
 * valid_addr - call check_addr to validate the current address.
 */
static int
valid_addr()
{
        short   end = 0, eof = 0;
        long    tcount = count;

        if (!trapped)
                return (1);
        if (cur_bytes < 0) {
                cur_bytes = 0;
                if (blocksize > filesize) {
                        printf("beginning of file\n");
                } else {
                        if (type == BLOCK)
                                printf("beginning of block\n");
                        else
                                printf("beginning of fragment\n");
                }
                error++;
                return (0);
        }
        count = 1;
        (void) check_addr(1, &end, &eof, (filesize < blocksize));
        count = tcount;
        if (eof) {
                printf("end of file\n");
                error++;
                return (0);
        }
        if (end == 2) {
                if (erraddr > addr) {
                        if (type == BLOCK)
                                printf("beginning of block\n");
                        else
                                printf("beginning of fragment\n");
                        error++;
                        return (0);
                }
        }
        if (end) {
                if (type == BLOCK)
                        printf("end of block\n");
                else
                        printf("end of fragment\n");
                error++;
                return (0);
        }
        return (1);
}

/*
 * check_addr - check if the address crosses the end of block or
 *      end of file.  Return the proper count.
 */
static int
check_addr(short eof_flag, short *end, short *eof, short keep_on)
{
        long    temp, tcount = count, tcur_bytes = cur_bytes;
        u_offset_t      taddr = addr;

        if (bcomp(addr + count * objsz - 1) ||
            (keep_on && taddr < (bmap(cur_block) << FRGSHIFT))) {
                error = 0;
                addr = taddr;
                cur_bytes = tcur_bytes;
                if (keep_on) {
                        if (addr < erraddr) {
                                if (cur_bytes < 0) {
                                        (*end) = 2;
                                        return (0);     /* Value ignored */
                                }
                                temp = cur_block - lblkno(fs, cur_bytes);
                                cur_block -= temp;
                                if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
                                        cur_block += temp;
                                        return (0);     /* Value ignored */
                                }
                                temp = tcur_bytes - cur_bytes;
                                addr += temp;
                                cur_bytes += temp;
                                return (0);     /* Value ignored */
                        } else {
                                if (cur_bytes >= filesize) {
                                        (*eof)++;
                                        return (0);     /* Value ignored */
                                }
                                temp = lblkno(fs, cur_bytes) - cur_block;
                                cur_block += temp;
                                if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
                                        cur_block -= temp;
                                        return (0);     /* Value ignored */
                                }
                                temp = tcur_bytes - cur_bytes;
                                addr += temp;
                                cur_bytes += temp;
                                return (0);     /* Value ignored */
                        }
                }
                tcount = (blkroundup(fs, addr+1)-addr) / objsz;
                if (!star)
                        (*end) = 2;
        }
        addr = taddr;
        cur_bytes = tcur_bytes;
        if (eof_flag) {
                if (blocksize > filesize) {
                        if (cur_bytes >= filesize) {
                                tcount = 0;
                                (*eof)++;
                        } else if (tcount > (filesize - cur_bytes) / objsz) {
                                tcount = (filesize - cur_bytes) / objsz;
                                if (!star || tcount == 0)
                                        (*eof)++;
                        }
                } else {
                        if (cur_bytes >= blocksize) {
                                tcount = 0;
                                (*end)++;
                        } else if (tcount > (blocksize - cur_bytes) / objsz) {
                                tcount = (blocksize - cur_bytes) / objsz;
                                if (!star || tcount == 0)
                                        (*end)++;
                        }
                }
        }
        return (tcount);
}

/*
 * print_check - check if the index needs to be printed and delete
 *      rows of zeros from the output.
 */
unsigned long *
print_check(unsigned long *lptr, long *tcount, short tbase, int i)
{
        int             j, k, temp = BYTESPERLINE / objsz;
        short           first_time = 0;
        unsigned long   *tlptr;
        unsigned short  *tsptr, *sptr;

        sptr = (unsigned short *)lptr;
        if (i == 0)
                first_time = 1;
        if (i % temp == 0) {
                if (*tcount >= temp - 1) {
                        if (objsz == SHORT)
                                tsptr = sptr;
                        else
                                tlptr = lptr;
                        k = *tcount - 1;
                        for (j = i; k--; j++)
                                if (objsz == SHORT) {
                                        if (*tsptr++ != 0)
                                                break;
                                } else {
                                        if (*tlptr++ != 0)
                                                break;
                                }
                        if (j > (i + temp - 1)) {
                                j = (j - i) / temp;
                                while (j-- > 0) {
                                        if (objsz == SHORT)
                                                sptr += temp;
                                        else
                                                lptr += temp;
                                        *tcount -= temp;
                                        i += temp;
                                        addr += BYTESPERLINE;
                                        cur_bytes += BYTESPERLINE;
                                }
                                if (first_time)
                                        printf("*");
                                else
                                        printf("\n*");
                        }
                        if (i)
                                printf("\n");
                        index(tbase);
                } else {
                        if (i)
                                printf("\n");
                        index(tbase);
                }
        }
        if (objsz == SHORT)
                /*LINTED*/
                return ((unsigned long *)sptr);
        else
                return (lptr);
}

/*
 * index - print a byte index for the printout in base b
 *      with leading zeros.
 */
static void
index(int b)
{
        int     tbase = base;

        base = b;
        print(addr, 8, 8, 1);
        printf(":\t");
        base = tbase;
}

/*
 * print - print out the value to digits places with/without
 *      leading zeros and right/left justified in the current base.
 */
static void
#ifdef _LARGEFILE64_SOURCE
printll(u_offset_t value, int fieldsz, int digits, int lead)
#else /* !_LARGEFILE64_SOURCE */
print(long value, int fieldsz, int digits, int lead)
#endif /* _LARGEFILE64_SOURCE */
{
        int     i, left = 0;
        char    mode = BASE[base - OCTAL];
        char    *string = &scratch[0];

        if (digits < 0) {
                left = 1;
                digits *= -1;
        }
        if (base != HEX)
                if (digits)
                        digits = digits + (digits - 1)/((base >> 1) - 1) + 1;
                else
                        digits = 1;
        if (lead) {
                if (left)
                        (void) sprintf(string, "%%%c%d%d.%d"
#ifdef _LARGEFILE64_SOURCE
                                "ll"
#endif /* _LARGEFILE64_SOURCE */
                                "%c", '-', 0, digits, lead, mode);
                else
                        (void) sprintf(string, "%%%d%d.%d"
#ifdef _LARGEFILE64_SOURCE
                                "ll"
#endif /* _LARGEFILE64_SOURCE */
                                "%c", 0, digits, lead, mode);
        } else {
                if (left)
                        (void) sprintf(string, "%%%c%d"
#ifdef _LARGEFILE64_SOURCE
                                "ll"
#endif /* _LARGEFILE64_SOURCE */
                                "%c", '-', digits, mode);
                else
                        (void) sprintf(string, "%%%d"
#ifdef _LARGEFILE64_SOURCE
                                "ll"
#endif /* _LARGEFILE64_SOURCE */
                                "%c", digits, mode);
        }
        printf(string, value);
        for (i = 0; i < fieldsz - digits; i++)
                printf(" ");
}

/*
 * Print out the contents of a superblock.
 */
static void
printsb(struct fs *fs)
{
        int c, i, j, k, size;
        caddr_t sip;
        time_t t;

        t = fs->fs_time;
#ifdef FS_42POSTBLFMT
        if (fs->fs_postblformat == FS_42POSTBLFMT)
                fs->fs_nrpos = 8;
        printf("magic\t%lx\tformat\t%s\ttime\t%s", fs->fs_magic,
            fs->fs_postblformat == FS_42POSTBLFMT ? "static" : "dynamic",
            ctime(&t));
#else
        printf("magic\t%x\ttime\t%s",
            fs->fs_magic, ctime(&t));
#endif
        printf("version\t%x\n", fs->fs_version);
        printf("nbfree\t%ld\tndir\t%ld\tnifree\t%ld\tnffree\t%ld\n",
            fs->fs_cstotal.cs_nbfree, fs->fs_cstotal.cs_ndir,
            fs->fs_cstotal.cs_nifree, fs->fs_cstotal.cs_nffree);
        printf("ncg\t%ld\tncyl\t%ld\tsize\t%ld\tblocks\t%ld\n",
            fs->fs_ncg, fs->fs_ncyl, fs->fs_size, fs->fs_dsize);
        printf("bsize\t%ld\tshift\t%ld\tmask\t0x%08lx\n",
            fs->fs_bsize, fs->fs_bshift, fs->fs_bmask);
        printf("fsize\t%ld\tshift\t%ld\tmask\t0x%08lx\n",
            fs->fs_fsize, fs->fs_fshift, fs->fs_fmask);
        printf("frag\t%ld\tshift\t%ld\tfsbtodb\t%ld\n",
            fs->fs_frag, fs->fs_fragshift, fs->fs_fsbtodb);
        printf("cpg\t%ld\tbpg\t%ld\tfpg\t%ld\tipg\t%ld\n",
            fs->fs_cpg, fs->fs_fpg / fs->fs_frag, fs->fs_fpg, fs->fs_ipg);
        printf("minfree\t%ld%%\toptim\t%s\tmaxcontig %ld\tmaxbpg\t%ld\n",
            fs->fs_minfree, fs->fs_optim == FS_OPTSPACE ? "space" : "time",
            fs->fs_maxcontig, fs->fs_maxbpg);
#ifdef FS_42POSTBLFMT
#ifdef sun
        printf("rotdelay %ldms\tfs_id[0] 0x%lx\tfs_id[1] 0x%lx\trps\t%ld\n",
            fs->fs_rotdelay, fs->fs_id[0], fs->fs_id[1], fs->fs_rps);
#else
        printf("rotdelay %dms\theadswitch %dus\ttrackseek %dus\trps\t%d\n",
            fs->fs_rotdelay, fs->fs_headswitch, fs->fs_trkseek, fs->fs_rps);
#endif /* sun */
        printf("ntrak\t%ld\tnsect\t%ld\tnpsect\t%ld\tspc\t%ld\n",
            fs->fs_ntrak, fs->fs_nsect, fs->fs_npsect, fs->fs_spc);
        printf("trackskew %ld\n", fs->fs_trackskew);
#else
        printf("rotdelay %ldms\trps\t%ld\n",
            fs->fs_rotdelay, fs->fs_rps);
        printf("ntrak\t%ld\tnsect\t%ld\tspc\t%ld\n",
            fs->fs_ntrak, fs->fs_nsect, fs->fs_spc);
#endif
        printf("si %ld\n", fs->fs_si);
        printf("nindir\t%ld\tinopb\t%ld\tnspf\t%ld\n",
            fs->fs_nindir, fs->fs_inopb, fs->fs_nspf);
        printf("sblkno\t%ld\tcblkno\t%ld\tiblkno\t%ld\tdblkno\t%ld\n",
            fs->fs_sblkno, fs->fs_cblkno, fs->fs_iblkno, fs->fs_dblkno);
        printf("sbsize\t%ld\tcgsize\t%ld\tcgoffset %ld\tcgmask\t0x%08lx\n",
            fs->fs_sbsize, fs->fs_cgsize, fs->fs_cgoffset, fs->fs_cgmask);
        printf("csaddr\t%ld\tcssize\t%ld\tshift\t%ld\tmask\t0x%08lx\n",
            fs->fs_csaddr, fs->fs_cssize, fs->fs_csshift, fs->fs_csmask);
        printf("cgrotor\t%ld\tfmod\t%d\tronly\t%d\n",
            fs->fs_cgrotor, fs->fs_fmod, fs->fs_ronly);
#ifdef FS_42POSTBLFMT
        if (fs->fs_cpc != 0)
                printf("blocks available in each of %ld rotational positions",
                        fs->fs_nrpos);
        else
                printf("insufficient space to maintain rotational tables\n");
#endif
        for (c = 0; c < fs->fs_cpc; c++) {
                printf("\ncylinder number %d:", c);
#ifdef FS_42POSTBLFMT
                for (i = 0; i < fs->fs_nrpos; i++) {
                        /*LINTED*/
                        if (fs_postbl(fs, c)[i] == -1)
                                continue;
                        printf("\n   position %d:\t", i);
                        /*LINTED*/
                        for (j = fs_postbl(fs, c)[i], k = 1; /* void */;
                                                j += fs_rotbl(fs)[j], k++) {
                                printf("%5d", j);
                                if (k % 12 == 0)
                                        printf("\n\t\t");
                                if (fs_rotbl(fs)[j] == 0)
                                        break;
                        }
                }
#else
                for (i = 0; i < NRPOS; i++) {
                        if (fs->fs_postbl[c][i] == -1)
                                continue;
                        printf("\n   position %d:\t", i);
                        for (j = fs->fs_postbl[c][i], k = 1; /* void */;
                                                j += fs->fs_rotbl[j], k++) {
                                printf("%5d", j);
                                if (k % 12 == 0)
                                        printf("\n\t\t");
                                if (fs->fs_rotbl[j] == 0)
                                        break;
                        }
                }
#endif
        }
        printf("\ncs[].cs_(nbfree, ndir, nifree, nffree):");
        sip = calloc(1, fs->fs_cssize);
        fs->fs_u.fs_csp = (struct csum *)sip;
        for (i = 0, j = 0; i < fs->fs_cssize; i += fs->fs_bsize, j++) {
                size = fs->fs_cssize - i < fs->fs_bsize ?
                    fs->fs_cssize - i : fs->fs_bsize;
                (void) llseek(fd,
                        (offset_t)fsbtodb(fs, (fs->fs_csaddr + j * fs->fs_frag))
                                * fs->fs_fsize / fsbtodb(fs, 1), 0);
                if (read(fd, sip, size) != size) {
                        free(fs->fs_u.fs_csp);
                        return;
                }
                sip += size;
        }
        for (i = 0; i < fs->fs_ncg; i++) {
                struct csum *cs = &fs->fs_cs(fs, i);
                if (i % 4 == 0)
                        printf("\n     ");
                printf("%d:(%ld,%ld,%ld,%ld) ", i, cs->cs_nbfree, cs->cs_ndir,
                                                cs->cs_nifree, cs->cs_nffree);
        }
        free(fs->fs_u.fs_csp);
        printf("\n");
        if (fs->fs_ncyl % fs->fs_cpg) {
                printf("cylinders in last group %d\n",
                    i = fs->fs_ncyl % fs->fs_cpg);
                printf("blocks in last group %ld\n",
                    i * fs->fs_spc / NSPB(fs));
        }
}

/*
 * Print out the contents of a cylinder group.
 */
static void
printcg(struct cg *cg)
{
        int i, j;
        time_t t;

        printf("\ncg %ld:\n", cg->cg_cgx);
        t = cg->cg_time;
#ifdef FS_42POSTBLFMT
        printf("magic\t%lx\ttell\t%llx\ttime\t%s",
            fs->fs_postblformat == FS_42POSTBLFMT ?
            ((struct ocg *)cg)->cg_magic : cg->cg_magic,
            fsbtodb(fs, cgtod(fs, cg->cg_cgx)) * fs->fs_fsize / fsbtodb(fs, 1),
            ctime(&t));
#else
        printf("magic\t%x\ttell\t%llx\ttime\t%s",
            cg->cg_magic,
            fsbtodb(fs, cgtod(fs, cg->cg_cgx)) * fs->fs_fsize / fsbtodb(fs, 1),
            ctime(&t));
#endif
        printf("cgx\t%ld\tncyl\t%d\tniblk\t%d\tndblk\t%ld\n",
            cg->cg_cgx, cg->cg_ncyl, cg->cg_niblk, cg->cg_ndblk);
        printf("nbfree\t%ld\tndir\t%ld\tnifree\t%ld\tnffree\t%ld\n",
            cg->cg_cs.cs_nbfree, cg->cg_cs.cs_ndir,
            cg->cg_cs.cs_nifree, cg->cg_cs.cs_nffree);
        printf("rotor\t%ld\tirotor\t%ld\tfrotor\t%ld\nfrsum",
            cg->cg_rotor, cg->cg_irotor, cg->cg_frotor);
        for (i = 1, j = 0; i < fs->fs_frag; i++) {
                printf("\t%ld", cg->cg_frsum[i]);
                j += i * cg->cg_frsum[i];
        }
        printf("\nsum of frsum: %d\niused:\t", j);
        pbits((unsigned char *)cg_inosused(cg), fs->fs_ipg);
        printf("free:\t");
        pbits(cg_blksfree(cg), fs->fs_fpg);
        printf("b:\n");
        for (i = 0; i < fs->fs_cpg; i++) {
                /*LINTED*/
                if (cg_blktot(cg)[i] == 0)
                        continue;
                /*LINTED*/
                printf("   c%d:\t(%ld)\t", i, cg_blktot(cg)[i]);
#ifdef FS_42POSTBLFMT
                for (j = 0; j < fs->fs_nrpos; j++) {
                        if (fs->fs_cpc == 0 ||
                                /*LINTED*/
                            fs_postbl(fs, i % fs->fs_cpc)[j] == -1)
                                continue;
                        /*LINTED*/
                        printf(" %d", cg_blks(fs, cg, i)[j]);
                }
#else
                for (j = 0; j < NRPOS; j++) {
                        if (fs->fs_cpc == 0 ||
                            fs->fs_postbl[i % fs->fs_cpc][j] == -1)
                                continue;
                        printf(" %d", cg->cg_b[i][j]);
                }
#endif
                printf("\n");
        }
}

/*
 * Print out the contents of a bit array.
 */
static void
pbits(unsigned char *cp, int max)
{
        int i;
        int count = 0, j;

        for (i = 0; i < max; i++)
                if (isset(cp, i)) {
                        if (count)
                                printf(",%s", count % 6 ? " " : "\n\t");
                        count++;
                        printf("%d", i);
                        j = i;
                        while ((i+1) < max && isset(cp, i+1))
                                i++;
                        if (i != j)
                                printf("-%d", i);
                }
        printf("\n");
}

/*
 * bcomp - used to check for block over/under flows when stepping through
 *      a file system.
 */
static int
bcomp(addr)
        u_offset_t      addr;
{
        if (override)
                return (0);

        if (lblkno(fs, addr) == (bhdr.fwd)->blkno)
                return (0);
        error++;
        return (1);
}

/*
 * bmap - maps the logical block number of a file into
 *      the corresponding physical block on the file
 *      system.
 */
static long
bmap(long bn)
{
        int             j;
        struct dinode   *ip;
        int             sh;
        long            nb;
        char            *cptr;

        if ((cptr = getblk(cur_ino)) == 0)
                return (0);

        cptr += blkoff(fs, cur_ino);

        /*LINTED*/
        ip = (struct dinode *)cptr;

        if (bn < NDADDR) {
                nb = ip->di_db[bn];
                return (nullblk(nb) ? 0L : nb);
        }

        sh = 1;
        bn -= NDADDR;
        for (j = NIADDR; j > 0; j--) {
                sh *= NINDIR(fs);
                if (bn < sh)
                        break;
                bn -= sh;
        }
        if (j == 0) {
                printf("file too big\n");
                error++;
                return (0L);
        }
        addr = (uintptr_t)&ip->di_ib[NIADDR - j];
        nb = get(LONG);
        if (nb == 0)
                return (0L);
        for (; j <= NIADDR; j++) {
                sh /= NINDIR(fs);
                addr = (nb << FRGSHIFT) + ((bn / sh) % NINDIR(fs)) * LONG;
                if (nullblk(nb = get(LONG)))
                        return (0L);
        }
        return (nb);
}

#if defined(OLD_FSDB_COMPATIBILITY)

/*
 * The following are "tacked on" to support the old fsdb functionality
 * of clearing an inode. (All together now...) "It's better to use clri".
 */

#define ISIZE   (sizeof (struct dinode))
#define NI      (MAXBSIZE/ISIZE)


static struct   dinode  di_buf[NI];

static union {
        char            dummy[SBSIZE];
        struct fs       sblk;
} sb_un;

#define sblock sb_un.sblk

static void
old_fsdb(int inum, char *special)
{
        int             f;      /* File descriptor for "special" */
        int             j;
        int             status = 0;
        u_offset_t      off;
        long            gen;
        time_t          t;

        f = open(special, 2);
        if (f < 0) {
                perror("open");
                printf("cannot open %s\n", special);
                exit(31+4);
        }
        (void) llseek(f, (offset_t)SBLOCK * DEV_BSIZE, 0);
        if (read(f, &sblock, SBSIZE) != SBSIZE) {
                printf("cannot read %s\n", special);
                exit(31+4);
        }
        if (sblock.fs_magic != FS_MAGIC) {
                printf("bad super block magic number\n");
                exit(31+4);
        }
        if (inum == 0) {
                printf("%d: is zero\n", inum);
                exit(31+1);
        }
        off = (u_offset_t)fsbtodb(&sblock, itod(&sblock, inum)) * DEV_BSIZE;
        (void) llseek(f, off, 0);
        if (read(f, (char *)di_buf, sblock.fs_bsize) != sblock.fs_bsize) {
                printf("%s: read error\n", special);
                status = 1;
        }
        if (status)
                exit(31+status);

        /*
         * Update the time in superblock, so fsck will check this filesystem.
         */
        (void) llseek(f, (offset_t)(SBLOCK * DEV_BSIZE), 0);
        (void) time(&t);
        sblock.fs_time = (time32_t)t;
        if (write(f, &sblock, SBSIZE) != SBSIZE) {
                printf("cannot update %s\n", special);
                exit(35);
        }

        printf("clearing %u\n", inum);
        off = (u_offset_t)fsbtodb(&sblock, itod(&sblock, inum)) * DEV_BSIZE;
        (void) llseek(f, off, 0);
        read(f, (char *)di_buf, sblock.fs_bsize);
        j = itoo(&sblock, inum);
        gen = di_buf[j].di_gen;
        (void) memset((caddr_t)&di_buf[j], 0, ISIZE);
        di_buf[j].di_gen = gen + 1;
        (void) llseek(f, off, 0);
        write(f, (char *)di_buf, sblock.fs_bsize);
        exit(31+status);
}

static int
isnumber(char *s)
{
        int     c;

        if (s == NULL)
                return (0);
        while ((c = *s++) != '\0')
                if (c < '0' || c > '9')
                        return (0);
        return (1);
}
#endif /* OLD_FSDB_COMPATIBILITY */

enum boolean { True, False };
extent_block_t  *log_eb;
ml_odunit_t     *log_odi;
int             lufs_tid;       /* last valid TID seen */

/*
 * no single value is safe to use to indicate
 * lufs_tid being invalid so we need a
 * seperate variable.
 */
enum boolean    lufs_tid_valid;

/*
 * log_get_header_info - get the basic info of the logging filesystem
 */
int
log_get_header_info(void)
{
        char            *b;
        int             nb;

        /*
         * Mark the global tid as invalid everytime we're called to
         * prevent any false positive responses.
         */
        lufs_tid_valid = False;

        /*
         * See if we've already set up the header areas. The only problem
         * with this approach is we don't reread the on disk data though
         * it shouldn't matter since we don't operate on a live disk.
         */
        if ((log_eb != NULL) && (log_odi != NULL))
                return (1);

        /*
         * Either logging is disabled or we've not running 2.7.
         */
        if (fs->fs_logbno == 0) {
                printf("Logging doesn't appear to be enabled on this disk\n");
                return (0);
        }

        /*
         * To find the log we need to first pick up the block allocation
         * data. The block number for that data is fs_logbno in the
         * super block.
         */
        if ((b = getblk((u_offset_t)ldbtob(logbtodb(fs, fs->fs_logbno))))
            == 0) {
                printf("getblk() indicates an error with logging block\n");
                return (0);
        }

        /*
         * Next we need to figure out how big the extent data structure
         * really is. It can't be more then fs_bsize and you could just
         * allocate that but, why get sloppy.
         * 1 is subtracted from nextents because extent_block_t contains
         * a single extent_t itself.
         */
        log_eb = (extent_block_t *)b;
        if (log_eb->type != LUFS_EXTENTS) {
                printf("Extents block has invalid type (0x%x)\n",
                    log_eb->type);
                return (0);
        }
        nb = sizeof (extent_block_t) +
            (sizeof (extent_t) * (log_eb->nextents - 1));

        log_eb = (extent_block_t *)malloc(nb);
        if (log_eb == NULL) {
                printf("Failed to allocate memory for extent block log\n");
                return (0);
        }
        memcpy(log_eb, b, nb);

        if (log_eb->nextbno != 0)
                /*
                 * Currently, as of 11-Dec-1997 the field nextbno isn't
                 * implemented. If someone starts using this sucker we'd
                 * better warn somebody.
                 */
                printf("WARNING: extent block field nextbno is non-zero!\n");

        /*
         * Now read in the on disk log structure. This is always in the
         * first block of the first extent.
         */
        b = getblk((u_offset_t)ldbtob(logbtodb(fs, log_eb->extents[0].pbno)));
        log_odi = (ml_odunit_t *)malloc(sizeof (ml_odunit_t));
        if (log_odi == NULL) {
                free(log_eb);
                log_eb = NULL;
                printf("Failed to allocate memory for ondisk structure\n");
                return (0);
        }
        memcpy(log_odi, b, sizeof (ml_odunit_t));

        /*
         * Consistency checks.
         */
        if (log_odi->od_version != LUFS_VERSION_LATEST) {
                free(log_eb);
                log_eb = NULL;
                free(log_odi);
                log_odi = NULL;
                printf("Version mismatch in on-disk version of log data\n");
                return (0);
        } else if (log_odi->od_badlog) {
                printf("WARNING: Log was marked as bad\n");
        }

        return (1);
}

static void
log_display_header(void)
{
        int x;
        if (!log_get_header_info())
                /*
                 * No need to display anything here. The previous routine
                 * has already done so.
                 */
                return;

        if (fs->fs_magic == FS_MAGIC)
                printf("Log block number: 0x%x\n------------------\n",
                    fs->fs_logbno);
        else
                printf("Log frag number: 0x%x\n------------------\n",
                    fs->fs_logbno);
        printf("Extent Info\n\t# Extents  : %d\n\t# Bytes    : 0x%x\n",
            log_eb->nextents, log_eb->nbytes);
        printf("\tNext Block : 0x%x\n\tExtent List\n\t--------\n",
            log_eb->nextbno);
        for (x = 0; x < log_eb->nextents; x++)
                printf("\t  [%d] lbno 0x%08x pbno 0x%08x nbno 0x%08x\n",
                    x, log_eb->extents[x].lbno, log_eb->extents[x].pbno,
                    log_eb->extents[x].nbno);
        printf("\nOn Disk Info\n\tbol_lof    : 0x%08x\n\teol_lof    : 0x%08x\n",
            log_odi->od_bol_lof, log_odi->od_eol_lof);
        printf("\tlog_size   : 0x%08x\n",
            log_odi->od_logsize);
        printf("\thead_lof   : 0x%08x\tident : 0x%x\n",
            log_odi->od_head_lof, log_odi->od_head_ident);
        printf("\ttail_lof   : 0x%08x\tident : 0x%x\n\thead_tid   : 0x%08x\n",
            log_odi->od_tail_lof, log_odi->od_tail_ident, log_odi->od_head_tid);
        printf("\tcheck sum  : 0x%08x\n", log_odi->od_chksum);
        if (log_odi->od_chksum !=
            (log_odi->od_head_ident + log_odi->od_tail_ident))
                printf("bad checksum: found 0x%08x, should be 0x%08x\n",
                    log_odi->od_chksum,
                    log_odi->od_head_ident + log_odi->od_tail_ident);
        if (log_odi->od_head_lof == log_odi->od_tail_lof)
                printf("\t --- Log is empty ---\n");
}

/*
 * log_lodb -- logical log offset to disk block number
 */
int
log_lodb(u_offset_t off, diskaddr_t *pblk)
{
        uint32_t        lblk = (uint32_t)btodb(off);
        int     x;

        if (!log_get_header_info())
                /*
                 * No need to display anything here. The previous routine
                 * has already done so.
                 */
                return (0);

        for (x = 0; x < log_eb->nextents; x++)
                if ((lblk >= log_eb->extents[x].lbno) &&
                    (lblk < (log_eb->extents[x].lbno +
                        log_eb->extents[x].nbno))) {
                        *pblk = (diskaddr_t)lblk - log_eb->extents[x].lbno +
                                logbtodb(fs, log_eb->extents[x].pbno);
                        return (1);
                }
        return (0);
}

/*
 * String names for the enumerated types. These are only used
 * for display purposes.
 */
char *dt_str[] = {
        "DT_NONE", "DT_SB", "DT_CG", "DT_SI", "DT_AB",
        "DT_ABZERO", "DT_DIR", "DT_INODE", "DT_FBI",
        "DT_QR", "DT_COMMIT", "DT_CANCEL", "DT_BOT",
        "DT_EOT", "DT_UD", "DT_SUD", "DT_SHAD", "DT_MAX"
};

/*
 * log_read_log -- transfer information from the log and adjust offset
 */
int
log_read_log(u_offset_t *addr, caddr_t va, int nb, uint32_t *chk)
{
        int             xfer;
        caddr_t         bp;
        diskaddr_t      pblk;
        sect_trailer_t  *st;

        while (nb) {
                if (!log_lodb(*addr, &pblk)) {
                        printf("Invalid log offset\n");
                        return (0);
                }

                /*
                 * fsdb getblk() expects offsets not block number.
                 */
                if ((bp = getblk((u_offset_t)dbtob(pblk))) == NULL)
                        return (0);

                xfer = MIN(NB_LEFT_IN_SECTOR(*addr), nb);
                if (va != NULL) {
                        memcpy(va, bp + blkoff(fs, *addr), xfer);
                        va += xfer;
                }
                nb -= xfer;
                *addr += xfer;

                /*
                 * If the log offset is now at a sector trailer
                 * run the checks if requested.
                 */
                if (NB_LEFT_IN_SECTOR(*addr) == 0) {
                        if (chk != NULL) {
                                st = (sect_trailer_t *)
                                    (bp + blkoff(fs, *addr));
                                if (*chk != st->st_ident) {
                                        printf(
                        "Expected sector trailer id 0x%08x, but saw 0x%08x\n",
                                                *chk, st->st_ident);
                                        return (0);
                                } else {
                                        *chk = st->st_ident + 1;
                                        /*
                                         * We update the on disk structure
                                         * transaction ID each time we see
                                         * one. By comparing this value
                                         * to the last valid DT_COMMIT record
                                         * we can determine if our log is
                                         * completely valid.
                                         */
                                        log_odi->od_head_tid = st->st_tid;
                                }
                        }
                        *addr += sizeof (sect_trailer_t);
                }
                if ((int32_t)*addr == log_odi->od_eol_lof)
                        *addr = log_odi->od_bol_lof;
        }
        return (1);
}

u_offset_t
log_nbcommit(u_offset_t a)
{
        /*
         * Comments are straight from ufs_log.c
         *
         * log is the offset following the commit header. However,
         * if the commit header fell on the end-of-sector, then lof
         * has already been advanced to the beginning of the next
         * sector. So do nothgin. Otherwise, return the remaining
         * bytes in the sector.
         */
        if ((a & (DEV_BSIZE - 1)) == 0)
                return (0);
        else
                return (NB_LEFT_IN_SECTOR(a));
}

/*
 * log_show --  pretty print the deltas. The number of which is determined
 *              by the log_enum arg. If LOG_ALLDELTAS the routine, as the
 *              name implies dumps everything. If LOG_NDELTAS, the routine
 *              will print out "count" deltas starting at "addr". If
 *              LOG_CHECKSCAN then run through the log checking the st_ident
 *              for valid data.
 */
static void
log_show(enum log_enum l)
{
        struct delta    d;
        int32_t         bol, eol;
        int             x = 0;
        uint32_t        chk;

        if (!log_get_header_info())
                /*
                 * No need to display any error messages here. The previous
                 * routine has already done so.
                 */
                return;

        bol = log_odi->od_head_lof;
        eol = log_odi->od_tail_lof;
        chk = log_odi->od_head_ident;

        if (bol == eol) {
                if ((l == LOG_ALLDELTAS) || (l == LOG_CHECKSCAN)) {
                        printf("Empty log.\n");
                        return;
                } else
                        printf("WARNING: empty log. addr may generate bogus"
                            " information");
        }

        /*
         * Only reset the "addr" if we've been requested to show all
         * deltas in the log.
         */
        if ((l == LOG_ALLDELTAS) || (l == LOG_CHECKSCAN))
                addr = (u_offset_t)bol;

        if (l != LOG_CHECKSCAN) {
                printf("       Log Offset       Delta       Count     Type\n");
                printf("-----------------------------------------"
                        "-----------------\n");
        }

        while ((bol != eol) && ((l == LOG_ALLDELTAS) ||
            (l == LOG_CHECKSCAN) || count--)) {
                if (!log_read_log(&addr, (caddr_t)&d, sizeof (d),
                    ((l == LOG_ALLDELTAS) || (l == LOG_CHECKSCAN)) ?
                    &chk : NULL))
                        /*
                         * Two failures are possible. One from getblk()
                         * which prints out a message or when we've hit
                         * an invalid block which may or may not indicate
                         * an error
                         */
                        goto end_scan;

                if ((uint32_t)d.d_nb > log_odi->od_logsize) {
                        printf("Bad delta entry. size out of bounds\n");
                        return;
                }
                if (l != LOG_CHECKSCAN)
                        printf("[%04d]  %08x  %08x.%08x %08x  %s\n", x++, bol,
                            d.d_mof, d.d_nb,
                            dt_str[d.d_typ >= DT_MAX ? DT_MAX : d.d_typ]);

                switch (d.d_typ) {
                case DT_CANCEL:
                case DT_ABZERO:
                        /*
                         * These two deltas don't have log space
                         * associated with the entry even though
                         * d_nb is non-zero.
                         */
                        break;

                case DT_COMMIT:
                        /*
                         * Commit records have zero size yet, the
                         * rest of the current disk block is avoided.
                         */
                        addr += log_nbcommit(addr);
                        lufs_tid = log_odi->od_head_tid;
                        lufs_tid_valid = True;
                        break;

                default:
                        if (!log_read_log(&addr, NULL, d.d_nb,
                            ((l == LOG_ALLDELTAS) ||
                            (l == LOG_CHECKSCAN)) ? &chk : NULL))
                                goto end_scan;
                        break;
                }
                bol = (int32_t)addr;
        }

end_scan:
        if (lufs_tid_valid == True) {
                if (lufs_tid == log_odi->od_head_tid)
                        printf("scan -- okay\n");
                else
                        printf("scan -- some transactions have been lost\n");
        } else {
                printf("scan -- failed to find a single valid transaction\n");
                printf("        (possibly due to an empty log)\n");
        }
}