#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "Disk.h"
#include "Buffer.h"
#include "packet.h"
#define STDIN  0
#define STDOUT 1

extern bool flag_v;
static int n_forks=0;

//Name:               [open]
//Return:  Fail(0),Success(1)       
#define CURRENT_DIR_NAME_LEN 256
int ToDisk::open(const char *path, const int flag)
{
  int ret;
  int fd;
  char current_dir[CURRENT_DIR_NAME_LEN];

  if (flag_v) {
    fprintf(stderr,"opening file pathname=%s flag=0%o\n",path,flag);
  }
  if ( flag & MASK_ARCHIVE ) /* path is dir. to chdir */ {
    if (flag_v) {
      fprintf(stderr," change working directory to %s \n",path);
    }
    if ( getcwd(current_dir,CURRENT_DIR_NAME_LEN) == NULL ){
      fprintf(stderr,"ToDisk::open getcwd failed.\n");
      perror("Reason");
    }
    if ( chdir(path) == -1 ) {
      fprintf(stderr,"ToDisk::open chdir failed.\n"
	      " I expect absolute pathnames in archive and continue \n");
      perror("chdir fail Reason");
    }
    fd = STDOUT; /* set to STDOUT as a default */
  } else /* path is a filename */ {
    fd = ::open(path,O_WRONLY|O_CREAT|O_TRUNC,0600);
    if (fd==(-1)) {
      fprintf(stderr,"ToDisk::open() Filename=%s open fail.\n",path);
      perror("Reason");
      return 0;
    }
  }
   
  if (flag==0) {
    aFD=fd;
    // seqNo=0;  not used in ToDisk::
    fStat=S_BEGIN;		// not used for the moment.
    return 1;
  } else /* need exec commands */ {
    ret = pipes_forks(fd,flag,1);
    if (ret==-1) {
      return 0;
    }
    aFD  = ret;
    fStat= S_BEGIN;
    return 1;
  }
}


//Name:               [pipes_forks]
//Return:   Fail(-1), Success(PipeOutputFileDescriptor)
int ToDisk::pipes_forks(const int fd, const int flag, const int level) {
  int ret;
  int pfd[2];
  int ztype,atype;
  int nflag;
  char *command_name,*command_arg1,*command_arg2;

  if (! (flag &  (MASK_COMPRESS | MASK_ARCHIVE))) {
    fprintf(stderr,"flag=0%o is strange! level=%d \n",flag,level);
    return -1;
  }

  if (pipe(pfd) == -1) { //create pipe
    fprintf(stderr,"ToDisk::pipes_forks()  flag=0%o level=%d\n",
	    flag,level);
    perror("Reason");
    return -1;
  }

  n_forks++;
  ret=fork();
  if (ret < 0) { //Failure
    fprintf(stderr,"ToDisk::pipes_forks() fork() failed.(#=%d)\n",n_forks);
    perror("Reason");
    exit(0);
  } else if (ret == 0) /* child */  {
    if (::close(pfd[1]) == -1) {
      fprintf(stderr,"ToDisk::pipes_forks() close out pipe (level=%d)",level);
      perror("Reason");
      exit(-1);
    }
    ::close(STDIN);   // redirect stdin to pipe input
    if (dup2(pfd[0],STDIN) == -1) {
      fprintf(stderr,"ToDisk::open() dup2 (level=%d)",level);
      perror("Reason");
      exit(-1);
    }

    if ( ztype = (flag & MASK_COMPRESS) ) /*Compress type*/ {
      switch (ztype) {
      case FLAG_GZ:
	command_name="zcat";
	break;
      case FLAG_Z:
	command_name="zcat";
	break;
      default:
	fprintf(stderr,"flag=0%o is strange in compress type! level=%d\n"
		,flag,level);
	return -1;
	break;
      }

      if ( flag & MASK_ARCHIVE ) /*redirect STDOUT to PIPE*/ {
	nflag = flag & ~MASK_COMPRESS;
	ret = pipes_forks(fd,nflag,2);
	if (ret < 0) {
	  fprintf(stderr,"fail in ToDisk::open call pipes_forks(2)\n");
	  return -1;
	}
	::close(STDOUT);  // redirect stdout to pipe output;
	if (dup2(ret,STDOUT) == -1) {
	  fprintf(stderr,"ToDisk::pipes_forks() dup2 '|' (level=%d)",level);
	  perror("Reason");
	  exit(-1);
	}
	ret = execlp(command_name,command_name,NULL);
      } else /*redirect STDOUT to a file */ {
	::close (STDOUT);
	if (dup2(fd,STDOUT) == -1) {
	  fprintf(stderr,"ToDisk::open() dup2 '>'(level=%d)",level);
	  perror("Reason");
	  exit(-1);
	}
	ret = execlp(command_name,command_name,NULL);
      }
    } else if ( atype=(flag & MASK_ARCHIVE) ) /*Archive type*/ {
      switch (atype) {
	case FLAG_CPIO:
	  command_name = "cpio";
	  command_arg1 = "-i";
	  command_arg2 = "-dm";
	  break;
	case FLAG_TAR:
	  command_name = "tar";
	  if (flag_v) {
	    command_arg1 = "-xvf";
	  } else {
	    command_arg1 = "-xf";
	  }
	  command_arg2 = "-";
	  break;
	default:
	  break;
      }
      ret = execlp(command_name,command_name,command_arg1,command_arg2,NULL);
    }
    /* never come here. if succed to exec */
    fprintf(stderr,"ToDisk::open() exel failed (level=%d)\n",level);
    perror("Reason");
    exit(-1);
  }

  // parent proc.
  if (::close(pfd[0]) == -1) {
    fprintf(stderr,"ToDisk::open() close pipe pfd[0] (level=%d)\n",
	    level);
    perror("Reason");
    return 0;
  }
  return pfd[1];
}
      
