/* @(#)find_list.c	1.14 06/10/31 Copyright 1985, 1995, 2000-2006 J. Schilling*/
#ifndef lint
static	char sccsid[] =
	"@(#)find_list.c	1.14 06/10/31 Copyright 1985, 1995, 2000-2006 J. Schilling";
#endif
/*
 *	List a file
 *
 *	Copyright (c) 1985, 1995, 2000-2006 J. Schilling
 */
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

#include <schily/mconfig.h>
#include <stdio.h>
#include <schily/unistd.h>
#include <schily/utypes.h>
#include <schily/dirent.h>
#include <schily/time.h>
#include <schily/standard.h>
#include <schily/string.h>
#include <schily/schily.h>
#include <schily/device.h>
#include <schily/nlsdefs.h>

#ifdef	HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifndef	DEV_BSIZE
#define	DEV_BSIZE	512
#endif

#include "walk.h"
#include "find.h"
#include "find_list.h"
#include "find_misc.h"

#define	K_DIV	(1024/DEV_BSIZE)

EXPORT	time_t	find_sixmonth;		/* 6 months before limit (ls)	*/
EXPORT	time_t	find_now;		/* now limit (ls)		*/

#define	paxls	TRUE
extern	BOOL	numeric;
extern	int	verbose;

LOCAL	void	modstr		__PR((struct stat *fs, char *s, Ulong  mode, char *name, char *sname, struct WALK *state));
EXPORT	void	find_list	__PR((struct stat *fs, char *name, char *sname, struct WALK *state));

/*--------------------------------------------------------------------------*/
extern	BOOL	nameuid	__PR((char *name, int namelen, uid_t uid));
extern	BOOL	namegid	__PR((char *name, int namelen, gid_t gid));
/*--------------------------------------------------------------------------*/

/*
 * Convert POSIX.1 mode/permission flags into string.
 */
LOCAL void
modstr(fs, s, mode, name, sname, state)
	struct stat	*fs;
		char	*s;
	register Ulong	mode;
		char	*name;
		char	*sname;
	struct WALK	*state;
{
	register char	*mstr = "xwrxwrxwr";
	register char	*str = s;
	register int	i;

	for (i = 9; --i >= 0; ) {
		if (mode & (1 << i))
			*str++ = mstr[i];
		else
			*str++ = '-';
	}
#ifdef	USE_ACL
	*str++ = ' ';
#endif
#ifdef	USE_XATTR
	*str++ = '\0';				/* Don't claim space for '@' */
#endif
	*str = '\0';
	str = s;
	if (mode & S_ISVTX) {
		if (mode &  S_IXOTH) {
			str[8] = 't';		/* Sticky & exec. by others  */
		} else {
			str[8] = 'T';		/* Sticky but !exec. by oth  */
		}
	}
	if (mode & S_ISGID) {
		if (mode & S_IXGRP) {
			str[5] = 's';		/* Sgid & executable by grp  */
		} else {
			if (S_ISDIR(fs->st_mode))
				str[5] = 'S';	/* Sgid directory	    */
			else
				str[5] = 'l';	/* Mandatory lock file	    */
		}
	}
	if (mode & S_ISUID) {
		if (mode & S_IXUSR)
			str[2] = 's';		/* Suid & executable by own. */
		else
			str[2] = 'S';		/* Suid but not executable   */
	}
	i = 9;
#ifdef	USE_ACL
	if (state->pflags & PF_ACL) {
		if (state->pflags & PF_HAS_ACL)
			str[i++] = '+';
	} else
	if (has_acl(name, sname, fs))
		str[i++] = '+';
#endif
#ifdef	USE_XATTR
	if (state->pflags & PF_XATTR) {
		if (state->pflags & PF_HAS_XATTR)
			str[i++] = '@';
	} else
	if (has_xattr(sname))
		str[i++] = '@';
#endif
	i++;	/* Make lint believe that we always use i. */
}

