/*
 * timecrypt -- time UNIX password routine
 *
 * compile-time option:
 *	ALONE	define this if you DON'T load the des library
 *
 * run-time options:
 * -r<number>	repeat this run <number> times
 * -t<number>	each iteration is to run for <number> seconds
 * -s		use the short version (omits the final transformation)
 *
 * (c) Copyright 1987 by Matt Bishop and the Universities Space Research
 *			 Association.
 * This work was funded by grant NCC 2-398 from the National Aeronautics
 * and Space Administration to the Universities Space Research Association.
 *
 * Author's Address:
 *		Matt Bishop
 *		Research Institute for Advanced Computer Science
 *		NASA Ames Research Center
 *		Moffett Field, CA  94035
 * ARPANET:	mab@riacs.edu, mab@icarus.riacs.edu
 * CSNET:	mab@riacs.edu
 * UUCP:	...!decvax!decwrl!riacs!mab
 *		...!ihnp4!ames!riacs!mab
 *		...!ucbvax!ames!riacs!mab
 */
#include <stdio.h>

#define NITER	1	/* number of loops */
#define NSEC	10	/* seconds per loop */
#define MAXKEYS	8192

char *progname = "timing DES";	/* program name */
int niters = NITER;	/* number of iterations */
int nsecs = NSEC;	/* seconds per iteration */
int shortcrypt = 0;	/* use the quick interface */
int go_on;		/* 1 when loop is to stop */

/*
 * timing program
 */
main(argc, argv)
int argc;		/* Number of args on command line */
char *argv[];		/* Those command line args */
{
	register int i;		/* Counters in for loops */
	register int count;	/* couints number of loops completed */
	char passwd[MAXKEYS][9];		/* Bit form of chars in mesg */
	char salt[MAXKEYS][3];		/* Used to hold input, output, key chars */
	char encpwd[13];
	int ntests;

	/*
	 * initialize counter, standard interface, and zero key
	 */
	shortcrypt = 0;

	/*
	 * process the argument list
	 */
	progname = argv[0];
	for(i = 1; i < argc; i++){
		if (strncmp(argv[i], "-r", 2) == 0){
			if ((count = atoi(&argv[i][2])) < 1){
				fprintf(stderr, "%s: bad repeat count %s\n",
					progname, &argv[i][2]);
				exit(1);
			}
			niters = count;
		}
		else if (strncmp(argv[i], "-t", 2) == 0){
			if ((count = atoi(&argv[i][2])) < 1){
				fprintf(stderr, "%s: bad number of seconds %s\n",
					progname, &argv[i][2]);
				exit(1);
			}
			nsecs = count;
		}
		else if (strcmp(argv[i], "-s") == 0)
			shortcrypt = 1;
		else{
			fprintf(stderr, "%s: bad option %s\n",
					progname, argv[i]);
			exit(1);
		}
	}
	/*
	 * Get the passwords
	 */
	ntests = 0;
	while(scanf("%s%s%s",encpwd, salt[ntests], passwd[ntests]) == 3)
	  ntests++;
	fprintf(stderr, "Number of tests = %d\n", ntests);

	/*
	 * do this niters times to get a better idea
	 */
	prheader();
	for(i = 0; i < niters; i++){
		/*
		 * loop through the transformation
		 */
#ifndef ALONE
		if (shortcrypt){
			/*
			 * start counting
			 * set timer to interrupt after nsecs seconds
			 */
		  fprintf(stderr, "Doing shortcrypt timing.\n");
			count = 0;
			go_on = 1;
			time_start(nsecs);

			for( ; go_on > 0; count++){
				/*
				 * time the shortcrypt password interface
				 */
				(void) shc_crypt(passwd, salt, ntests);
			}
			/*
			 * end counting
			 */
			time_end(count);
		}
		else{
#endif
			/*
			 * start counting
			 * set timer to interrupt after nsecs seconds
			 */
			count = 0;
			go_on = 1;
			time_start(nsecs);

			for( ; go_on > 0; count++){
				/*
				 * time the password routine
				 */
				(void) crypt(passwd, salt);
			}
			/*
			 * end counting
			 */
			time_end(count);
#ifndef ALONE
		}
#endif

		/*
		 * compensate for the loop
		 * this assumes increment and decrement cost the same
		 */
		tick();
		for( ; count > 0; count--);
		tock();

		/*
		 * print the statistics
		 */
		time_print();
	}

	/*
	 * say goodnight, Dick!
	 */
	exit(0);

}

