/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred."
 *
 */
/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: ff.c 1.7 89/10/10 $@(#)ff:ff.c	1.15"
/*

	ff - fast(er) find

	ff produces a list of filenames for a specified filesystem,
	allowing selection criteria.

	strategy:

	- process command line, saving any selection criteria.
	- read superblock of fs to obtain size, and allocate data
	  areas (see below).
	- read the ilist and save:
		- inumbers and inode information from selected files
		- block numbers of directory files.
	- read all directory blocks saving all directory entries
	- for all selected inumbers, generate their names.

	data areas:

	- Ireq is simply a list of saved inumbers who have been selected
	  for name generation.
	- Db holds directory block data. Specifically we need to know which
	  blocks in the filesystem belong to directories, and which dir is
	  their owner. This associates the dirnames which we read with their
	  parent directory (.) pointer.
	- Dd is an array, with inumber as an index, whose entries are the
	  leaf name of a file. The pinum member of Dd points to an entry's 
	  parent directory name, whose pinum points ...  So we go, climbing
	  the directory structure for each name we generate.
	  NOTE: Dd, being an array, MUST have as many elements as there are
	  inumbers. Can't compromise on space for this guy. 
	- Indb holds info on the rare case of indirect directories.
	- Link is a Dd array in which we save info on linked files. If a
	  given inode already has an entry in Dd we save it in Link.

*/
#include <sys/param.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ufsfilsys.h>
#include <sys/ufsinode.h>
#include <sys/ufsdir.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pwd.h>

/*
	Limits - These values are expanded in line in routine super().
	They use superblock values and rough approximations to define
	the sizes of the data structures. Note that they are probably
	different at each installation and maybe even across filesystems.
	ilb is the ilist size in blocks, lstblk & fstblk are just that.
	They represent an effort to balance the needs of the progam against
	the limited amount of memory available on your mini computer.
	Note that super() will adjust these values depending on avail-
	able memory. 

*/

char *valloc();
char *malloc();

#define	LIMITS	{	\
maxDd = g_inodes;		/* % of all inodes which are dirs */\
maxIreq = g_inodes;		/* % of all inodes which are used */\
maxL = 20+g_inodes/3;		/* % of all files which are links */\
maxDb = 20+g_inodes/2;		/* number of blocks owned by dirs */\
maxIb = 5+maxDb/10;		/* number of indirect dir blocks  */\
		}


#define PHYSBLKSZ 512
#define	MAXNLIST 64	/* max specific inumbers after -i option */
#define	A_DAY	86400L	/* seconds in a 24 hour period */
#define	DIRENT	(sizeof(struct direct))
#define	EQ(x,y)	(strcmp(x,y)==0)
#define	ALLOC	0
#define	FREE	1
#define	DATA	0
#define	SINGLE	1
#define	DOUBLE	2
#define	TRIPLE	3

/*
	Formatting options for printout
*/
#define	F_INODE	1
#define	F_SIZE	2
#define	F_UID	4
#define	F_PREPATH	8

/*
	Run options
*/
#define	F_REQUEST	1
#define	F_LINKSTOO	4
#define	F_DEBUG		8
/*	Data Structures	*/

#define MAXNAMLEN	20
/* Dd and Link - holds data from a directory entry */
struct	Dd	{
	ino_t	inum;		/* inumber of file represented by entry */
	ino_t	pinum;		/* inumber of parent directory */
	char	dname[MAXNAMLEN+1];	/* file name */
	};
struct	Dd	*Ddlist, *Linklist;
long	nDd;
long	maxDd;
long	nLink;
long	maxL;

/* Ireq - holds inumbers and any inode data for selected files */ struct	Ireq	{
	ino_t	inum;
	off_t	size;
	ushort	uid;
	};
struct	Ireq	*pIreq, *Ireqlist;
long	nIreq;
long	maxIreq;


/* Db - holds data on the blocks of the filesystem which are directories */
struct	Db	{
	ino_t	inum;		/* inumber of this directory */
	off_t	size;		/* # of data bytes in this directory blk */
	daddr_t	blkno;		/* block number */
	};
