// Copyright (C) 1999-2006 Jean-Marc Valin & Dominic Letourneau
#include "stream_wrap.h"
#include <string>
#include "BaseException.h"
#include "Object.h"

//@implements core
using namespace std;

namespace FD {

DECLARE_TYPE(EOFObject)


fileptr_streambuf::fileptr_streambuf(FILE *_file, bool _owner, bool _isPipe)
   : file(_file)
   , owner(_owner)
   , takeFromBuf(false)
   , isPipe(_isPipe)
{

}

int fileptr_streambuf::overflow(int c)
{
   fputc(c, file);
   if (feof(file))
      return EOF;
   else
      return c;
}


streamsize fileptr_streambuf::xsputn(const char *s, streamsize n)
{
   return fwrite(s, 1, n, file);
}



int fileptr_streambuf::uflow()
{
   if (takeFromBuf)
   {
      takeFromBuf = false;
      return charBuf;
   } else {
      charBuf = fgetc(file);
      return charBuf;      
   }
}

int fileptr_streambuf::underflow()
{
   if (takeFromBuf)
   {
      return charBuf;
   } else
   {
      charBuf = fgetc(file);
      takeFromBuf = true;
      return charBuf;
   }
}

int fileptr_streambuf::pbackfail(int c)
{
   if (!takeFromBuf)
   {
      if (c != EOF)
	 charBuf = c;
      
      takeFromBuf = true;
      return charBuf;
   } else {
      return EOF;
   }
}

streamsize fileptr_streambuf::xsgetn(char *s, streamsize n)
{
   return fread(s, 1, n, file);
}





//#ifndef WIN32

#ifndef WIN32
#include <sys/wait.h>
#endif

#include <unistd.h>

fd_streambuf::fd_streambuf(int _fd, bool _owner)
   : fd(_fd)
   , owner(_owner)
   , takeFromBuf(false)
{
   
}

int fd_streambuf::overflow(int c)
{
   unsigned char _c = c;
   //FIXME: How about EOF?
   write(fd, &_c, 1);
   return c;
}




streamsize fd_streambuf::xsputn(const char *s, streamsize n)
{
   return write(fd, s, n);
}


int fd_streambuf::uflow()
{
   if (takeFromBuf)
   {
      takeFromBuf = false;
      return charBuf;
   } else {
      if (read(fd, &charBuf, 1) > 0)
         return charBuf;
      else
         return EOF;
   }
}



int fd_streambuf::underflow()
{
   if (takeFromBuf)
   {
      return charBuf;
   } else
   {
      if (read(fd, &charBuf, 1) <= 0)
      {
         return EOF;
      }
      takeFromBuf = true;
      return charBuf;
   }
}

int fd_streambuf::pbackfail(int c)
{
   if (!takeFromBuf)
   {
      if (c != EOF)
	 charBuf = c;
      
      takeFromBuf = true;
      return charBuf;
   } else {
      return EOF;
   }
}


streamsize fd_streambuf::xsgetn(char *s, streamsize n)
{
   int tot_read = 0;
   while (1) {
      int nbytes = read(fd, s+tot_read, n-tot_read);
      if (nbytes<=0)
      {
         if (tot_read)
            return tot_read;
         else
            return EOF;
      } else {
         tot_read += nbytes;
         if (tot_read == n)
            return tot_read;
      }
   }
   //Old implementation
   //cerr << "fd_streambuf::xsgetn read " << nbytes << " byte." << endl;
   //if (nbytes > 0)
   //   return nbytes;
   //else
   //{
   //   cerr << "read only " << nbytes << " bytes " << endl;
   //   return EOF;
   //}
}


#ifndef WIN32
pipe_streambuf::pipe_streambuf(const string &command, bool _waitOnClose)
   : ifd(-1)
   , ofd(-1)
   , pid(0)
   , waitOnClose(_waitOnClose)
   , takeFromBuf(false)
{
   int ifiledes[2];
   int ofiledes[2];
   if (pipe(ifiledes) || pipe(ofiledes))
      throw new GeneralException("pipe_streambuf: cannot oen pipe, out of some resource?", __FILE__, __LINE__);
   pid = fork();
   if (pid>0)
   {
      //parent
      ifd=ifiledes[0];
      ofd=ofiledes[1];
      //It's important to close the pipes we don't use, otherwise everything blocks at EOF
      close(ifiledes[1]);
      close(ofiledes[0]);
   } else if (pid==0)
   {
      //child
      //Close stdin and stdout... that's wierd, but that's how it must be done
      close(0);
      close(1);
      //FIXME: Must do error checking
      dup(ofiledes[0]);
      dup(ifiledes[1]);
      //Not sure here
      //close(ofiledes[0]);
      //close(ifiledes[1]);
      char *argv[4];
      argv[0] = "sh";
      argv[1] = "-c";
      argv[2] = const_cast<char *> (command.c_str());
      argv[3] = 0;
      //cerr << "exec\n";
      execv("/bin/sh", argv);
      throw new GeneralException("execv failed. Something really bad happened", __FILE__, __LINE__);
   } else 
      throw new GeneralException("pipe_streambuf: cannot fork process, out of some resource?", __FILE__, __LINE__);
}

pipe_streambuf::~pipe_streambuf()
{
   if (ifd != -1)
      close(ifd);
   if (ofd != -1)
      close(ofd);
   if (pid)
   {
      if (waitOnClose)
	 waitpid(pid, NULL, 0);
      else
	 waitpid(pid, NULL, WNOHANG);
   }
}

int pipe_streambuf::ll_read(void *buf, size_t count)
{
   int ori=count;
   int total=0;
   while (total<ori)
   {
      int res = 0;
      res = read(ifd, buf, count);
      if (res==0)
      {
	 //cerr << "got zero" << endl;
	 if (waitpid(pid, NULL, WNOHANG)==pid)
	 {
	    //cerr << "proc got killed?\n";
	    pid=0;
	 }
	 break;
      } else if (res==-1)
      {
	 //cerr << "ERROR" << endl;
	 perror("read");
	 break;
      }
      total+=res;
      count-=res;
   }
   return total;
}

int pipe_streambuf::ll_write(const void *buf, size_t count)
{
   int res = 0;
   res = write(ofd, buf, count);
   if (res==0)
   {
      if (waitpid(pid, NULL, WNOHANG)==pid)
      {
	 pid=0;
	 return 0;
      }
   }
   return res;
}


int pipe_streambuf::overflow(int c)
{
   unsigned char _c = c;
   //FIXME: How to handle EOF?
   if (ofd!=-1)
      return ll_write(&_c, 1);
   else
      throw new GeneralException("Cannot write to read-only pipe", __FILE__, __LINE__);
}


streamsize pipe_streambuf::xsputn(const char *s, streamsize n)
{
   if (ofd!=-1)
      return ll_write(s, n);
   else
      throw new GeneralException("Cannot write to read-only pipe", __FILE__, __LINE__);
}



int pipe_streambuf::uflow()
{
   if (ifd!=-1)
   {
      if (takeFromBuf)
      {
	 takeFromBuf = false;
	 return charBuf;
      } else {
	 if (ll_read(&charBuf, 1))
	    return charBuf;
	 else
	    return EOF;
      }
   } else
      throw new GeneralException("Cannot read from write-only pipe", __FILE__, __LINE__);
}

int pipe_streambuf::underflow()
{
   if (ifd!=-1)
   {
      if (takeFromBuf)
      {
	 return charBuf;
      } else
      {
	 takeFromBuf = true;
	 if (ll_read(&charBuf, 1))
	    return charBuf;
	 else
	    return EOF;
      }
   } else
      throw new GeneralException("Cannot read from write-only pipe", __FILE__, __LINE__);
}

int pipe_streambuf::pbackfail(int c)
{
   if (!takeFromBuf)
   {
      if (c != EOF)
	 charBuf = c;
      
      takeFromBuf = true;
      return charBuf;
   } else {
      return EOF;
   }
}

streamsize pipe_streambuf::xsgetn(char *s, streamsize n)
{
   if (ifd!=-1)
   {
      //int size = read(ifd, s, n);
      //return size;
      return ll_read(s,n);
   } else
      throw new GeneralException("Cannot read from write-only pipe", __FILE__, __LINE__);
}

#if 0
struct buff {
   int fd;
   char *data;
   int length;
};

void *send_thread(void *in)
{
   buff *b=(buff*)in;
   write(b->fd, b->data, b->length);
}
#endif

void pipe_streambuf::pipeString(const string &in, string &out)
{
#if 0
   pthread_t thr;
   buff b = {ifd, const_cast<char *>(in.c_str()), in.size()};
   pthread_create(&thr, NULL, send_thread, (void*)&b);
   int len=1024;
   char *buf;
   while (len==1024)
   {
      len=ll_read((void *)buf, 1024);
      cerr.write(buf,len);
   }
#endif
}

#endif

}//namespace FD




syntax highlighted by Code2HTML, v. 0.9.1