/*		byteyears <filelist [[+|-]options]
*
*	This program accepts a list of filenames on standard input, and produces
*	the same list of names, each preceded by its time-size since last use in
*	"byte-years".  This measure has been found to be useful in determining
*	which files are the biggest wastes of space.  A file's size in bytes is
*	multiplied by the time in years since it was last opened.  Options are:
*
*	-d<l><f>	sets debug level <l>, written to file <f>.
*
*	-a			use access time (default).
*	-c			use status-change time.
*	-m			use modification time.
*	-m<n>		don't show files under minimum measure <n>.
*	+m<n>		don't show files over  maximum measure <n>.
*
*	NOTE: filenames must be separated by \r or \n.  All other characters are
*	considered part of the filename.  The output will have a 7-digit number,
*	a tab, and a filename on each line.
*
*	BUG: The "current" time is noted only once when byteyears starts.  This
*	makes the numbers comparable, but means that a file that is changing now
*	may come out (slightly) negative.  If byteyears is reading from a slow
*	pipe, the results may be look funny.
*
*	Kludge:  Some Ultrix systems (notably PMAX release 4.0) does very strange
*	things when doubles are passed to the debug routines.  We should figure
*	out what's messed up; but for now, we kludge our way around the problem
*	by using the s*[] arrays and sprintf().
*/
#include "V.h"
#include "sys_values.h"
#include "sys_stat.h"

#define SpM 60			/* Seconds per Minute */
#define MpH 60			/* Minutes per Hour */
#define HpD 24			/* Hours per day */
#define DpY 365			/* Days per year; not exact but good enough */
#define SpY SpM*MpH*HpD*DpY	/* Seconds per year */
double spy = SpY;		/* Seconds per year as a doubleing-point number */

double min = 0.0;
#if defined(MAXDOUBLE)
double m = MAXDOUBLE;	// Was: DBL_MAX
#else 
#if defined(HUGE_VAL)
double m =  HUGE_VAL;
#else
static char*byteyears_err1 = "### MAXDOUBLE and HUGE_VAL not defined###";
double m = 1.7976931348623157e+30;
#endif
#endif

double s, t, by, now;

char   s0[15],s1[15],s2[15],s3[15];	/* Scratch arrays for building %g strings */
char   ff[] = "%g\0";
Flag   atimfl=0, ctimfl=0, mtimfl=0;
struct   stat filstat;

main(ac,av)
	char**av;
{	int   a, i;
	char *p;
	char l[BUFSIZ+1];

	ac = Vinit(ac,av);
	V3 "min=%s	m=%s",sprintf(s0,ff,min),sprintf(s1,ff,m) D;
	for (a=1; a<ac; a++) {
		switch(av[a][0]) {
		  case '-': case '+':
			switch(av[a][1]) {
			  case 'A': case 'a':
					atimfl++;
					V2 "Using access time." D;
					break;
			  case 'C': case 'c':
					ctimfl++;
					V2 "Using status-change time." D;
					break;
			  case 'D': case 'd':
				Vopt(av[a]+2);
				V3 "Debug level %d.",Vlvl D;
				continue;
			  case 'M': case 'm':
				if (av[a][2]) {
					if (sscanf(av[a]+2,"%d", &i) < 1)
						i = 1000;
					if (av[a][0] == '+') {
						m = (double)i;
						V3 "i=%d m=%s",i,sprintf(s0,ff,m) D;
					} else {
						min = (double)i;
						V3 "i=%d min=%s",i,sprintf(s0,ff,min) D;
					}
					V2 "min=%5.0f m=%5.0f",min,m D;
				} else {
					mtimfl++;
					V2 "Using modification time." D;
				}
				break;
			default:
				V1 "Unknown option \"%s\" ignored.",av[a] D;
				continue;
			}
			break;
		default:
			V1 "Unknown arg \"%s\" ignored.",av[a] D;
			break;
		}
	}
	V2 "min=%s	m=%s",sprintf(s0,ff,min),sprintf(s1,ff,m) D;
	V2 "spy=%s	SpY=%d",sprintf(s0,ff,spy),SpY D;
	Vtime();
	V4 "Start time: %08X=%lu=%s",Vtiml,Vtiml,Vtimy D;
	now = (double)Vtiml;
	while (fgets(l,BUFSIZ,stdin)) {
		i = strlen(l);
		if (i>0 && l[i-1]=='\n')
			l[--i] = 0;
		V2 "File: \"%s\"",l D;
		if (stat(l,&filstat) == 0) {
			if (Vlvl > 3)
				dumpfstat(&filstat,l);
			s = (double)filstat.st_size;
			t = (double)(now - (
				mtimfl ? filstat.st_mtime :
				ctimfl ? filstat.st_ctime :
					     filstat.st_atime));
			by = (s * t) / spy;
			V2 "%s:	s=%s	t=%s	by=%s [SpY]",
				l,
				sprintf(s0,ff,s),
				sprintf(s1,ff,t),
				sprintf(s2,ff,by) D;
			if (by >= min && by <= m)
				printf("%7.0f\t%s\n",by,l);
		} else {
			V1 "Can't access \"%s\" [Err %d=%s]",l,Erreason D;
		}
	}
	exit(0);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*	Formatted dump of file-status structure.
*/
dumpfstat(s,n)
	STat *s;
	char *n;
{
	P1 "File \"%s\""
		,n
	 D;
	P1 "     dev=%d rdev=%d ino=%d nlink=%d"
		,s->st_dev
		,s->st_rdev
		,s->st_ino
		,s->st_nlink
	 D;
	P1 "    mode=0%o uid=%d gid=%d"
		,s->st_mode
		,s->st_uid
		,s->st_gid
	 D;
	P1 "   atime: %24.24s",ctime(&s->st_atime) D;
	P1 "   mtime: %24.24s",ctime(&s->st_mtime) D;
	P1 "   ctime: %24.24s",ctime(&s->st_ctime) D;
#if defined(ESIX4) || defined(SUN)
	P1 " blksize=%d blocks=%d"
		,s->st_blksize
		,s->st_blocks
	 D;
#endif
}