struct	Db	*pDb, *Dblist;
long	nDb;
long	maxDb;


/* Iblk - saves indirect blocks of directory files */
struct	Iblk	{
	ino_t	inum;
	daddr_t	single;
	daddr_t	dubble;
	daddr_t	triple;
	off_t	size;
	};
struct	Iblk	*pIb, *Iblklist;
long	nIb;
long	maxIb;

/* Nlist holds specific inode numbers requested with the -i option */
ino_t	*Nlist, *pN;

char	*usage =
	"[-Isul -i ilist -p path -n file -a # -m # -c #]  /inputdev";

extern	int	errno;
extern	int	sys_nerr;
extern	char	*sys_errlist[];

struct	stat	statb;
struct	fs	*fs;

int	g_bufn,		/* internal buffer allocation counter */
	g_ilb,		/* ilist size in blocks */
	g_inodes,	/* ilist size in inodes */
	g_inopb,	/* number of inodes in a block */
	g_bsize,	/* actual filesystem block size */
	g_fpi;		/* input filesystem file pointer */

char	*g_cmd,		/* our name, as invoked by the user */
	*g_dev,		/* name of filesystem we are processing */
	*g_path,	/* optional prefix path for generated names */
	*g_buf;		/* our general purpose buffer */

off_t	g_size;

int	criteria;	/* count of user selection criteria */

int	badnames;	/* incremented for unresolved path names */
#if u3b2  || u3b15
int	result;		/*  result returned  from  block size  heuristic */
#endif

char	prtopt,		/* flags - print options */
	runopt,		/* flags - run time options */
	as,		/* sign of number for -a option */
	ms,		/* "        "      "  -m option */
	cs;

long	atime,		/* abs(number) of days from -a option */
	mtime,
	crtime;		/* "         "           "  -c option */
time_t	ntime,
	today;		/* today as returned by time() */
