/*
 * open.c
 *
 * Copyright (C) 1993 Alain Knaff
 */

#define _LARGEFILE64_SOURCE
#define _GNU_SOURCE

#include "sysincludes.h"

static char *zlib_compressor[]= { COMPR, 0 };


#ifdef HAVE___LIBC_OPEN
int __libc_open(__const char *pathname, int flags, mode_t mode);
#define open __libc_open
/*ALIASF2(open, __libc_open, int, mode_t);*/
#endif


#include <stdarg.h>

int open(__const char *name, int flags, mode_t mode);

int open64 (__const char *name, int flags, ...)
{
  va_list ap;
  va_start(ap, flags);
  if(flags & O_CREAT) {
    mode_t mode = va_arg(ap, mode_t);
    return open(name, flags | O_LARGEFILE, mode);
  } else {
    return open(name, flags | O_LARGEFILE, 0);
  }
}

int open(__const char *name, int flags, mode_t mode)
{
  int fd;
  int zfd;
  int pid;
  int filetype;
  int zflags;
  int do_append;
  int do_uncompress;
  int do_create;
  int have_filetype;
  int tmp;
  int stat_result;
  int status;
  struct stat buf;
#ifdef SYS_UTIME
  struct utimbuf buf2;
#else
# ifdef SYS_UTIMES
  struct timeval buf2[2];
# endif
#endif
  int pipeout[2];

  char newname[MAXPATHLEN + MAXEXTLEN + 1];
  char _pname[MAXPATHLEN + MAXEXTLEN + 1];
  __const char *pname;
  _zlibc_init();
  filetype=have_filetype=do_append=do_uncompress=do_create=0;

  zflags = flags;
  if (flags & O_CREAT){
    zlib_initialise();
    have_filetype=1;
    if ( ! (zlib_mode & CM_DISAB )){
      filetype = zlib_getfiletype(name,-1);

      if ((flags & O_ACCMODE) == O_WRONLY &&
	  (flags & (O_TRUNC | O_EXCL)) &&
	  (filetype & PM_CREATE_COMPR))
	do_create = 1;
      else if ((flags & O_ACCMODE) == O_WRONLY &&
	       (flags & O_APPEND) &&
	       (filetype & PM_APPEND_COMPR))
	do_append=1;
      else if ((flags & O_ACCMODE ) != O_RDONLY &&
	       (filetype & PM_UNCOMPR_BEFORE_WRITE))
	do_uncompress=1;
      if (do_create | do_append | do_uncompress)
	zflags &= ~O_CREAT;
    }
  }

  fd = zlib_real_open(name,zflags,mode);

#ifdef DEBUG
  fprintf(stderr,"file opened *: %s %d %x %x %d\n", name,fd, flags, zflags,
	  errno);
#endif

  /* success to open uncompressed file */
  if ( fd >= 0 || errno != ENOENT )
    return fd;

  if (!have_filetype)
    zlib_initialise();

  if ( zlib_mode & CM_DISAB )
    return fd;

  if (!have_filetype)
    filetype = zlib_getfiletype(name,-1);

  if ( zlib_mode & CM_VERBOSE )
    fprintf(stderr,"opening %s %o\n", name,flags);

  strncpy(newname,name,MAXPATHLEN);
  strcat(newname,zlib_ext);

  /* open compressed file */

  zflags = flags;
  if ((flags & O_ACCMODE) == O_WRONLY &&
      (flags & O_TRUNC) &&
      (filetype & PM_CREATE_COMPR))
    do_create = 1;
  else if ((flags & O_ACCMODE) == O_WRONLY &&
	   (flags & O_APPEND) &&
	   (filetype & PM_APPEND_COMPR))
    do_append=1;
  else if ((flags & O_ACCMODE ) != O_RDONLY &&
	   (filetype & PM_UNCOMPR_BEFORE_WRITE)){
    zflags = O_RDONLY;
    do_uncompress=1;
  } else if ((flags & O_ACCMODE) != O_RDONLY)
    return fd;

  zfd = zlib_real_open(newname,zflags,mode);

  if ( zfd < 0 ){
    if ( flags & O_CREAT )
      return zlib_real_open(name,flags,mode);
    if ( errno == EINVAL ) /* don't want to replace perm errors */
      errno = ENOENT;
    return zfd;
  }

  if (do_append || do_create){
    /* make a pipe */
    if(pipe(pipeout) < 0 )
      return -1;
    
    /* double fork */
    switch(pid=fork()){
    case 0: /* son */
      switch(fork()){
      case 0: /* son of son */
	if (zfd == 0){
	  /* shit happens ... */
	  tmp = dup(zfd);
	  close(zfd);
	  zfd = tmp;
	}
	if (pipeout[0] != 0){
	  close(0);
	  dup(pipeout[0]);
	}
	if ( zfd != 1 ){
	  close(1);
	  dup(zfd); /* compressed file, input */
	}

	close(pipeout[0]);
	close(pipeout[1]);

	if(! (zlib_mode & CM_VERBOSE))
	  close(2);

	execvp (zlib_compressor[0], zlib_compressor);
	
	if ( zlib_mode & CM_VERBOSE ){
	  perror("exec compressor");
	  exit(1);
	}
      case -1: /* error */
	if ( zlib_mode & CM_VERBOSE )
	  perror("fork error");
	exit(1);
      default: /* father */
	exit(0); /* son of son will be taken over by init */
      }
    case -1: /* error */
      errno = ENOENT;
      return -1;
    }
    close(pipeout[0]); /* close read end of pipe */

    close(zfd);        /* close raw compressed file */
    fd=dup(pipeout[1]);
    close(pipeout[1]);
    /*wait4(pid,&status,0,0);  wait for son */
    wait(&status);
    return fd;    
  }

  if (!do_uncompress &&  (filetype & PM_READ_MASK) >= PM_HIDE_PIPE ){
    /* make a pipe */
    if(pipe(pipeout) < 0 )
      return -1;
    
    /* double fork */
    switch(pid=fork()){
    case 0: /* son */
      switch(fork()){
      case 0: /* son of son */
	if ( zfd != 0 ){
	  close(0);
	  dup(zfd); /* compressed file, input */
	}
	
	if ( pipeout[1] != 1 ){
	  close(1);
	  dup(pipeout[1]);
	}

	close(pipeout[0]);
	close(pipeout[1]);

	if(! (zlib_mode & CM_VERBOSE))
	  close(2);

	execvp ( zlib_uncompressor[0], zlib_uncompressor );
	
	if ( zlib_mode & CM_VERBOSE ){
	  perror("exec uncompressor");
	  exit(1);
	}
      case -1: /* error */
	if ( zlib_mode & CM_VERBOSE )
	  perror("fork error");
	exit(1);
      default: /* father */
	exit(0); /* son of son will be taken over by init */
      }
    case -1: /* error */
      errno = ENOENT;
      return -1;
    }
    close(pipeout[1]); /* close write end of pipe */


    close(zfd);        /* close raw compressed file */
    fd=dup(pipeout[0]);
    close(pipeout[0]);
    wait(&status);
  } else {
    stat_result=fstat(zfd, &buf);

    mode = 0400;
    if ( !do_uncompress){
      sprintf(_pname,"%s/pipe.%d",zlib_tmp,(int)getpid());
      pname = _pname;
    } else {
      mode = 0;
      pname = name;
    }
    zlib_real_unlink(pname);
    pipeout[0]=zlib_real_open(pname, O_RDWR | O_CREAT | O_EXCL, mode);
    /* temporary output file */
    if ( pipeout[0] < 0 ){
      if ( zlib_mode & CM_VERBOSE )
	perror("could not create uncompressed file");
      errno = ENOENT;
      return -1;
    }

    switch(pid=fork()){
    case 0: /* son  */
      if ( zfd != 0 ){
	close(0);
	dup(zfd); /* compressed file, input */
      }

      if ( pipeout[0] != 1 ){
	close(1);
	dup(pipeout[0]);
      }

      if(! (zlib_mode & CM_VERBOSE))
	close(2);

      execvp ( zlib_uncompressor[0], zlib_uncompressor ) ;
      if ( zlib_mode & CM_VERBOSE )
	perror("exec uncompressor");
      exit(1);
    case -1: /* error */
      errno = ENOENT;
      return -1;
    }

    wait(&status);
       
    /* touch it */
    close(pipeout[0]);

    if(do_uncompress){
      /* restore the original mode */
      if ( stat_result >= 0)
	mode = buf.st_mode;
      else
	mode = 0600;
      zlib_real_chmod(pname, mode);
    }

    if ( stat_result >= 0 ){
#ifdef SYS_UTIME
      buf2.actime = buf.st_atime;
      buf2.modtime = buf.st_mtime;
      zlib_real_utime(pname, &buf2);
#else
# ifdef SYS_UTIMES
      buf2[0].tv_sec =  buf.st_atime;
      buf2[0].tv_usec = 0;
      buf2[1].tv_sec =  buf.st_mtime;
      buf2[1].tv_usec = 0;
      zlib_real_utimes(pname, buf2);
# endif
#endif
    }
    close(zfd);        /* close raw compressed file */
    
    /* re-open it */
    fd=zlib_real_open(pname, flags, mode);

    if ( !do_uncompress)
      zlib_real_unlink(pname);  
    else {
      if ( fd>=0 && WIFEXITED(status) && WEXITSTATUS(status)==0 )
	/* only erase the original file if nothing looks fishy... */
	zlib_real_unlink(newname);
    }
  }
  return fd;
}

#undef creat
int creat(__const char *name, mode_t mode)
{
  return open(name, O_CREAT | O_WRONLY | O_TRUNC, mode);
}

#ifdef linux

FILE *fopen(const char *path, const char *mode)
{
  FILE *f;
  int fd;

  _zlibc_init();

  f=zlib_real_fopen(path, mode);
  if(f != NULL)
    return f;
  if ( zlib_mode & CM_DISAB )
    /* ZLIBC disabled */
    return NULL;
  
  if(strcmp(mode, "r"))
    /* If mode is anything else than read, don't bother */
    return NULL;

  fd = open(path, O_RDONLY, 0);
  if(fd < 0)
    return NULL;

  return fdopen(fd, mode);
}

FILE *fopen64(const char *path, const char *mode)
{
  FILE *f;
  int fd;

  _zlibc_init();

  f=zlib_real_fopen64(path, mode);
  if(f != NULL)
    return f;
  if ( zlib_mode & CM_DISAB )
    /* ZLIBC disabled */
    return NULL;
  
  if(strcmp(mode, "r"))
    /* If mode is anything else than read, don't bother */
    return NULL;

  fd = open(path, O_RDONLY, 0);
  if(fd < 0)
    return NULL;

  return fdopen(fd, mode);
}

#endif
