/*
 * Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
 *
 * Copyright (c) 1999
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1999
 * Boris Fomitchev
 *
 * This material is provided "as is", with absolutely no warranty expressed
 * or implied. Any use is at your own risk.
 *
 * Permission to use or copy this software for any purpose is hereby granted
 * without fee, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 *
 */

#include "stlport_prefix.h"
#include "stdio_streambuf.h"
// #include "file_streambuf.h"

#ifdef _STLP_UNIX
#  include <sys/types.h>
#  include <sys/stat.h>
#endif /* __unix */

#include <fstream>
#include <limits>
#include "fstream_impl.h"

#if defined (_STLP_USE_WIN32_IO) && !defined(_STLP_WCE)
#  if defined (__BORLANDC__)
// #  include <cio.h>
#    include <cfcntl.h>
#  else
#    include <io.h>
#    include <fcntl.h>
#  endif
#  include <sys/stat.h>
#endif

_STLP_BEGIN_NAMESPACE
_STLP_MOVE_TO_PRIV_NAMESPACE

// Compare with streamoff definition in stl/char_traits.h!

#ifdef _STLP_USE_DEFAULT_FILE_OFFSET
#  define FSEEK fseek
#  define FTELL ftell
#  define FSTAT fstat
#  define STAT  stat
#  define FSETPOS  fsetpos
#  define FGETPOS  fgetpos
#  define FPOS_T   fpos_t
#elif defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE) /* || defined(__USE_FILE_OFFSET64) */ \
     /* || (defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)) */ /* || defined(__sgi) */
#  define FSEEK fseeko64
#  define FTELL ftello64
#  define FSTAT fstat64
#  define STAT  stat64
#  define FSETPOS  fsetpos64
#  define FGETPOS  fgetpos64
#  define FPOS_T   fpos64_t
#else
#  define FSEEK fseek
#  define FTELL ftell
#  define FSTAT fstat
#  define STAT  stat
#  define FSETPOS  fsetpos
#  define FGETPOS  fgetpos
#  define FPOS_T   fpos_t
#endif

//----------------------------------------------------------------------
// Class stdio_streambuf_base

stdio_streambuf_base::stdio_streambuf_base(FILE* file)
    : /* _STLP_STD::FILE_basic_streambuf(file, 0), */
    _M_file(file)
{}

stdio_streambuf_base::~stdio_streambuf_base() {
  _STLP_VENDOR_CSTD::fflush(_M_file);
}

_STLP_STD::streambuf* stdio_streambuf_base::setbuf(char* s, streamsize n) {
#ifdef _STLP_WCE
  // no buffering in windows ce .NET
#else
  size_t __n_size_t = (sizeof(streamsize) > sizeof(size_t)) ? __STATIC_CAST(size_t, (min)(__STATIC_CAST(streamsize, (numeric_limits<size_t>::max)()), n))
                                                            : __STATIC_CAST(size_t, n);
  _STLP_VENDOR_CSTD::setvbuf(_M_file, s, (s == 0 && n == 0) ? _IONBF : _IOFBF, __n_size_t);
#endif
  return this;
}

stdio_streambuf_base::pos_type
stdio_streambuf_base::seekoff(off_type off, ios_base::seekdir dir,
                              ios_base::openmode /* mode */) {
  int whence;
  switch (dir) {
  case ios_base::beg:
    whence = SEEK_SET;
    break;
  case ios_base::cur:
    whence = SEEK_CUR;
    break;
  case ios_base::end:
    whence = SEEK_END;
    break;
  default:
    return pos_type(-1);
  }

  if (off <= numeric_limits<off_type>::max() && FSEEK(_M_file, off, whence) == 0) {
    FPOS_T pos;
    FGETPOS(_M_file, &pos);
    // added 21 june 00 mdb,rjf,wjs: glibc 2.2 changed fpos_t to be a struct instead
    // of a primitive type
#if (defined (__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2))))
    return pos_type((streamoff)pos.__pos);
#elif defined (__ISCPP__) || defined (__MVS__) || defined (__OS400__)
    return pos_type(pos.__fpos_elem[ 0 ]);
#elif defined (__EMX__)
    return pos_type((streamoff)pos._pos);
#else
    return pos_type(pos);
#endif
  }
  else
    return pos_type(-1);
}


