#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include "config.h"
#include "zlib/zlib.h"

#include <sys/types.h>
#include <sys/stat.h>

static int fputlong (FILE* f, unsigned long x)
{
  int n;
  for (n = 0; n < 4; n++) {
    if (fputc((int)(x & 0xff), f) == EOF) return -1;
    x >>= 8;
  }
  return 0;
}

time_t get_mtime(FILE* f)
{
  struct stat s;

  if (fstat(fileno(f), &s) == 0) return s.st_mtime;
  else return 0;
}

FILE* optimal_gzip(FILE* ffin, const char* fout, size_t blocksize)
{
  time_t mtime = get_mtime(ffin);
  FILE* ffout = fopen(fout,"wb+");

  if (!ffout) { perror("open"); return NULL; }

  fwrite("\x1f\x8b\x08\x00",4,1,ffout);
  fputlong(ffout,mtime);
  fwrite("\x00\x03",2,1,ffout);
  {
    z_stream zs;
    unsigned char* inbuf = malloc(blocksize);
    unsigned char* outbuf = malloc(blocksize+500);
    int err, r;
    unsigned long crc = crc32(0L, Z_NULL, 0);
    
    zs.zalloc = Z_NULL;
    zs.zfree = Z_NULL;
    zs.opaque = NULL;
    zs.total_in = 0;
    zs.total_out = 0;

    /* windowBits is passed < 0 to suppress zlib header */
    err = deflateInit2(&zs, 9,
		       Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);

    for (r = 1; r > 0;) {
      r = fread(inbuf, 1, blocksize, ffin);

      if (r < 0) break;

      crc = crc32(crc, inbuf, r);

      zs.next_in = inbuf;
      zs.avail_in = r;
      zs.next_out = outbuf;
      zs.avail_out = blocksize+500;

      err = deflate(&zs, r ? Z_PARTIAL_FLUSH : Z_FINISH);
      switch (err) {
      case Z_STREAM_END:
      case Z_OK:
	{
	  int w = zs.next_out - outbuf;
	  
	  if (w != fwrite(outbuf, 1, w, ffout)) { perror("write"); r = -1; }
	}
	break;
      default:
	fprintf(stderr,"zlib error: %s (%d)\n",zs.msg, err);
	r = -1;
      }
    }

    fputlong(ffout, crc);
    fputlong(ffout, zs.total_in);
    fflush(ffout);
    free(outbuf);
    free(inbuf);
    if (fclose(ffin) != 0 || r != 0) { fclose(ffout); return NULL; }
  }
  rewind(ffout);
  return ffout;
}


syntax highlighted by Code2HTML, v. 0.9.1