/* Prints stat() parameters except the unused st_spare[1-4] */
#ifdef	ournix	/* { */
#include "ournix.h"
#endif		/* } */
#ifdef SCCS	/* { */
static char sccsID[] =
 "@(#) statv.c V1.7 Copyright Julian H. Stacey, Munich, 1990 04 17, 1998, 2012, 2015\n";
#endif		/* } */
/* Copyright:
	This code was written independently by Julian H. Stacey before being
	used or delivered with any code for Stacey/VSL clients.
	Clients who receive such code are permitted to use & resell code etc,
	but copyright to this file is Not assigned to the client.
	& no Liability is accepted, nor Warranty granted for this free code
	that the client/recipient has not paid for development of.
*/

#include <stdio.h>
#include <unistd.h>	// for MAXPATHLEN for man 2 readlink
#include <sys/param.h>	// for MAXPATHLEN for man 2 readlink
// #include <roken-common.h.h>	// for MAXPATHLEN for man 2 readlink
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef	scs	/* { BSD */
#ifndef BSD	/* { */
#define BSD
#endif		/* } */
#endif		/* } */

#ifdef	__386BSD__	/* { BSD */
#ifndef BSD	/* { */
#define BSD
#endif		/* } */
#endif		/* } */

#if	( __FreeBSD__ || __OpenBSD__ )	/* { BSD */
#ifndef BSD	/* { */
#define BSD
#endif		/* } */
#endif		/* } */

#ifdef	BSD	/* { BSD */
#define major_dev_no	major
#define minor_dev_no	minor
#else		/* }{ */
#ifdef	i386	/* { */
/* this next bit either for esix or sco, cant remember which or both */
#ifndef sco	/* { this SCO is unfortunately not defined by gcc on
		 * sco-open-server-5.0.5, & probably not by native cc either
		 */
#include <sys/mkdev.h>
#include <sys/ddi.h>
#endif		/* } */
#define major_dev_no	major
#define minor_dev_no	minor
#ifndef sco	/* { */
#include <sys/ddi.h>
#endif		/* } */
/* I have no idea if an esix 486 has seperate internal & external numbers,
	as mentioned by the manual, or whether this is just a 3b2 weirdness */
#endif		/* } */
#endif		/* } */

/* #include <stdlib.h>	/ * for user_from_uid() & group_from_gid() */
#include <pwd.h>	/* for getpwuid */
#include <grp.h>	/* for getgrgid */

static char **ARGV ;
static int err_no = 0 ;

extern char *ctime() ;

	static void
syntax()
	{
	fprintf(stderr,"Error, correct syntax is:\n%s file[s]\n",*ARGV);
	}

	int