main(argc,argv)
int argc;
char **argv;
{

	extern	char	*optarg;
	extern	int	optind;
	int	stat(), atoi();
	int	c;

	g_cmd = argv[0];
	g_dev = "";
	g_path = "";
	prtopt |= F_INODE;
	runopt = 0;
	badnames=0;
	time(&today);
	while((c=getopt(argc,argv,"Isulda:m:c:n:i:p:")) != EOF)
		switch(c) {
		case 'I':
			prtopt &= ~F_INODE;
			break;
		case 's':
			prtopt |= F_SIZE;
			break;
		case 'u':
			prtopt |= F_UID;
			break;
		case 'd':
			runopt |= F_DEBUG;
			break;
		case 'a':
			as = *optarg;
			if ((atime = atoi(optarg)) == 0) 
				errorx("ff: bad value %s for option a\n",optarg);
			++criteria;
			break;
		case 'm':
			ms = *optarg;
			if ((mtime = atoi(optarg)) == 0) 
				errorx("ff: bad value %s for option m\n",optarg);
			++criteria;
			break;
		case 'c':
			cs = *optarg;
			if ((crtime = atoi(optarg)) == 0) 
				errorx("ff: bad value %s for option c\n",optarg);
			++criteria;
			break;
		case 'n':
			if (stat(optarg,&statb) < 0)  {
				perror("ff: n option");
				exit(1);
			}
			++criteria;
			ntime = statb.st_mtime;
			break;

		case 'i':
			runopt |= F_REQUEST;
			++criteria;
			if (inodes(optarg) == 0)
				exit(1);
			break;

		case 'p':
			prtopt |= F_PREPATH;
			g_path = optarg;
			break;

		case 'l':
			runopt |= F_LINKSTOO;
			break;

		case '?':
		default:
			errorx("Usage: %s %s\n",g_cmd,usage);

		}
		if(optind >= argc)
			errorx("Usage: %s %s\n",g_cmd,usage);


	process(argv[optind]);

	if (badnames)
		error("ff: NOTE! %d pathnames only partially resolved\n", badnames);

	if (runopt&F_DEBUG) {
		error("maxL=%3d\tmaxIreq=%3d\tmaxDb=%3d\tmaxIb=%3d\tmaxDd=%3d\n",
		maxL, maxIreq, maxDb, maxIb, maxDd);
		error("nLink=%3d\tnIreq=%3d\tnDb=%3d\tnIb=%3d\tnDd=%3d\n",
		nLink, nIreq, nDb, nIb, nDd);
	}

	exit(0);

}
process(dev)
char *dev;
{
	int	bcomp();
	static	struct cg *cgp;

	sync();
	g_dev = dev;

	if (stat(dev,&statb) < 0) {
		fprintf(stderr,"ff: %s  - ",g_dev);
		perror("");
		exit(1);
	}
	if (cgp == (struct cg *)NULL) {
		cgp = (struct cg *)valloc(MAXBSIZE);
		if (cgp == (struct cg *)NULL) {
			fprintf(stderr, "Couldn't valloc cg\n");
			exit(1);
		}
	}

	if ((((statb.st_mode&IFMT)==IFBLK)|((statb.st_mode&IFMT)==IFCHR))==0)
		errorx("ff: %s is neither block nor character special\n", g_dev);

	if ((g_fpi=open(dev,O_RDONLY)) < 0)
		errorx("ff:  Open failed for input on %s\n",g_dev);

	if (super() <= 0)
		return(0);

	ilist();
	if (nIreq == 0) {
		error("ff: No files were selected.\n");
		return(0);
	}

	if (nIb > 0)
		indir();

	qsort((char *)Dblist, (int) nDb, sizeof(struct Db), bcomp);
	readdirs();

	error("ff: %d files selected\n", nIreq);

	if (runopt&F_LINKSTOO)
		error("ff: %d link names detected\n", nLink);

	monikers();

	return(1);

}
int super()
{

	char	*calloc(), *valloc();
#ifndef _SEQUENT_
	int	fstblk, lstblk, retry;
#endif /* ! _SEQUENT_ */
	long	have, want, ulimit();

	fs = (struct fs *)valloc(SBSIZE);
	if (fs == NULL)
		errorx("ff: out of memory for superblock\n");
	g_bsize = SBSIZE;
	seer(SBLOCK, fs);
	if (fs->fs_magic != FS_MAGIC)
		errorx("ff: Invalid super block\n");

	/* allocate a page-aligned utility buffer */
	g_buf = valloc(MAXBSIZE);
	if (g_buf == NULL)
		errorx("ff: out of memory\n");

#ifndef _SEQUENT_
	fstblk = fs->s_isize;
	lstblk = fs->s_fsize;
	g_ilb = fstblk - 2;
#endif /* ! _SEQUENT_ */
#ifdef FsMAGIC
	if (fs->s_magic != FsMAGIC)
		g_bsize = BSIZE;
	else {
		if (fs->s_type == Fs1b)
			g_bsize = 512;
#if u3b2 || u3b15
		else if (fs->s_type == Fs2b) {
			result = s5bsize(g_fpi);
				if (result == Fs2b)
					g_bsize = 1024;
				else if (result == Fs4b)
					g_bsize  = 2048;
				else 
					errorx("ff: can't verify logical block  size of file system\n    root inode or root directory may be  corrupted\n");
		} else if  (fs->s_type == Fs4b)
			g_bsize = 2048;
#else
		else if (fs->s_type ==  Fs2b)
			g_bsize = 1024;
#endif /* u3b2 || u3b15 */
		else
			errorx("ff: unknown filesystem BSIZE\n");
	}
#else
	g_bsize = fs->fs_bsize;
#endif /* FsMAGIC */
	g_inodes = fs->fs_ncg * fs->fs_ipg - fs->fs_cstotal.cs_nifree;
	g_ilb = fs->fs_ncg;
	g_inopb = fs->fs_ipg;

	if (runopt&F_DEBUG)
		error("g_ilb:%d  g_inodes:%d  g_bsize:%d  g_inopb:%d\n",
			g_ilb, g_inodes, g_bsize, g_inopb);
	
	LIMITS;

	if (runopt&F_DEBUG) {
		error("maxIreq:%d  maxL:%d  maxDb:%d  maxIb:%d\n",
			maxIreq, maxL, maxDb, maxIb);
	}

#ifndef _SEQUENT_
	retry = 0;
again:	have = ulimit(3,0) - sbrk(0) - 16000;	/* get memory available */
	want =	maxIreq*sizeof(struct Ireq) + maxL*sizeof(struct Dd) +
		maxDb*sizeof(struct Db) + maxIb*sizeof(struct Iblk) +
		(1+g_ilb*g_inopb)*sizeof(struct Dd);
	if (have < want) {
		if (retry++ > 4) 
			errorx("ff: Insufficient memory. Giving up\n");
		maxL = (long)((float)maxL*0.90);
		maxDb = (long)((float)maxDb*0.90);
		maxIreq = (long)((float)maxIreq*0.90);
		goto again;
	}
	if (retry)
		error("ff: Reducing memory demands. May fail.\n");
#endif /* ! _SEQUENT_ */

	if (runopt&F_DEBUG) {
		error("Ddlist:%d  Linklist:%d  Ireqlist:%d  Dblist:%d  Iblklist:%d\n",
		maxDd*sizeof(struct Dd),
		maxL*sizeof(struct Dd),
		maxIreq*sizeof(struct Ireq),
		maxDb*sizeof(struct Db),
		maxIb*sizeof(struct Iblk));
	}

	if ((Ddlist=(struct Dd *)calloc(g_inodes,sizeof(struct Dd))) == 0) 
		errorx("ff: insufficient memory for Dd area\n");

	if ((Linklist=(struct Dd *)calloc(maxL,sizeof(struct Dd))) == 0) 
		errorx("ff: insufficient memory for Link area\n");

	if ((pIreq=Ireqlist=(struct Ireq *)malloc((unsigned)(maxIreq*sizeof(struct Ireq)))) == 0) 
		errorx("ff: insufficient memory for Ireq area\n");

	if ((pDb=Dblist=(struct Db *)malloc((unsigned)(maxDb*sizeof(struct Db)))) == 0) 
		errorx("ff: insufficient memory for Db area\n");

	if ((pIb=Iblklist=(struct Iblk *)malloc((unsigned)(maxIb*sizeof(struct Iblk)))) == 0) 
		errorx("ff: insufficient memory for Iblk area\n");

	nIreq = nDb = nIb = nLink = nDd = 0;

	return(1);

}

