
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long long decodesize(unsigned char *enc);


int main(int argc, char *argv[]){
  FILE *inputfile;
  if(argc==1){
    printf("Hashtar is a very simple program which takes a command and a tar file. The command is then executed once for each file within the tar, passing the file on stdin. The outputs are all on stdout. The command is most commonly a hash program (md5sum, etc). It can't handle .tar.gz/bz2/xz, but it can accept a tar on stdin so piping easily allows for handling that situation.\n");
    printf("Usage:\n  hashtar <command> [tarfile] [detail]\n  If no file is specified or tarfile is -, stdin is assumed. The command is called via the shell, so it handles environment strings (including path) and any special characters. Longlink extension is handled correctly. If detail is set (Any string will do, it just checks argc) the start position and length of files is also output.\n");
    return(1);
  }
  if(argc>2){
    inputfile=fopen64(argv[2], "rb");
  }
  else
    inputfile=stdin;
  if(!inputfile){
    fprintf(stderr, "Could not open input file.\n");
    return(1);
  }

  unsigned char *usepipe=argv[1];
  char *fileheader=malloc(16384);
  char *fhsize=fileheader+124;
  do{
    fileheader[0]=0;
    fread(fileheader, 512, 1, inputfile);
    fhsize[12]=0; //Make quite sure there is a null terminator for the file length. This overwrites the timestamp field, but we don't need that.
    if(feof(inputfile)||fileheader[0]==0){
//      fprintf(stderr,"Found end of input file.\n");
      break; //There'll be some blank records at the end. Tar files use padding.
    }
    unsigned long long filebytes=decodesize(fhsize);
//    fprintf(stderr, "Entry '%s' %c %02llX\n", fileheader, fileheader[156], filebytes);
    if(fileheader[156]=='0'){ //Older approach: strcmp(fileheader, "././@LongLink")
      if(argc>3)
        printf("%0.15llu %0.15llu ", ftello64(inputfile), filebytes);
      fflush(stdout);
      FILE *called;
      if(usepipe){
        called=popen(usepipe, "w");
        if(!called){
          fprintf(stderr, "Pipe error. Possibly cannot allocate memory?\n");
          return(1);
        }
        if(called==(FILE*)-1){
          fprintf(stderr, "Pipe error.\n");
          return(1);
        }
      }
      while(filebytes>=16384){
        fread(fileheader, 16384, 1, inputfile);
        filebytes-=16384;
        fwrite(fileheader, 16384, 1, called);
      }
      while(filebytes>=512){
        fread(fileheader, 512, 1, inputfile);
        filebytes-=512;
        fwrite(fileheader, 512, 1, called);
      }
      if(filebytes){
        fread(fileheader, 512, 1, inputfile);
        fwrite(fileheader, filebytes, 1, called);
      }
      pclose(called); //Very important that the process exits fully - we need it to flush stdout, or things will be outputted out of order.
    }else{
      unsigned int blocks=decodesize(fhsize); //Not actually blocks yet
//      fprintf(stderr, "Skipping, size %u\n", blocks);
      if(blocks%512)
        blocks=(blocks/512)+1;
      else
        blocks=(blocks/512);                     //Now it's blocks.
      fseeko64(inputfile, (blocks)*512, SEEK_CUR);
    }
  }while(!feof(inputfile));
}

unsigned long long decodesize(unsigned char *enc){
  unsigned long long val;
  if(enc[0]&0x80){ //base256 encoding. It's a lot like ordinary bigendian.
    val=enc[0]&0x7F; //But not quite.
    int n;
    for(n=1;n<12;n++){
      val=(val*256)+enc[n];
    }
  }else
    val=strtoull(enc, NULL, 8); //This is why the timestamp was overwritten earlier.
  return(val);
}