EXPORT void
find_list(fs, name, sname, state)
	struct stat	*fs;
	char		*name;
	char		*sname;
	struct WALK	*state;
{
		time_t	*tp;
		char	*tstr;
		char	mstr[12]; /* 9 UNIX chars + ACL '+' XATTR '@' + nul */
		char	lstr[11]; /* contains link count as string */
	static	char	nuid[32+1];
	static	char	ngid[32+1];
		char	*add = "";
char	lname[8192];
char	*lnamep = lname;
		int	lsize;

#define	verbose	1
	if (verbose) {
		char	*uname;
		char	*gname;
		int	umaxlen;
		int	gmaxlen;
		char	ft;

		tp = state->walkflags & WALK_LS_ATIME ? &fs->st_atime :
				(state->walkflags & WALK_LS_CTIME ?
					&fs->st_ctime : &fs->st_mtime);
		tstr = ctime(tp);

		if (nameuid(nuid, sizeof (nuid), fs->st_uid)) {
			uname = nuid;
			umaxlen = sizeof (nuid)-1;
		} else {
			sprintf(nuid, "%llu", (Llong)fs->st_uid);
			uname = nuid;
			umaxlen = sizeof (nuid)-1;
		}

		if (namegid(ngid, sizeof (ngid), fs->st_gid)) {
			gname = ngid;
			gmaxlen = sizeof (ngid)-1;
		} else {
			sprintf(ngid, "%llu", (Llong)fs->st_gid);
			gname = ngid;
			gmaxlen = sizeof (ngid)-1;
		}

		{
			printf("%7llu ", (Llong)fs->st_ino);
#ifdef	HAVE_ST_BLOCKS
			printf("%4llu ", (Llong)fs->st_blocks/K_DIV);
#else
			printf("%4llu ", (Llong)(fs->st_size+1023)/1024);
#endif
		}
		if (!paxls) {
			if (S_ISBLK(fs->st_mode) || S_ISCHR(fs->st_mode))
				printf("%3lu %3lu",
					(long)major(fs->st_rdev), (long)minor(fs->st_rdev));
			else
				printf("%7llu", (Llong)fs->st_size);
		}
		modstr(fs, mstr, fs->st_mode, name, sname, state);

		if (paxls || fs->st_nlink > 0) {
			/*
			 * UNIX ls uses %3d for the link count
			 * and does not claim space for ACL '+'
			 */
			js_sprintf(lstr, " %2ld", (long)fs->st_nlink);
		} else {
			lstr[0] = 0;
		}

		switch (fs->st_mode & S_IFMT) {

		case S_IFREG:	ft = '-'; break;
		case S_IFLNK:	ft = 'l';
				if (state->lname != NULL) {
					lnamep = state->lname;
					break;
				}
				lname[0] = '\0';
				lsize = readlink(sname, lname, sizeof (lname));
				if (lsize < 0)
					errmsg(
					gettext("Cannot read link '%s'.\n"),
						name);
				lname[sizeof (lname)-1] = '\0';
				if (lsize >= 0)
					lname[lsize] = '\0';
				break;
		case S_IFDIR:	ft = 'd'; break;
		case S_IFBLK:	ft = 'b'; break;
		case S_IFCHR:	ft = 'c'; break;
		case S_IFIFO:	ft = 'p'; break;
#ifdef	S_IFDOOR
		case S_IFDOOR:	ft = 'D'; break;
#endif
#ifdef	S_IFSOCK
		case S_IFSOCK:	ft = 's'; break;
#endif
#ifdef	S_IFNAM
		case S_IFNAM:	switch (fs->st_rdev) {
				case S_INSEM:
					ft = 's';
					break;
				case S_INSHD:
					ft = 'm';
					break;
				default:
					ft = '-';
					break;
				}
#endif

		default:	ft = '?'; break;
		}

		if (!paxls) {
			printf(" %c%s%s %3.*s/%-3.*s %.12s %4.4s ",
				ft,
				mstr,
				lstr,
				umaxlen, uname,
				gmaxlen, gname,
				&tstr[4], &tstr[20]);
		} else {
			printf("%c%s%s %-8.*s %-8.*s ",
				ft,
				mstr,
				lstr,
				umaxlen, uname,
				gmaxlen, gname);
			if (S_ISBLK(fs->st_mode) || S_ISCHR(fs->st_mode))
/*				printf("%3lu %3lu",*/
/* XXXXXXXXXXXXXX */		printf("%3lu, %3lu",
					(long)major(fs->st_rdev), (long)minor(fs->st_rdev));
			else
				printf("%7llu", (Llong)fs->st_size);
			if ((*tp < find_sixmonth) || (*tp > find_now)) {
				printf(" %.6s  %4.4s ",
					&tstr[4], &tstr[20]);
			} else {
				printf(" %.12s ",
					&tstr[4]);
			}
		}
	}
	printf("%s%s", name, add);

	if (S_ISLNK(fs->st_mode))
		printf(" -> %s", lnamep);

	printf("\n");
}