/*
 * for each cylinder group, read in the cylinder group structure.
 * Pass that to the cgilist() function, to have all the inodes in that
 * group processed.
 */
ilist()
{
	int	cgx; 		/* cylinder group number */
	static struct	cg *cgp; /* pointer to cylinder group (valloc'd) */


	/*
	 * valloc the cylinder group buffer ONCE.
	 */
	if (cgp == (struct cg *)NULL) {
		cgp = (struct cg *)valloc(MAXBSIZE);
		if (cgp == (struct cg *)NULL) {
			fprintf(stderr, "Couldn't valloc cg\n");
			exit(1);
		}
	}
	g_bsize = MAXBSIZE;
	/*
	 * index through the cylinder group numbers.
	 *	 g_ilb contains number if cylinder groups (ugh)
	 */
	for (cgx=0; cgx < g_ilb; cgx++) {
		seer(fsbtodb(fs, cgtod(fs, cgx)), cgp);
		if (!cg_chkmagic(cgp)) {
			fprintf(stderr, "bad magic in cylinder group %d\n",
				cgx);
			exit(1);
		}
		cgilist(cgp);
	}
}

/*
 * process the inodes within a cylinder group
 *	cgp is a pointer to the cylinder group structure being processed.
 *	curip points to the running inode number counter.
 */
cgilist(cgp)
struct cg *cgp;
{
	struct	dinode	*dip;
	char	*imp;
	daddr_t	iblkno = -1, niblkno;
	ino_t	curi;
	int i;