#ifndef EMBEDDED /* { */
main(argc,argv)
#else /* }{ */
stat_js(argc, argv) /* cant call it stat as there''s a sys call of that name */
#endif	/* } */
	int	argc ;
	char	**argv ;
	{
	struct stat stat_buf, lstat_buf, *stat_buf_p ;
	char *cmt ;
	typedef char FLAG ;
	FLAG	found = 0 ;	/* if we found a valid pathname to examine */
#ifdef sco	/* { */
#include <ustat.h>
	struct ustat sco_buf;
		/*
		daddr_t f_tfree; Total free blocks
	ino_t f_tinode; Number of free inodes
	char f_fname[6]; Filsys name
	char f_fpack[6]; Filsys pack name
		*/
#endif		/* } */
	struct passwd *pwd ;
	struct group *grp ;
	int lnklen;
	char target[MAXPATHLEN + 1]; // Allow null terminator.

	ARGV = argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */

	if (argc < 2)
		{
		syntax() ;
		return(++err_no) ;
		}
	else while (--argc)
		{
		argv++ ;
#define STRADR &
		if (stat(*argv, &stat_buf) || lstat(*argv, &lstat_buf))
			// Normally stat() will succeed & return 0,
			// so normally lstat gets called too.
			{
			err_no++ ;
			perror(*ARGV) ;
			fprintf(stderr,"%s\n",*argv);
			continue ;
			}
		found = 1 ;
		/* All types shown in comment delimeters inside printf s
			in this procedure are from the BSD 4.2 man stat
			manual page listing */

		printf("Name\t%s%s\n",
			/* Shift long names to left, saves user scrolling */
			(strlen(*argv) > strlen(
			"Mon Jan 25 11:06:36 1999 \040 \040 \040 "))
				/* I originally put in backslashes
				 * before spaces above, to defeat
				 * tabulators on source, but gcc warns
				 * unknown escape sequence ``\ '' &
				 * SCO cc warns dubious escape: so I
				 * use alternate \040
				 */
			? "" : "\t\t\t\t\t", *argv) ;
		stat_buf_p = STRADR lstat_buf;
		for(;;)
			{
			/* If symbolic link print symbolic atributes, then
			 * normal, else just normal atributes
			 */
			if ((stat_buf_p->st_mode & S_IFMT) == S_IFLNK)
			  printf( "Atributes of Symbolic link:\n");
			  else stat_buf_p = STRADR stat_buf ;
// JJLATER hacking here.
#ifdef sco	/* { */
			if (ustat(stat_buf_p->st_dev,&sco_buf))
				fprintf(stderr,"ustat failed\n");
			else	{
				sco_buf.f_fname[0] = sco_buf.f_fpack[0] = '\0' ;
				sco_buf.f_tfree = (daddr_t)0 ;
				sco_buf.f_tinode = (ino_t)0 ;
				}
#endif		/* } */
			printf("Size\t\t(Bytes)\t\t\t\t%lu\n",
			 /* off_t */ (long unsigned)(stat_buf_p->st_size) );
			printf(
			  "Permissions\t(Octal, U rwx, G rwx, O rwx)\t0%o\n",
				/* u_short */ (unsigned)(~S_IFMT &
					 (stat_buf_p->st_mode)) );
				/* JJLATER add rwx display, per
				 * FreeBSD 3.0.0-RELEASE/src/bin/ls/print.c
				 * which uses strmode
				 */
			switch( (stat_buf_p->st_mode) & S_IFMT)
				{
				case S_IFDIR:	cmt = "Directory" ;
					break ;
				case S_IFCHR:	cmt = "Character special" ;
					break ;
				case S_IFBLK:	cmt = "Block special" ;
					break ;
				case S_IFREG:	cmt = "Regular File" ;
					break ;
				case S_IFLNK:	cmt = "Symbolic link" ;
					break ;
#ifndef sco	/* { */
				case S_IFSOCK:	cmt = "Socket" ;
					break ;
#endif		/* } */
				case S_IFIFO:	cmt = "Fifo" ;
					break ;
				default:	cmt = "Unknown" ;
					break ;
				}
#ifndef scs	/* { */
			if (((stat_buf_p->st_mode & S_IFMT) == S_IFBLK) ||
				((stat_buf_p->st_mode & S_IFMT) == S_IFCHR) )
				/* inode that is device */
				printf(
#ifndef sco	/* { */
					/* Assume BSD4.4 */
					"Special device type pointed to (maj & min)\t%d\t\t%d\n",
					(int)/* dev_t */ major_dev_no(stat_buf_p->st_rdev),
					(int)/* dev_t */ minor_dev_no(stat_buf_p->st_rdev)
#else		/* }{ */
					"Filsys name\t\t\t\t\t%s\nFilsys pack name\t\t\t\t%s\n",
					sco_buf.f_fname , sco_buf.f_fpack
#endif		/* } */
					);
#ifndef sco	/* { */

			printf("Flags\t\t\t\t\t\t");
			if (!(stat_buf_p->st_flags)) printf("<none>\n"); else
				{
				if ((stat_buf_p->st_flags) & UF_NODUMP )
					printf("UF_NODUMP ");
					/* do not dump file */
				if ((stat_buf_p->st_flags) & UF_IMMUTABLE )
					printf("UF_IMMUTABLE ");
					/* file may not be changed */
				if ((stat_buf_p->st_flags) & UF_APPEND )
					printf("UF_APPEND ");
					/* writes to file may only append */
				if ((stat_buf_p->st_flags) & SF_ARCHIVED )
					printf("SF_ARCHIVED ");
					/* file is archived */
				if ((stat_buf_p->st_flags) & SF_IMMUTABLE )
					printf("SF_IMMUTABLE ");
					/* file may not be changed */
				if ((stat_buf_p->st_flags) & SF_APPEND )
					printf("SF_APPEND ");
					/* writes to file may only append */
				printf("\n");
				}
#endif		/* } */
#endif		/* } */

			printf("Type\t\t(File, directory, etc)\t\t%s\n",cmt) ;
/* JJLATER add `file` type info from /etc/magic here */

			printf("Links\t\t(Usually 1)\t\t\t%d\n",
				/* short */ (int)(stat_buf_p->st_nlink) );

			printf("User\t\t\t\t\t\t");
/*
 * struct passwd *getpwuid (uid)
 * getpwuid- searches for matching numerical user ID
 * The getpwuid routine searches from the beginning of the /etc/passwd file
 * until a numerical user ID matching the argument uid is found. getpwuid
 * returns a pointer to the particular passwd structure in which the argument
 * uid was found.
 * Upon successful completion, the routines getpwent, getpwuid, getpwnam, and
 * fgetpwent each return a pointer to a passwd structure. If an end-of-file or
 * an error is encountered on reading, these routines return a NULL pointer.
 */
			if ((struct passwd *)0 !=
				( pwd = getpwuid(stat_buf_p->st_uid)))
				printf("%s\n", pwd->pw_name );
			else printf("%d\n",
				/* short */ (int)(stat_buf_p->st_uid) );

			printf("Group\t\t\t\t\t\t");
			if ((struct group *)0 !=
				( grp = getgrgid(stat_buf_p->st_gid)))
				printf("%s\n", grp->gr_name );
			else printf("%d\n",
				/* short */ (int)(stat_buf_p->st_gid) );
			/* FreeBSD 3.0.0-RELEASE/src/bin/ls/ls.c uses
				user_from_uid((stat_buf_p->st_gid, 0);
				group_from_gid(stat_buf_p->st_gid, 0);
			 */

#if ( __FreeBSD_cc_version > 460001 /* 460001 is for FreeBSD-4.11 */ )	// {
/* I do not know when it came in, for other versions try
 * cpp -dM /dev/null | grep __FreeBSD_cc_version
 */
#if defined(__FreeBSD__) /* added 2007.06.22 */
			printf("Inode Created (localy)\t\t(st_Birthtime)\t%s",
				ctime((time_t *)&(stat_buf_p->st_birthtime)) );
#endif
#endif									// }

			/* If FreeBSD ports distfiles are fetched,
			   they can have strange times where st_Birthtime
				is newer than st_Mtime, eg:
	current/ports/distfiles/PyLTXML-1.3.tar.gz
	Inode Created (localy)     (st_Birthtime)  Mon Mar 16 09:53:40 2015
	Inode Changed              (st_Ctime)      Mon Mar 16 09:53:40 2015
	Data Modified (somewhere)  (st_Mtime)      Wed Jan 16 13:20:47 2008
	Data Accessed              (st_Atime)      Wed Mar 18 12:30:12 2015
			 */

			printf("Inode Changed\t\t\t(st_Ctime)\t%s",
				/* time_t */
				ctime((time_t *)&(stat_buf_p->st_ctime)) );

			printf("Data Modified (somewhere)\t(st_Mtime)\t%s",
				/* time_t */
				ctime((time_t *)&(stat_buf_p->st_mtime)) );

			printf("Data Accessed\t\t\t(st_Atime)\t%s",
				/* time_t */
				ctime((time_t *)&(stat_buf_p->st_atime)) );

#ifndef OPS_POING	/* { Omit, Too much info. for OCE end users. */

			printf("Blocks\t\t\t\t\t\t%lu\n",
				/* long unsigned */ (long)(stat_buf_p->st_blocks) );

			printf("Optimal I/O\t\t\t\t\t%lu\n",
				/* long */ (long unsigned) (stat_buf_p->st_blksize) );
#ifndef sco	/* { */

			printf("Major\t\t\t\t\t\t%d\n",
					(int)/* dev_t */
					major_dev_no(stat_buf_p->st_dev));

			printf("Minor\t\t\t\t\t\t%d\n",
					(int)/* dev_t */
					minor_dev_no(stat_buf_p->st_dev) );
#else		/* }{ */
#ifdef DEBUG_JJLATER	/* { */
	/* ifdef''d cos it generates blank fields for some reason */

			printf(
			"Filsys name\t\t\t\t\t%s\nFilsys pack name\t\t\t\t%s\n",
				sco_buf.f_fname , sco_buf.f_fpack ) ;
#endif		/* } */
#endif		/* } */

			printf("Inode Number\t\t\t\t\t%lu\n",
				/* ino_t */ (long unsigned)(stat_buf_p->st_ino) );
#ifdef SPARE	/* { */
			printf("st_spare1\t\t\t\t%d\n",
				/* int */ (int)(stat_buf_p->st_spare1) );

			printf("st_spare2\t\t\t\t%d\n",
				/* int */ (int)(stat_buf_p->st_spare2) );

			printf("st_spare3\t\t\t\t%d\n",
				/* int */ (int) (stat_buf_p->st_spare3) );

			printf("st_spare4[1]\t\t\t\t%ld\n",
				/* long */ (long)(stat_buf_p->st_spare4[1]) );

			printf("st_spare4[2]\t\t\t\t%ld\n",
				/* long */ (long)(stat_buf_p->st_spare4[2]) );
#endif		/* } */
#endif	/* } */
			if (stat_buf_p == STRADR stat_buf)
				/* A non symbolic link */ break ;

			/* We were printing a symbolic link, now run again
			   printing what the symbolic links points to */

			printf("Atributes of where symbolic link points (might or not be another sym link):\n");


			/* Find name of where sym link points. */
/*
 * /usr/include/sys/param.h does not specify if if
 * MAXPATHLEN includes space for a terminal null.
 * /usr/src/usr.bin/find/ls.c:
 *	int lnklen;
 *	char path[MAXPATHLEN];
 *
 *	if ((lnklen = readlink(name, path, MAXPATHLEN - 1)) == -1) {
 *		warn("%s", name);
 *		return;
 *	}
 *	path[lnklen] = '\0';
 *	(void)printf(" -> %s", path);
 */
			if ((lnklen =
				readlink(*argv, target, MAXPATHLEN )) == -1)
				{
				fprintf(stderr,"readlink failed %s\n",*argv);
				break ;
				}
			target[lnklen] = '\0';
			printf("Name\t%s%s\n",
				/* Shift long names to left,
					saves user scrolling */
				(strlen(*argv) > strlen(
				"Mon Jan 25 11:06:36 1999 \040 \040 \040 "))
					/* I originally put in backslashes
					 * before spaces above, to defeat
					 * tabulators on source, but gcc warns
					 * unknown escape sequence ``\ '' &
					 * SCO cc warns dubious escape: so I
					 * use alternate \040
					 */
				? "" : "\t\t\t\t\t", target) ;
			stat_buf_p = STRADR stat_buf;
			}
		if (argc > 1 ) printf("\n");	// Ready for next file name.
		}
	if (found)
		{
#ifdef __FreeBSD__	/* { */
		/* printf("Key:\n"); */
		printf("\n%s\n%s\n%s\n",
 "  st_Mtime = When data was last modified. Not set by changes of owner, group,",
 "    link count, or mode. Changed by mknod, utimes, write.",
 "    Preserved by ftp:get. Changed by sftp:get") ;

		printf("%s\n%s\n",
 "  st_Atime = When file data was last read or modified. Changed by mknod,",
 "    utimes, read, and write. Not set when a directory is searched.") ;

		printf("%s\n%s\n",
 "  st_Birthtime : Not copied to target by rdist6. Zeroed to 1970 by ftp:get.",
 "                 Changed by sftp:get" ) ;

		printf("%s\n%s\n%s\n",
 "  st_Ctime = When file status was last changed. Set by writing or changing",
 "    the i-node. Changed by chmod, chown, link, mknod, unlink, utimes, write,",
 "    ftp:get & sftp:get.  Not copied by rdist6 to target.") ;

#endif			/* } */
#ifdef	sco	/* { */
	/* From SCO man stat:
	 *	st_atime;	Time of last access
	 *	st_mtime;	Time of last data modification
	 *	st_ctime;	Time of last file status change
	 */
		printf("%s:\n\t%s\n\t%s\n",
			"st_atime",
			"Time when file data was last accessed. Changed by the following",
			"system calls: creat(S), mknod(S), pipe(S), utime(S), and read(S)."
			);
		printf("%s:\n\t%s\n\t%s\n\t%s\n",
			"st_mtime",
			"Time when data was last modified. Changed by the following system",
			"calls: creat(S), link(S), mknod(S), pipe(S), unlink(S), utime(S),",
			"and write(S)."
			);
		printf("%s:\n\t%s\n\t%s\n\t%s\n",
			"st_ctime",
			"Time when file status was last changed. Changed by the following",
			"system calls: chmod(S), chown(S), creat(S), link(S), mknod(S), pipe",
			"(S), unlink(S), utime(S), and write(S)."
			);
#endif		/* } */
		}
	return(err_no);
	}
