/*******************************************************************************
*	DLA - Date of last access.  This program implements the often requested
* date of last access feature for VMS, using the expiration date field in the
* file header.  To use DLA, you must first enable expiration dates on your
* disks thusly;
*
*		SET VOLUME/RETENTION=(9998-,9999-) DUA0:
*
*	This command will cause VMS to update the expiration date of files
* which are accessed (read or write).  The difference of 1 between 9998 and
* 9999 causes VMS to update the field only the first time it is accessed in
* a 24 hour period (to reduce overhead on frequently accessed files).  In case
* you were worried about files expiring, 9999 days is about 27.4 years... I
* myself believe that VAXen will be history by then anyway.
*
*	DLA opens each file in the wildcard expression, and calculates date
* of last access and number of days since last access.  DLA reports filename,
* allocated size, date of last access, days since last access and owner.
*
*	NOTE: Due to the fact that the expiration date has a granularity of
* minutes, and DLA only deals in whole days, you may see files that were last
* accessed on the same date, but have a different number of days since last
* access.
*
*	NOTE: Until the first time a file is accessed after the
* "SET VOLUME/RETENTION" command is issued, the file has zeroes in the EDT
* field.  For those files, DLA displays "<<Genesis>>".  DLA displays
* "<<File locked>>" or "<<Open error>>" if it is unable to open the file.  It
* displays "<<No info>>" if date translation fails.  If strange dates of
* last access appear, it could be caused by files which were copied from
* a foreign system that actually uses file expiration for it's original
* purpose (ZOUNDS!).  To fix both of these problems, it is suggested that you
* "touch" all of the files on a disk immediately after doing the
* "SET VOLUME/RETENTION" by using the GROPE program.
*
*	To build DLA, see the BUILD.COM command procedure.  To invoke DLA, first
* define a symbol;
*
*		DLA :==$SYS$LOGIN:DLA
*
* assuming that DLA.EXE resides in your home directory.  Then issue commands
* thusly;
*
*		DLA			! Look at files in current directory
*		DLA [...]		! Look at files in subsequent subdirs
*		DLA dua0:[*...]*.c	! Look at all .c files on DUA0:
*		DLA/AGE=90		! Look at all files 90 days or older
*
*	The /AGE option may appear anywhere on the command line.  Default
* wildcarding is *.*;*.
*
*******************************************************************************/

#include <descrip.h>
#include <rms.h>
#include <ssdef.h>
#include <ctype.h>