	curi = cgp->cg_cgx * fs->fs_ipg;
	for (imp = cg_inosused(cgp); imp < &cg_inosused(cgp)[fs->fs_ipg/NBBY];
		imp++) {
		if (*imp == 0) {
			curi += NBBY;
			continue;
		}
		for (i = 0; i < NBBY; i++) {
			if ((curi < 2) || !((*imp)&(1 << i))) {
				curi++;
				continue;
			}
			if (iblkno != (niblkno = itod(fs, curi))) {
				seer(fsbtodb(fs, niblkno), g_buf);
				iblkno = niblkno;
			}
			dip = (struct dinode *)g_buf + (curi)%INOPB(fs);
			if (dip->di_mode == 0) {
				fprintf(stderr, "allocated inode is free %d\n", 
					curi);
				exit(1);
			}
			if ((dip->di_mode&IFMT)==IFDIR) 
				savedir(curi,dip);
			if (select(curi,dip)) 
				saveinod(curi,dip);
			curi++;
		}
	}
}


saveinod(inum,dip)
int	inum;
struct	dinode	*dip;
{

	(*pIreq).inum = inum;
	if (dip != 0) {
		(*pIreq).size = dip->di_size;
		(*pIreq).uid = dip->di_uid;
	}
	pIreq++;
	if (nIreq++ > maxIreq)
		exceed(maxIreq, "Ireq - selected file list");

	return;

}
savedir(inum,dip)
ino_t	inum;
struct	dinode	*dip;
{

	int	i, size;
	struct	direct	*dirp;

	if (dip->di_mode == 0 || !((dip->di_mode&IFMT)==IFDIR))  
		return;

	size = dip->di_size;
#ifdef u370
	/* Small Block UNIX/370 
	in SBu370 an inode with SBAUSE set contains SBASIZE data bytes
	*/
	if (dip->di_sbmode == SBAUSE) {
		dirp = (struct direct *) (dip->di_data);
		i = (size > SBASIZE ? SBASIZE : size) / DIRENT;
		while (i--)
			xdname(dirp++,inum);
		size -= SBASIZE;
	}
#endif

	for (i=0; i<NDADDR && size > 0 ; i++) {   
		if (dip->di_db[i] == 0)
			break;
		adddirb(inum, dip->di_db[i],
		(off_t)(size > g_bsize ? size-=g_bsize, g_bsize : size));
	}

	if ( dip->di_ib[0] || dip->di_ib[1] || dip->di_ib[2] ) {
		addindb(inum, dip->di_size,
		dip->di_ib[0], dip->di_ib[1], dip->di_ib[2]);
	}

	return;

}
adddirb(ino,blk,sz)
ino_t	ino;
daddr_t	blk;
off_t	sz;

{

	pDb->inum = ino;
	pDb->blkno = blk;
	pDb->size = sz;
	pDb++;
	if (nDb++ > maxDb)
		exceed(maxDb, "Db - directory data block list");

	return;

}


addindb(ino, sz, s, d, t)
ino_t	ino;
off_t	sz;
daddr_t	s, d, t;
{

	pIb->inum = ino;
	pIb->single = s;
	pIb->dubble = d;
	pIb->triple = t;
	pIb->size = sz;
	pIb++;
	if (nIb++ > maxIb)
		exceed(maxIb, "Iblk - indirect directory block list");
	return;

}

select(curi,dip)
ino_t	curi;
struct dinode *dip;
{

	register int ok = 0;

	if (criteria == 0)
		return(1);

	if (atime) 
		ok += (scomp((long)((today-dip->di_atime.tv_sec)/A_DAY),atime,as)) ? 1 : 0;

	if (mtime) 
		ok += (scomp((long)((today-dip->di_mtime.tv_sec)/A_DAY),mtime,ms)) ? 1 : 0;

	if (crtime) 
		ok += (scomp((long)((today-dip->di_ctime.tv_sec)/A_DAY),crtime,cs)) ? 1 : 0;