stdio_streambuf_base::pos_type
stdio_streambuf_base::seekpos(pos_type pos, ios_base::openmode /* mode */) {
  // added 21 june 00 mdb,rjf,wjs: glibc 2.2 changed fpos_t to be a struct instead
  // of a primitive type
#if (defined(__GLIBC__) && ( (__GLIBC__ > 2) || ( (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2) ) ) )
  FPOS_T p;
  p.__pos = pos;
#  ifdef _STLP_USE_UCLIBC
#    ifdef __STDIO_MBSTATE
  memset( &(p.__mbstate), 0, sizeof(p.__mbstate) );
#    endif
#    ifdef __STDIO_WIDE
  p.mblen_pending = 0;
#    endif
#  else
  memset( &(p.__state), 0, sizeof(p.__state) );
#  endif
#elif defined (__MVS__) || defined (__OS400__)
  FPOS_T p;
  p.__fpos_elem[0] = pos;
#elif defined(__EMX__)
  FPOS_T p;
  p._pos = pos;
  memset( &(p._mbstate), 0, sizeof(p._mbstate) );
#else
  FPOS_T p(pos);
#endif

  return FSETPOS(_M_file, &p) == 0 ? pos : pos_type(-1);
}

int stdio_streambuf_base::sync() {
  return _STLP_VENDOR_CSTD::fflush(_M_file) == 0 ? 0 : -1;
}

//----------------------------------------------------------------------
// Class stdio_istreambuf

stdio_istreambuf::~stdio_istreambuf() {}

streamsize stdio_istreambuf::showmanyc() {
  if (feof(_M_file))
    return -1;
  else {
#ifdef __SYMBIAN32__
	  int fd = fileno(_M_file);
#else
    int fd = _FILE_fd(_M_file);
#endif
#ifdef _STLP_WCE
   (fd); // prevent warning about unused variable
// not sure if i can mix win32 io mode with ftell but time will show
// cannot use WIN32_IO implementation since missing stat
    streamsize tmp = FTELL(_M_file);
    FSEEK(_M_file, 0, SEEK_END);
    streamoff size= FTELL(_M_file)-tmp;
    FSEEK(_M_file, tmp, SEEK_SET);
#elif defined (_STLP_USE_WIN32_IO)
    // in this case, __file_size works with Win32 fh , not libc one
    streamoff size;
    struct stat buf;
    if(FSTAT(fd, &buf) == 0 && ( _S_IFREG & buf.st_mode ) )
      size = ( buf.st_size > 0  ? buf.st_size : 0);
    else
      size = 0;
#else
    streamoff size = __file_size(fd);
#endif
    // fbp : we can use ftell as this flavour always use stdio.
    streamsize pos = FTELL(_M_file);
    return pos >= 0 && size > pos ? size - pos : 0;
  }
}

stdio_istreambuf::int_type stdio_istreambuf::underflow()
{
#ifdef _STLP_WCE
  int c = fgetc(_M_file);
#else
  int c = getc(_M_file);
#endif
  if (c != EOF) {
    _STLP_VENDOR_CSTD::ungetc(c, _M_file);
    return c;
  }
  else
    return traits_type::eof();
}

stdio_istreambuf::int_type stdio_istreambuf::uflow() {
#ifdef _STLP_WCE
  int c = fgetc(_M_file);
#else
  int c = getc(_M_file);
#endif
  return c != EOF ? c : traits_type::eof();
}

stdio_istreambuf::int_type stdio_istreambuf::pbackfail(int_type c) {
  if (c != traits_type::eof()) {
    int result = _STLP_VENDOR_CSTD::ungetc(c, _M_file);
    return result != EOF ? result : traits_type::eof();
  }
  else{
    if (this->eback() < this->gptr()) {
      this->gbump(-1);
      return traits_type::not_eof(c);
    }
    else
      return traits_type::eof();
  }
}

//----------------------------------------------------------------------
// Class stdio_ostreambuf

stdio_ostreambuf::~stdio_ostreambuf() {}

streamsize stdio_ostreambuf::showmanyc() {
  return -1;
}

stdio_ostreambuf::int_type stdio_ostreambuf::overflow(int_type c) {
  // Write the existing buffer, without writing any additional character.
  if (c == traits_type::eof()) {
    // Do we have a buffer to write?
    ptrdiff_t unwritten = this->pptr() - this->pbase();
    if (unwritten != 0) {
      _STLP_VENDOR_CSTD::fflush(_M_file);
      // Test if the write succeeded.
      if (this->pptr() - this->pbase() < unwritten)
        return traits_type::not_eof(c);
      else
        return traits_type::eof();
    }

    // We always succeed if we don't have to do anything.
    else
      return traits_type::not_eof(c);
  }

  // Write the character c, and whatever else might be in the buffer.
  else {
#ifdef _STLP_WCE
    int result = fputc(c, _M_file);
#else
    int result = putc(c, _M_file);
#endif
    return result != EOF ? result : traits_type::eof();
  }
}

_STLP_MOVE_TO_STD_NAMESPACE
_STLP_END_NAMESPACE

// Local Variables:
// mode:C++
// End:

