#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#ifdef __hpux
#include <sys/scsi.h>
#else
#include <linux/unistd.h>
#endif

unsigned long blocksize = 4096, block = 0, end_block = ULONG_MAX;
int step = 1, zero = 0, verbose = 0, async = 0;
char *buffer;

#ifdef linux
_syscall5(int,_llseek,uint,fd,ulong,hi,ulong,lo,loff_t*,res,uint,wh);
#endif

void sseek(int fd,unsigned long where) {
#ifdef linux
  loff_t res;
  if (-1 != _llseek(fd, 0, where, &res, SEEK_SET))
#else
  if (-1 != lseek(fd, where, SEEK_SET))
#endif
    return;
  fprintf(stderr,"error: llseek(%d,%lu): %s\n",fd,where,strerror(errno));
  exit(1);
}

int main(int argc,char **argv) {
  int i,rr,ww,vf;

  for (i = 1; i < argc; ++i) {
    if (!strcmp(argv[i],"-a")) {
      async = 1;
    } else if (!strcmp(argv[i],"-b") && argc > i) {
      blocksize = atoi(argv[++i]);
      continue;
    } else if (!strcmp(argv[i],"-s") && argc > 1) {
      block = strtoul(argv[++i],NULL,10);
      continue;
    } else if (!strcmp(argv[i],"-e") && argc > 1) {
      end_block = strtoul(argv[++i],NULL,10);
      continue;
    } else if (!strcmp(argv[i],"-d") && argc > 1) {
      step = strtol(argv[++i],NULL,10);
      continue;
    } else if (!strcmp(argv[i],"-v")) {
      ++verbose;
    } else {
      fprintf(stderr,"usage: %s [-a] [-v]* [-b bsize] [-s start] [-d step] [-e end]"
                     "\n",argv[0]);
      return 2;
    }
  }

  fprintf(stderr,"ddd v0.4 starting... blocksize is %ld; range = [%lu, %lu].\n",
          blocksize,block,end_block);

  buffer = malloc(blocksize);
  if (!buffer) {
    fprintf(stderr,"malloc failure -- go suck\n");
    return 1;
  }

#ifdef __hpux
  if (!async && ioctl(1,SIOC_SET_IR,&zero))
    fprintf(stderr,"warning: ioctl(SIOC_SET_IR) failed: %s\n",strerror(errno));
#endif

  if (!async && -1 == fcntl(1,F_SETFL,O_SYNC))
    fprintf(stderr,"warning: fcntl(O_SYNC) failed: %s\n",strerror(errno));

  for(;;) {
    vf = (verbose > 1 || ((verbose > 0) && (block % 1000) == 0));
    fflush(stderr);
    sseek(0,block * blocksize);
    sseek(1,block * blocksize);
    if (vf) fprintf(stderr,"block %lu (%lu): ",block,block * blocksize);
    rr = read(0,buffer,blocksize);
    if (rr == 0) {
      if (verbose) {
        if (!vf) fprintf(stderr,"block %lu: ",block);
        fprintf(stderr,"read EOF.\n");
      }
      break;
    }
    if (rr == -1) {
      if (!vf) fprintf(stderr,"block %lu: ",block);
      fprintf(stderr,"read error: %s\n",strerror(errno));
      memset(buffer,0,blocksize);
    } else if (rr != blocksize) {
      fprintf(stderr,"short read: %d bytes... ",rr);
      memset(buffer + rr,0,blocksize - rr);
    } else
      if (vf) fprintf(stderr,"read OK... ");

    ww = write(1,buffer,blocksize);
    if (ww == -1) {
      if (!verbose) fprintf(stderr,"block %lu: ",block);
      fprintf(stderr,"write error: %s\n",strerror(errno));
      goto error;
    }
    if (blocksize != ww) {
      fprintf(stderr,"short write: %d bytes.\n",ww);
      goto error;
    }
    if (vf) fprintf(stderr,"write OK.\n");

  error:
    if (block == end_block) {
      if (verbose) fprintf(stderr,"end reached.\n");
      break;
    }
    block += step;
  }

  return 0;
}
