#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <fcntl.h>
#include <ctype.h>

#include <sys/ioctl.h>
#include <tmc/stcreg.h>

extern int errno;
main(ac, av)
int ac;
char **av;
{
    struct timeval starttime, endtime;
    double timediff, rate;
    char *buf;
    int bufsize;
    int nwrites, nbytes;
    register int i, ret;
    int read(), write();
    int (*xfr)();
    int fd;
    int nloops;
    int saved_errno;
    int blksize;

    if (ac < 3) {
    usage:
	printf("usage: %s { read | write } device [blksize] [nwrites | \"fixed\"]\n", 
	       av[0]);
	exit(1);
    }
    if (strcmp(av[1], "read") == 0)
	xfr = read;
    else if (strcmp(av[1], "write") == 0)
	xfr = write;
    else 
	goto usage;

    if (xfr == read) 
	fd = open(av[2], O_RDONLY);
    else
	fd = open(av[2], O_WRONLY|O_CREAT|O_TRUNC, 0666);

    if (fd < 0) {
	perror(av[2]);
	exit(1);
    }
     
    if (ac < 4) {
	bufsize = 32*1024;
    } else {
	bufsize = argtoi(av[3]);
    }

    if (ac < 5) {
	nwrites = 128;
	blksize = 0;
    } else {
	if (strcmp(av[4], "fixed") == 0) {
	    blksize = bufsize;
	    bufsize = ((1024 * 1024) / blksize) * blksize ;
	    nwrites = 10;
	} else {
	    nwrites = atoi(av[4]);
	}
    }
	

    buf = (char *)memalign(4, bufsize);
    if (buf == NULL) {
	perror("malloc");
	exit(1);
    }

    { 
	register int *p;
	register i = 0;

	for (p = (int *)buf; p < (int *)&buf[bufsize]; p++) 
	    *p = i++;
    }

    if (blksize) {
	int foo;
	if (ioctl(fd, STCIOC_SET_BLKSIZE, &blksize) < 0)
	    perror("set blksize");
	if (ioctl(fd, STCIOC_GET_BLKSIZE, &foo) < 0)
	    perror("get blksize");
	printf("blocksize set to %d\n", foo);
    }
    
    if (gettimeofday(&starttime, 0) < 0) {
	perror("gettimeofday");
    }
    ret = 1;
    nbytes = 0;
    for (i = 0; (i < nwrites) && (ret >= 0); i++) {
	ret = (*xfr)(fd, buf, bufsize);
        nbytes += ret;
    }
    saved_errno = errno;
    if (gettimeofday(&endtime, 0) < 0) {
	perror("gettimeofday");
    }
    close(fd);
    if (ret < 0) {
	errno = saved_errno;
	perror("data transfer error");
	exit(1);
    }
    timediff = (double) tv_usec_diff(&starttime, &endtime)
	/ 1e6;

    rate = (nbytes / timediff) / 1e6;

    printf("%d bytes in %.2f seconds (%.4f mbytes/sec)  \n", 
	   nbytes, timediff, rate);
}

static long
tv_usec_diff(tv0, tv1)
struct timeval *tv0, *tv1;
{
  long val;

  val = (tv1->tv_sec - tv0->tv_sec) * 1000000;
  val += tv1->tv_usec - tv0->tv_usec;
  return val;
}
/*
 * ARG TO I
 * Sort of like atoi(), but allows nK for (n * 1024) and nM for (nK * 1024).
 * Positive ints only, returns -1 on error.
 */
int
argtoi(ap)
register char *ap;
{
	register result = 0;

	while (*ap != '\0') {
		if (isdigit(*ap)) {
			result *= 10;
			result += *ap - '0';
		}
		else if (*ap == 'k' || *ap == 'K')
			result *= 1024;
		else if (*ap == 'm' || *ap == 'M')
			result *= 1024*1024;
		else
			return -1;
		++ap;
	}
	return result;
}