main(argc,argv)
int argc;
char *argv[];
{
	struct FAB wild_fab;		/* Used in wildcard search */
	struct NAM wild_nam;		/* Used in conjunction with wild_fab */
	struct FAB open_fab;		/* Used in retreiving file allocation */
	struct XABDAT open_xab1;	/* Used in retrieving expiration date */
	struct XABPRO open_xab2;	/* Used in retrieving owner */

	typedef int quadword[2];
	char ascii_access[13], ascii_age[17], ascii_id[30], id_txt[32];
	char parse[100], *pointer;
	$DESCRIPTOR (delta,"9999");
	$DESCRIPTOR (access_desc,ascii_access);
	$DESCRIPTOR (age_desc,ascii_age);
	$DESCRIPTOR (id_desc,ascii_id);
	quadword now, delta_bin, later, then, age, tmp;
	int edt[2], zero[2] = {0,0}, len;
	unsigned short grp, mem, num_age, select_age = 0, arg;

	char template[] = "*.*;*";	/* Default template */
	char fullname[256];		/* Input file spec given by user */
	char expanded[256];		/* File spec after logical expansion */
	char result[256];		/* Result from search */
	char last_dir[256];		/* Last directory found */
	int tot_blocks = 0, tot_files = 0, this_dirlen;
	int counter;
	register status;

/* Initialize the wildcard FAB & NAM */
	wild_fab = cc$rms_fab;
	wild_nam = cc$rms_nam;

	wild_fab.fab$l_fna = fullname;
	wild_fab.fab$b_fac = FAB$M_GET;
	wild_fab.fab$l_fop = FAB$V_NAM;
	wild_fab.fab$l_nam = &wild_nam;
	wild_fab.fab$l_dna = template;
	wild_fab.fab$b_dns = strlen(template);

	wild_nam.nam$l_esa = expanded;
	wild_nam.nam$b_ess = 255;
	wild_nam.nam$l_rsa = result;
	wild_nam.nam$b_rss = 255;

/* Initialize the FAB and XAB's for open */
	open_fab = cc$rms_fab;
	open_xab1 = cc$rms_xabdat;	/* To retrieve expiration date */
	open_xab2 = cc$rms_xabpro;	/* To retrieve owner */

	open_fab.fab$l_fna = result;
	open_fab.fab$b_fac = FAB$M_GET;
	open_fab.fab$b_shr = FAB$M_SHRGET;
	open_fab.fab$l_xab = &open_xab1;

	open_xab1.xab$l_nxt = &open_xab2;

/* Get delta time of 9999 days */
	if (((status = sys$bintim(&delta,delta_bin)) &1) != 1)
		exit(status);

/* First parse the file spec given to us */
	argv++;
	strcpy(fullname,template);
	for (arg = 1;arg < argc;arg++,argv++)
		{
		sscanf(*argv,"%s",&parse);
		if ((pointer = strchr(parse,'/')) != 0)
			{
			pointer[0] = '\0';
			pointer++;
			for (counter=0;pointer[counter] != '\0';counter++)
				pointer[counter] = toupper(pointer[counter]);
			if (strncmp(pointer,"AGE=",4) == 0)
				{
				pointer+= 4;
				sscanf(pointer,"%d",&select_age);
				}
			    else
				{
				printf("Invalid qualifier\n");
				exit(SS$_NORMAL);
				}
			}
		if (parse[0] != 0)
			strcpy (fullname, parse);
		}

	wild_fab.fab$b_fns = strlen(fullname);
	if (((status = SYS$PARSE(&wild_fab)) &1) != 1)
		{
		printf("Error %d on parse\n",status);
		exit(status);
		}

/* Main loop */
	for(;;)
		{
		if (((status = SYS$SEARCH(&wild_fab)) &1) != 1)
			{
			if (status == RMS$_NMF)
				break;
			    else
				{
				printf("Error %d on search\n",status);
				exit(status);
				}
			}

		/* Terminate some strings */
		expanded[wild_nam.nam$b_esl] = '\0';
		result[wild_nam.nam$b_rsl] = '\0';

		/* Establish the length of the directory for comparison */
		this_dirlen = ((char *)strchr(result,']') - result) + 1;

		/* Print directory spec when it changes */
		if (strncmp(last_dir,result,this_dirlen) != 0)
			{
			strncpy(last_dir,result,this_dirlen);
			last_dir[this_dirlen] = '\0';
			printf("	%s\n",last_dir);
			}
			
		/* Get the current time here, for greater accuracy */
		if (((status = sys$gettim(now)) &1) != 1)
			exit(status);

		/* Open the file */
		open_fab.fab$b_fns = strlen(result);
		if (((status = SYS$OPEN(&open_fab)) &1) != 1)
			{
			if (status == RMS$_FLK)
				{
				printf("%-33s<<File locked>>\n",
					(strchr(result,']') + 1));
				continue;
				}
			printf("%-33s<<Open error>>\n",
				(strchr(result,']') + 1));
			continue;
			}

		/* Close the file */
		if (((status = SYS$CLOSE(&open_fab)) &1) != 1)
			{
			printf("Error %d on close\n",status);
			exit(status);
			}

		/* Calculate last access date and days since last access */
		ascii_access[12] = '\0';
		ascii_age[16] = '\0';
		addquad (&zero,&open_xab1.xab$q_edt,&edt);	/* Quad move */
		if ((edt[0] == 0) && (edt[1] == 0))
		    {
		    strcpy(ascii_access,"<<Genesis>> ");
		    strcpy(ascii_age,"    ");
		    num_age = 65535;
		    }
		  else
		    {
		    addquad (&delta_bin,&edt,&then); /* Calculate DLA */
		    if ((sys$asctim(0,&access_desc,then,0)&1)!= 1)
			{
			strcpy(ascii_access,"<<No info>> ");
			strcpy(ascii_age,"0   ");
			num_age = 0;
			}
		      else
			{
			subquad(&now,&then,&tmp);  /* Get age in days */
			subquad(&zero,&tmp,&age);  /* Must be neg */
			if ((sys$asctim(0,&age_desc,age,0)&1)!= 1)
			    {
			    strcpy(ascii_age,"0   ");
			    num_age = 0;
			    }
			  else
			    {
			    ascii_age[4] = '\0';

			    /* Prevent "future" age for open files */
			    for (counter=0;ascii_age[counter] != '\0';counter++)
				if (isalpha(ascii_age[counter]) != 0)
				    {
				    strcpy(ascii_age,"    ");
				    num_age = 0;
				    break;
				    }
			    }
			}
		    sscanf(ascii_age,"%d",&num_age);
		    }
		if (num_age >= select_age)
			{
			if (((status = sys$idtoasc(open_xab2.xab$l_uic,&len,
			    &id_desc,0,0,0)) &1) != 1)
				{
				grp = open_xab2.xab$l_uic >> 16;
				mem = open_xab2.xab$l_uic & 65535;
				sprintf(id_txt,"[%04o,%04o]",grp,mem);
				}
			    else
				{
				ascii_id[len] = '\0';
				sprintf(id_txt,"[%s]",ascii_id);
				}
			printf("%-30s	%6d  %s(%s)  %s\n",
			    (strchr(result,']') + 1),open_fab.fab$l_alq,
			    ascii_access,ascii_age,id_txt);
			tot_files++;
			tot_blocks += open_fab.fab$l_alq;
			}
		}

	printf("\n	%d total blocks in %d files\n",
		tot_blocks,tot_files);

	exit(SS$_NORMAL);
}
