/*++
/* NAME
/*	ils 1
/* SUMMARY
/*	list inode information
/* SYNOPSIS
/*	\fBils\fR [\fB-hlouv\fR] \fIdevice\fR [\fIstart-stop\fR ...]
/* DESCRIPTION
/*	\fBils\fR opens the named \fIdevice\fR and lists inode information.
/*	When no inode numbers are given, \fBils\fR lists unlinked and/or
/*	unallocated inodes only. When explicit inode numbers or number
/*	ranges are given, \fBils\fR lists all specified inodes or all
/*	inodes within the specified ranges.
/*
/* 	Arguments:
/* .IP \fB-h\fR
/*	Do not prepend a time machine header to the output (see below).
/* .IP \fB-l\fR
/*	Show linked inodes, too.
/* .IP \fB-o\fR
/*	Show unlinked open files only.
/* .IP \fB-u\fR
/*	Show never-used inodes, too.
/* .IP \fB-v\fR
/*	Turn on verbose mode.
/* .IP \fIdevice\fR
/*	Regular file or special device containing a file system.
/*	With UNIX FFS, using a raw disk device gives better efficiency
/*	than using a block device.
/*	With LINUX ext2fs, \fBils\fR does not support access via the
/*	raw disk device interface.
/* .IP "\fIstart-stop\fR ..."
/*	Examine the specified inode number or number range. Either the
/*	\fIstart\fR or the \fI-stop\fR may be omitted.
/* .PP
/*	The output format is in time machine format, as described in
/*	tm-format(5). The output begins with a two-line header that
/*	describes the data origin, and is followed by a one-line header
/*	that lists the names of the data attributes that make up the
/*	remainder of the output:
/* .IP \fBinum\fR
/*	The inode number.
/* .IP \fBallocation\fR
/*	`a' for allocated inode, `f' for free inode.
/* .IP \fBuid\fR
/*	Owner user ID.
/* .IP \fBgid\fR
/*	Owner group ID.
/* .IP \fBm_time\fR
/*	UNIX time (seconds) of last file modification.
/* .IP \fBa_time\fR
/*	UNIX time (seconds) of last file access.
/* .IP \fBc_time\fR
/*	UNIX time (seconds) of last inode status change.
/* .IP \fBdtime\fR
/*	UNIX time (seconds) of file deletion (LINUX only).
/* .IP \fBmode\fR
/*	File type and permissions (octal).
/* .IP \fBlinks\fR
/*	Number of hard links.
/* .IP \fBsize\fR
/*	File size in bytes.
/* .IP \fBblock0,block1\fR
/*	The first two entries in the direct block address list.
/* BUGS
/*	\fBils\fR currently supports only one file system type,
/*	namely, the default file system type of the system it was
/*	compiled for.
/* LICENSE
/*	This software is distributed under the IBM Public License.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

#include "fs_tools.h"
#include "error.h"
#include "split_at.h"

/* atoinum - convert string to inode number */

INUM_T  atoinum(const char *str)
{
    char   *cp;
    INUM_T  inum;

    if (*str == 0)
	return (0);
    inum = STRTOUL(str, &cp, 0);
    if (*cp || cp == str)
	error("bad inode number: %s", str);
    return (inum);
}

/* usage - explain and terminate */

static void usage()
{
    error("usage: %s [-h (no header)] [-l (linked) [-o (unlinked open only)] [-u (unused)] [-v] device [inum... ]",
	  progname);
}

/* print_header - print time machine header */

static void print_header(void)
{
    char    hostnamebuf[BUFSIZ];
    unsigned long now;

    if (gethostname(hostnamebuf, sizeof(hostnamebuf) - 1) < 0)
	error("gethostname: %m");
    hostnamebuf[sizeof(hostnamebuf) - 1] = 0;
    now = time((time_t *) 0);

    /*
     * Identify table type and table origin.
     */
    printf("class|host|start_time\n");
    printf("ils|%s|%lu\n", hostnamebuf, now);

    /*
     * Identify the fields in the data that follow.
     */
    printf("inum|allocation|uid|gid|m_time|a_time|c_time");
#ifdef HAVE_DTIME
    printf("|dtime");
#endif
    printf("|mode|links|size|block0|block1\n");
}

/* print_inode - list generic inode */

static void print_inode(INUM_T inum, FS_INODE *fs_inode, int flags, char *ptr)
{
    int    *orig_flags = (int *) ptr;

    if (*orig_flags & FS_FLAG_TMHDR) {
	print_header();
	*orig_flags &= ~FS_FLAG_TMHDR;
    }
    printf("%lu|%c|%d|%d|%lu|%lu|%lu",
	   (ULONG) inum, (flags & FS_FLAG_ALLOC) ? 'a' : 'f',
	   (int) fs_inode->uid, (int) fs_inode->gid,
	   (ULONG) fs_inode->mtime, (ULONG) fs_inode->atime,
	   (ULONG) fs_inode->ctime);
#ifdef HAVE_DTIME
    printf("|%lu", (ULONG) fs_inode->dtime);
#endif
    printf("|%lo|%d|%lu|%lu|%lu\n",
	   (ULONG) fs_inode->mode, (int) fs_inode->nlink,
	   (ULONG) fs_inode->size, (ULONG) fs_inode->direct_addr[0],
	   (ULONG) fs_inode->direct_addr[1]);
}

/* main - open file system, list inode info */

int     main(int argc, char **argv)
{
    FS_INFO *fs;
    char   *start;
    char   *last;
    INUM_T  istart;
    INUM_T  ilast;
    int     ch;
    int     flags = (FS_FLAG_USED | FS_FLAG_UNLINK
		     | FS_FLAG_ALLOC | FS_FLAG_UNALLOC
		     | FS_FLAG_TMHDR);

    progname = argv[0];

    while ((ch = getopt(argc, argv, "hlouv")) > 0) {
	switch (ch) {
	default:
	    usage();
	case 'h':
	    flags &= ~FS_FLAG_TMHDR;
	    break;
	case 'l':
	    flags |= FS_FLAG_LINK;
	    break;
	case 'o':
	    flags &= ~(FS_FLAG_UNALLOC | FS_FLAG_LINK);
	    break;
	case 'u':
	    flags |= FS_FLAG_UNUSED;
	    break;
	case 'v':
	    verbose++;
	    break;
	}
    }

    if (optind >= argc)
	usage();

    /*
     * Open the file system.
     */
    fs = fs_open(argv[optind++], DEF_FSTYPE);

    /*
     * List the named inodes.
     */
    if (optind < argc) {
	while ((start = argv[optind]) != 0) {
	    last = split_at(start, '-');
	    istart = atoinum(start);
	    ilast = (last ? atoinum(last) : istart);
	    fs->inode_walk(fs, istart, ilast, ~0, print_inode, (char *) &flags);
	    optind++;
	}
    }

    /*
     * List all inodes, subject to the specified restrictions.
     */
    else {
	fs->inode_walk(fs, fs->root_inum, fs->last_inum,
		       flags, print_inode, (char *) &flags);
    }
    fs->close(fs);
    exit(0);
}