//Name:               [open]
//Return:  Fail(0),Success(1)       
int FromDisk::open(const char *filename) 
{
  int ret;

  ret=::open(filename,O_RDONLY);
  if (ret==(-1)) {
    fprintf(stderr,"Filename = %s \n",filename);
    perror("FromDisk::open()");
    return 0;
  } else {
    aFD=ret;
    seqNo=0;
    fStat=S_BEGIN;
    return 1;
  }
}

#define MAX_DISK_RETRY 4
//Name:			[ read_tobuff() ]
//Return:  Fail(0),Success(1),EOF(2)
int FromDisk::read_tobuff(Buffer *buff)
{
  int ii,ret,r_bytes;

  int sum=0;
  buff->set_seqno(seqNo);
  seqNo++;

  buff->set_eof(B_NORMAL);
  r_bytes=buff->get_size();
  for (ii=0;ii<MAX_DISK_RETRY;ii++) {
    if ((ret=read(aFD,buff->get_addr(),r_bytes))<0) {
      perror("disk read");
      return(0);
    }
    sum += ret;
    r_bytes -= ret;
    if (ret==0) {
      fStat=S_EOF;
      buff->set_eof(B_EOF);
      buff->set_contsize(sum);
      return 2;			// EOF detected
    } else {
      fStat=S_MIDDLE;
    }
    if (r_bytes==0) {
      break;
    }    
  }
  if (r_bytes!=0) {
    fprintf(stderr,"read() repeated %d times but failed. buffsize=%d "
	    "remain=%d bytes.\n", MAX_DISK_RETRY,buff->get_size(),r_bytes);
    buff->set_contsize(sum);
    return 0;
  }
  buff->set_contsize(sum);
  return 1;
}

//Name:			[ write_frombuff() ]
//Return: Success(1), Fail(0);
int ToDisk::write_frombuff(Buffer *buff)
{
  int ii,ret,w_bytes;

  w_bytes=buff->get_contsize();
  
  for (ii=0;ii<MAX_DISK_RETRY;ii++) {
    if ((ret=write(aFD,buff->get_addr(),w_bytes)) < 0) {
      perror("disk write");
      return 0;
    }
    w_bytes -= ret;
    if (w_bytes==0) break;
  }
  if (w_bytes>0) {
    fprintf(stderr,"write() repeated %d times but failed. buffsize=%d "
	    "remain=%d bytes.\n", MAX_DISK_RETRY,buff->get_size(),w_bytes);
    return 0;
  } else {
    /*
    if ( fdatasync(aFD) < 0 ) {
      perror("fdatasync at write_fromBuffer()@Disk.cpp \n");
      exit(3);
    }
    */
    return 1;
  }
}











syntax highlighted by Code2HTML, v. 0.9.1