prheader()
{
	/*
	 * print title information
	 */
	printf("CRYPT Timings (%s): ",
		shortcrypt ? "short" : "standard");
#ifdef ALONE
	printf("standard UNIX crypt function\n");
#else
	putchar('\n');
#endif
	printf("%d intervals of approximately %d (user/virtual) seconds used\n",
		niters, nsecs);
	/*
	 * compute and print total time
	 */
	printf("total time\ttotal count\t  run/sec \t sec/run  \n");
}

/*============== timing routines ===============*/

#ifdef BSD4
#	include <signal.h>
#	include <sys/time.h>
#	include <sys/resource.h>
#	define	ALARMSIG SIGVTALRM
#	define	utimelt	ru_utime	/* user time element */
#	define	cvttime(x)	/* convert a time to a double */\
		((double)((x).tv_sec)+(((double)((x).tv_usec))/1.0e6))
#	define	gettime(x)	/* get the time used */\
		getrusage(0, x);
#	define	inittime(x,y)	/* set the timer limit */\
		(x)->it_interval.tv_sec = 0L; (x)->it_interval.tv_usec = 0L;\
		(x)->it_value.tv_sec = (long) (y); (x)->it_value.tv_usec = 0L;
#	define	settime(x)	/* set the timer itself */\
		setitimer(ITIMER_VIRTUAL, &(x), (struct itimer *)NULL)
typedef struct rusage TRES;	/* type returned by gettime */
typedef struct itimerval TIMER;	/* type to set timer */
#endif
#ifdef SYSV
#	include <sys/types.h>
#	include <sys/param.h>
#	include <sys/times.h>
#	include <signal.h>
#	define	ALARMSIG SIGALRM
#	define	utimelt	tms_utime	/* user time element */
#	define	cvttime(x)	/* convert a time to a double */\
		((double)(x)/(double)HZ)
#	define	gettime(x)	/* get the time used */\
		times(x);
#	define	inittime(x,y)	/* set the timer limit */\
		*(x) = (unsigned) (2 * (y));
#	define	settime(x)	/* set the timer itself */\
		alarm(x)
typedef struct tms TRES;	/* type returned by gettime */
typedef unsigned TIMER;		/* type to set timer */
#endif

/*
 * These are the variables that hold the times and resources.
 */
TRES res1;		/* resources when timing begins */
TRES res2;		/* resources when timing ends */
double tottim = 0.0;	/* total (virtual/user) time */
long totcnt = 0L;	/* total number of calls */

/*
 * handle a timer interrupt
 */
bing()
{
	go_on = 0;
}
		
/*
 * This sets up an interrupt to occur after arg seconds of virtual
 * (user + system) time.  This also grabs the current time for more
 * accurate resolution.
 */
time_start(arg)
int arg;
{
	TIMER ringat;			/* when to interrupt */

	/*
	 * set up the alarm
	 */
	(void) signal(ALARMSIG, bing);

	/*
	 * initialize the timer and the counts
	 */
	inittime(&ringat, arg);
	tottim = 0.0;
	totcnt = 0L;

	/*
	 * get the current resource usage
	 */
	gettime(&res1);

	/*
	 * set up the interrupt
	 */
	if (settime(ringat) < 0){
		perror("timer problem");
		exit(1);
	}
}

/*
 * end timer, grab and save statistics
 */
time_end(count)
int count;
{
	/*
	 * get the current resource usage
	 */
	gettime(&res2);

	/*
	 * adjust the time and count accordingly
	 */
	tottim += cvttime(res2.utimelt) - cvttime(res1.utimelt);
	totcnt += count;
}

/*
 * start the timer
 */
tick()
{
	/*
	 * get the current resource usage
	 */
	gettime(&res1);
}

/*
 * end timer, subtract the relevant time
 */
tock()
{
	/*
	 * get the current resource usage
	 */
	gettime(&res2);

	/*
	 * adjust the time and count accordingly
	 */
	tottim -= cvttime(res2.utimelt) - cvttime(res1.utimelt);
}

/*
 * start the timer

/*
 * analyze and print the results
 */
time_print()
{
	/*
	 * compute and print total time
	 */
	printf("%10.3e\t%11d\t%10.3e\t%10.3e\n",
		tottim, totcnt, ((double)totcnt)/tottim, tottim/((double)totcnt));
}