	if (ntime) 
		ok += (dip->di_mtime.tv_sec > ntime) ? 1 : 0;

	if (runopt&F_REQUEST)
		ok += (ncheck(curi) ? 1 : 0);

	return(ok == criteria);

}

scomp(a,b,s)
register long a, b;
register char s;
{

	if (s == '+')
		return(a > b);
	if (s == '-')
		return(a < (b * -1));
	return(a == b);

}
readdirs()
{

	int	i, j;
	struct	direct	*dirp;

	pDb = Dblist;
	for (i=0; i<nDb; i++, pDb++) {
		g_bsize = roundup(pDb->size, DEV_BSIZE);
		seer(fsbtodb(fs, pDb->blkno), g_buf);
		for (j=0; j<pDb->size; j+=dirp->d_reclen) {
			dirp = (struct direct *)(g_buf + j);
			xdname(dirp,pDb->inum);
		}
	}

	g_bsize = fs->fs_bsize;
	return;

}

xdname(dirp,dotinum)
register struct	direct	*dirp;
register ino_t	dotinum;
{

	register ino_t	dino = dirp->d_ino;

	if (dino==0 || EQ(".",dirp->d_name) || EQ("..",dirp->d_name))
		return;

	if (dino > g_ilb * g_inopb)
		errorx("ff: Improbable inumber %d. Check filesystem.\n",dino);

	if (inDdlist(dino, 0) == 0) {
		Ddlist[nDd].inum = dino;
		Ddlist[nDd].pinum = dotinum;
		strncpy(Ddlist[nDd].dname, dirp->d_name, MAXNAMLEN);

		if (nDd++ > maxDd)
			exceed(maxDd, "Ddlist - directory entry list");
	} else {
		Linklist[nLink].inum = dino;
		Linklist[nLink].pinum = dotinum;
		strncpy(Linklist[nLink].dname, dirp->d_name, MAXNAMLEN);

		if (nLink++ > maxL)
			exceed(maxL, "Link - linked file list");
	}

	return;

}
inDdlist(dino, ri)
register ino_t dino;
int	*ri;  /* return index? */
{
	/* is dino in the Ddlist? */
	/* In an effort to save memory, Ddlist is no longer indexed
	   by the inumber.  Instead, there is only space allocated
	   for the total number of active inodes (as kept current
	   in the superblock).

	   The inode in question (dino) can never produce an index
	   into the Ddlist greater than itself; a full file system
	   would give a 1:1 mapping between dino and Ddlist[index].

	   If Ddlist was kept in sorted order the search could be
	   made much quicker.  For now just do a sequential (yuk)
	   search.  Maybe this can be optimized sometime later.
	*/
	register int i, match;

	for (i=0; i<=nDd; i++)
		if (match = (Ddlist[i].inum == dino))
			break;
	if (ri)
		*ri = i;
	return(match);
}

monikers()
{

	register int	i;
	char	Pathname[1024];

	pIreq = Ireqlist;

	for (i=0; i<nIreq; i++, pIreq++) {
		name(pIreq->inum, Pathname, 0);
		format(Pathname, pIreq->inum, pIreq);
	}

	if (runopt&F_LINKSTOO)
		for (i=0; i<nLink; i++) {
			name(Linklist[i].pinum, Pathname, Linklist[i].dname);
			format(Pathname,Linklist[i].inum,0);
		}

	return;

}


format(Path,inum,pi)
register	char *Path;
register	ino_t	inum;
register	struct Ireq *pi;
{
	char	*uidtonam(), *nam;

	printf(Path);
	if (!prtopt)
		printf("\n");
	else {
		if (prtopt&F_INODE)
			printf("\t%d",inum);
		if (prtopt&F_SIZE && pi)
			printf("\t%d",pi->size);
		if (prtopt&F_UID && pi) {
			if (*(nam=uidtonam(pi->uid)) == '?')
				printf("\t%d",pi->uid);
			else
				printf("\t%8s",nam);
			}
		printf("\n");
		}

}
name(curi,Path,link)
register ino_t	curi;
register char	*Path;
char	*link;
{
	struct  P  {
		char	p[MAXNAMLEN+1];
		} P[50];
	int	n = 0;
	int index;

	if (prtopt&F_PREPATH)
		strcpy(Path, g_path);
	else
		strcpy(Path, ".");

	if (link)
		strncpy(P[n++].p, link, MAXNAMLEN);

	for (; curi > 2; curi=Ddlist[index].pinum) { 
		if (n > 50)
			exceed(50,"path components");
		if (inDdlist(curi, &index) == 0) {
			strcpy(P[n++].p, "??unresolved??");
			badnames++;
			break;
		}
		strncpy(P[n++].p,Ddlist[index].dname,MAXNAMLEN);
	}
	while(n > 0) { 
		strcat(Path,"/");
		strncat(Path,P[--n].p,MAXNAMLEN);
	}
	return;

}
/*
 * convert uid to login name; interface to getpwuid that keeps up to USIZE1
 * names to avoid unnecessary accesses to passwd file
 * returns ptr to NSZ-byte name (not necessarily null-terminated)
 * returns pointer to "?" if we cannot resolve uid
 * lifted gleefully from the Unix accounting package.
 */

#define	NSZ	8	/* max length of a login name */
#define USIZE1	50

static	usize1;
static struct ulist {
	char	uname[NSZ];
	uid_t	uuid;
	} ul[USIZE1];

char *
uidtonam(uid)
uid_t	uid;
{
	register struct ulist *up;
	struct passwd *getpwuid();
	register struct passwd *pp;

	for (up = ul; up < &ul[usize1]; up++)
		if (uid == up->uuid)
			return(up->uname);
	setpwent();
	if ((pp = getpwuid(uid)) == NULL)
		return("?");
	else {
		if (usize1 < USIZE1) {
			up->uuid = uid;
			strncpy(up->uname, pp->pw_name, sizeof(up->uname));
			usize1++;
		}
		return(pp->pw_name);
	}
}
int indir()
{

	int	i;

	pIb = Iblklist;
	for (i=0; i<nIb; i++, pIb++) {
		g_size = (*pIb).size - g_bsize*(NDADDR);

		if ((*pIb).single) {
			seer((*pIb).single, g_buf);
			ptrblk(g_buf, (*pIb).inum, DATA);
		}

		if ((*pIb).dubble) {
			seer((*pIb).dubble, g_buf);
			ptrblk(g_buf, (*pIb).inum, SINGLE);
		}

		if ((*pIb).triple) {
			seer((*pIb).triple, g_buf);
			ptrblk(g_buf, (*pIb).inum, DOUBLE);
		}

	}

	return;

}
ptrblk(buf,inum,type)
daddr_t	buf[NIADDR];
ino_t	inum;
int	type;
{

	int	i = 0;

	while (i < NIADDR) {
		if (*buf)
			switch (type) {

			case DATA:
				adddirb(inum, *buf, (off_t)(g_size > g_bsize ? g_size-=g_bsize, g_bsize : g_size));
				break;

			case SINGLE:
				seer(*buf, g_buf);
				ptrblk(g_buf, inum, DATA);
				break;

			case DOUBLE:
				seer(*buf, g_buf);
				ptrblk(g_buf, inum, SINGLE);
				break;

			}
		buf++;
		i++;
	}

	return;

}

#ifndef _SEQUENT_
char *getbuf(type,buf)
int	type;
char 	*buf;
{

	char	*p, *malloc();

	switch(type) {

	case ALLOC :
		if((p=malloc(g_bsize)) == 0)
			errorx("ff: out of memory\n");
		return(p);

	case FREE :
		free(buf);
		return;
	}
}
#endif /* ! _SEQUENT_ */

int seer(blk, buf)
daddr_t	blk;
char	*buf;
{

	long	l,s;
	int	r;

#ifdef FsMAGIC
	s = (blk<2 ? blk*BSIZE : blk*g_bsize); 
#else
	s = dbtob(blk);
#endif

	l = lseek(g_fpi, s, 0);
	r = read(g_fpi, buf, g_bsize);

	if (l<0 || r< 0)
		errorx("ff: I/O failed for block %d. seek()=%d read()=%d,err=%d\n",
			blk,l,r,errno);
	if (runopt&F_DEBUG)
		error("seek to %d (%dblks), read %d\n",s,s/g_bsize,r);

	return(r);

}


getblk(buf)
char *buf;
{

	int i;

	if ((i=read(g_fpi, buf, g_bsize)) < 0 || i!=g_bsize)
		errorx("ff: read failed in getblk(), %s\n",
			(errno<sys_nerr?sys_errlist[errno]:"?? errno"));

}
int inodes(args)
char *args;
{

	char	*calloc(), *strtok(), *p;
	int	nN, n, atoi();
	int	first = 0;

	nN = 0;

	if ((pN=Nlist=(ino_t *) calloc(MAXNLIST,sizeof(ino_t))) == 0) {
		error("ff: calloc failed in inodes()\n");
		return(0);
	}

	while ((p=strtok((first++==0?args:0),", ")) != NULL) {
		if ((n=atoi(p)) <= 0)
			error("ff: -i value, %d, ignored\n",n);
		else {
			*pN++ = n;
			if (nN++ > MAXNLIST)
				exceed(MAXNLIST,"-i inumber list");
		}
	}

	return(1);
}


int ncheck(curi)
register ino_t curi;
{

	ino_t	*p;

	for (p=Nlist; p<pN ; p++ )
		if (*p == curi)
			return(1);
	return(0);
}
exceed(max, s)
long	max;
char	*s;
{

	errorx("ff: program limit of %d exceeded for %s\n",max,s);

}


error(s,v1,v2,v3,v4,v5,v6,v7,v8,v9)
{

	fprintf(stderr,"%s: %s: ", g_cmd, g_dev);

	fprintf(stderr, s,v1,v2,v3,v4,v5,v6,v7,v8,v9);
	
}

errorx(s,v1,v2,v3,v4,v5,v6,v7,v8,v9)
{

	error(s,v1,v2,v3,v4,v5,v6,v7,v8,v9);
	exit(1);

}


int bcomp(a,b)
struct	Db	*a, *b;

{

	return( (*a).blkno - (*b).blkno);

}
#if u3b2 || u3b15
/* heuristic function to determine logical block size of System V file system */

s5bsize(fd)
int fd;
{

	int results[3];
	int count;
	long address;
	long offset;
	char *buf;
	struct dinode *inodes;
	struct direct *dirs;
	char * p1;
	char * p2;
	
	results[1] = 0;
	results[2] = 0;

	buf = (char *)malloc(PHYSBLKSZ);

	for (count = 1; count < 3; count++) {

		address = 2048 * count;
		if (lseek(fd, address, 0) != address)
			continue;
		if (read(fd, buf, PHYSBLKSZ) != PHYSBLKSZ)
			continue;
		inodes = (struct dinode *)buf;
		if ((inodes[1].di_mode & IFMT) != IFDIR)
			continue;
		if (inodes[1].di_nlink < 2)
			continue;
		if ((inodes[1].di_size % sizeof(struct direct)) != 0)
			continue;
	
		p1 = (char *) &address;
		p2 = inodes[1].di_addr;
		*p1++ = 0;
		*p1++ = *p2++;
		*p1++ = *p2++;
		*p1   = *p2;
	
		offset = address << (count + 9);
		if (lseek(fd, offset, 0) != offset)
			continue;
		if (read(fd, buf, PHYSBLKSZ) != PHYSBLKSZ)
			continue;
		dirs = (struct direct *)buf;
		if (dirs[0].d_ino != 2 || dirs[1].d_ino != 2 )
			continue;
		if (strcmp(dirs[0].d_name,".") || strcmp(dirs[1].d_name,".."))
			continue;
		results[count] = 1;
		}
	free(buf);
	
	if(results[1])
		return(Fs2b);
	if(results[2])
		return(Fs4b);
	return(-1);
}
#endif
