-
Nat Goodspeed authored
To be more accurate, this changeset doesn't actually eliminate the dependency: it eliminates the use cases for the llifstream / llofstream feature that requires it. Currently you can construct an llifstream or llofstream from an open LLFILE* file handle (or, except on Windows, an int file descriptor). But rather than containing a streambuf implementation based on FILE*, llfile.h relies on the fact that the Windows std::filebuf happens to support that as a nonstandard extension; also on a nonstandard GNU extension __gnu_cxx::stdio_filebuf<char>. To move from GNU libstdc++ to clang's libc++ (the direction on Mac), we could code a streambuf that supports FILE*. But before doing that, it's worth asking whether anyone actually uses this questionable feature. In fact there were only two methods: LLWearable::exportFile() and importFile() -- and only one call to either, in LLViewerWearable::saveNewAsset(). The code in saveNewAsset() opened the LLFILE* immediately before calling exportFile(), meaning we could reasonably push the open operation down into exportFile(). That logic was complex anyway due to the need for the caller to close the LLFILE* regardless of the success of the exportFile(). Change LLWearable::exportFile() and importFile() to accept a std::string filename rather than an open LLFILE*. Change LLViewerWearable::saveNewAsset() to simply call exportFile(filename) rather than horsing around with an LLFILE* handle. (This improves the code in another way too: it encapsulates the need to open the relevant file in binary mode. Previously, each caller had to remember to do that.) To prevent inadvertent reintroduction of ll[io]fstream(LLFILE*) code, add llstream_LLFILE preprocessor macro (default 0) to control access to the relevant constructors. Also suppress rdbuf() override, the only method whose signature references llstdio_filebuf.
Nat Goodspeed authoredTo be more accurate, this changeset doesn't actually eliminate the dependency: it eliminates the use cases for the llifstream / llofstream feature that requires it. Currently you can construct an llifstream or llofstream from an open LLFILE* file handle (or, except on Windows, an int file descriptor). But rather than containing a streambuf implementation based on FILE*, llfile.h relies on the fact that the Windows std::filebuf happens to support that as a nonstandard extension; also on a nonstandard GNU extension __gnu_cxx::stdio_filebuf<char>. To move from GNU libstdc++ to clang's libc++ (the direction on Mac), we could code a streambuf that supports FILE*. But before doing that, it's worth asking whether anyone actually uses this questionable feature. In fact there were only two methods: LLWearable::exportFile() and importFile() -- and only one call to either, in LLViewerWearable::saveNewAsset(). The code in saveNewAsset() opened the LLFILE* immediately before calling exportFile(), meaning we could reasonably push the open operation down into exportFile(). That logic was complex anyway due to the need for the caller to close the LLFILE* regardless of the success of the exportFile(). Change LLWearable::exportFile() and importFile() to accept a std::string filename rather than an open LLFILE*. Change LLViewerWearable::saveNewAsset() to simply call exportFile(filename) rather than horsing around with an LLFILE* handle. (This improves the code in another way too: it encapsulates the need to open the relevant file in binary mode. Previously, each caller had to remember to do that.) To prevent inadvertent reintroduction of ll[io]fstream(LLFILE*) code, add llstream_LLFILE preprocessor macro (default 0) to control access to the relevant constructors. Also suppress rdbuf() override, the only method whose signature references llstdio_filebuf.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
llfile.cpp 28.78 KiB
/**
* @file llfile.cpp
* @author Michael Schlachter
* @date 2006-03-23
* @brief Implementation of cross-platform POSIX file buffer and c++
* stream classes.
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if LL_WINDOWS
#include "llwin32headerslean.h"
#include <stdlib.h> // Windows errno
#else
#include <errno.h>
#endif
#include "linden_common.h"
#include "llfile.h"
#include "llstring.h"
#include "llerror.h"
#include "stringize.h"
using namespace std;
static std::string empty;
// Many of the methods below use OS-level functions that mess with errno. Wrap
// variants of strerror() to report errors.
#if LL_WINDOWS
// On Windows, use strerror_s().
std::string strerr(int errn)
{
char buffer[256];
strerror_s(buffer, errn); // infers sizeof(buffer) -- love it!
return buffer;
}
typedef std::basic_ios<char,std::char_traits < char > > _Myios;
#else
// On Posix we want to call strerror_r(), but alarmingly, there are two
// different variants. The one that returns int always populates the passed
// buffer (except in case of error), whereas the other one always returns a
// valid char* but might or might not populate the passed buffer. How do we
// know which one we're getting? Define adapters for each and let the compiler
// select the applicable adapter.
// strerror_r() returns char*
std::string message_from(int /*orig_errno*/, const char* /*buffer*/, size_t /*bufflen*/,
const char* strerror_ret)
{
return strerror_ret;
}
// strerror_r() returns int
std::string message_from(int orig_errno, const char* buffer, size_t bufflen,
int strerror_ret)
{
if (strerror_ret == 0)
{
return buffer;
}
// Here strerror_r() has set errno. Since strerror_r() has already failed,
// seems like a poor bet to call it again to diagnose its own error...
int stre_errno = errno;
if (stre_errno == ERANGE)
{
return STRINGIZE("strerror_r() can't explain errno " << orig_errno
<< " (" << bufflen << "-byte buffer too small)");
}
if (stre_errno == EINVAL)
{
return STRINGIZE("unknown errno " << orig_errno);
}
// Here we don't even understand the errno from strerror_r()!
return STRINGIZE("strerror_r() can't explain errno " << orig_errno
<< " (error " << stre_errno << ')');
}
std::string strerr(int errn)
{
char buffer[256];
// Select message_from() function matching the strerror_r() we have on hand.
return message_from(errn, buffer, sizeof(buffer),
strerror_r(errn, buffer, sizeof(buffer)));
}
#endif // ! LL_WINDOWS
// On either system, shorthand call just infers global 'errno'.
std::string strerr()
{
return strerr(errno);
}
int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
{
if (rc < 0)
{
// Capture errno before we start emitting output
int errn = errno;
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
// EEXIST. Don't warn if caller explicitly says this errno is okay.
if (errn != accept)
{
LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
<< "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
}
#if 0 && LL_WINDOWS // turn on to debug file-locking problems
// If the problem is "Permission denied," maybe it's because another
// process has the file open. Try to find out.
if (errn == EACCES) // *not* EPERM
{
// Only do any of this stuff (before LL_ENDL) if it will be logged.
LL_DEBUGS("LLFile") << empty;
const char* TEMP = getenv("TEMP");
if (! TEMP)
{
LL_CONT << "No $TEMP, not running 'handle'";
}
else
{
std::string tf(TEMP);
tf += "\\handle.tmp";
// http://technet.microsoft.com/en-us/sysinternals/bb896655
std::string cmd(STRINGIZE("handle \"" << filename
// "openfiles /query /v | fgrep -i \"" << filename
<< "\" > \"" << tf << '"'));
LL_CONT << cmd;
if (system(cmd.c_str()) != 0)
{
LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
}
else
{
std::ifstream inf(tf);
std::string line;
while (std::getline(inf, line))
{
LL_CONT << '\n' << line;
}
}
LLFile::remove(tf);
}
LL_CONT << LL_ENDL;
}
#endif // LL_WINDOWS hack to identify processes holding file open
}
return rc;
}
// static
int LLFile::mkdir(const std::string& dirname, int perms)
{
#if LL_WINDOWS
// permissions are ignored on Windows
std::string utf8dirname = dirname;
llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
int rc = _wmkdir(utf16dirname.c_str());
#else
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
#endif
// We often use mkdir() to ensure the existence of a directory that might
// already exist. Don't spam the log if it does.
return warnif("mkdir", dirname, rc, EEXIST);
}
// static
int LLFile::rmdir(const std::string& dirname)
{
#if LL_WINDOWS
// permissions are ignored on Windows
std::string utf8dirname = dirname;
llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
int rc = _wrmdir(utf16dirname.c_str());
#else
int rc = ::rmdir(dirname.c_str());
#endif
return warnif("rmdir", dirname, rc);
}
// static
LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */
{
#if LL_WINDOWS
std::string utf8filename = filename;
std::string utf8mode = std::string(mode);
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
return _wfopen(utf16filename.c_str(),utf16mode.c_str());
#else
return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */
#endif
}
LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag)
{
#if LL_WINDOWS
std::string utf8filename = filename;
std::string utf8mode = std::string(mode);
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
#else
llassert(0);//No corresponding function on non-windows
return NULL;
#endif
}
int LLFile::close(LLFILE * file)
{
int ret_value = 0;
if (file)
{
ret_value = fclose(file);
}
return ret_value;
}
int LLFile::remove(const std::string& filename)
{
#if LL_WINDOWS
std::string utf8filename = filename;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
int rc = _wremove(utf16filename.c_str());
#else
int rc = ::remove(filename.c_str());
#endif
return warnif("remove", filename, rc);
}
int LLFile::rename(const std::string& filename, const std::string& newname)
{
#if LL_WINDOWS
std::string utf8filename = filename;
std::string utf8newname = newname;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
#else
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc);
}
bool LLFile::copy(const std::string from, const std::string to)
{
bool copied = false;
LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
if (in)
{
LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
if (out)
{
char buf[16384]; /* Flawfinder: ignore */
size_t readbytes;
bool write_ok = true;
while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
LL_WARNS("LLFile") << "Short write" << LL_ENDL;
write_ok = false;
}
}
if ( write_ok )
{
copied = true;
}
fclose(out);
}
fclose(in);
}
return copied;
}
int LLFile::stat(const std::string& filename, llstat* filestatus)
{
#if LL_WINDOWS
std::string utf8filename = filename;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
int rc = _wstat(utf16filename.c_str(),filestatus);
#else
int rc = ::stat(filename.c_str(),filestatus);
#endif
// We use stat() to determine existence (see isfile(), isdir()).
// Don't spam the log if the subject pathname doesn't exist.
return warnif("stat", filename, rc, ENOENT);
}
bool LLFile::isdir(const std::string& filename)
{
llstat st;
return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
}
bool LLFile::isfile(const std::string& filename)
{
llstat st;
return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
}
const char *LLFile::tmpdir()
{
static std::string utf8path;
if (utf8path.empty())
{
char sep;
#if LL_WINDOWS
sep = '\\';
DWORD len = GetTempPathW(0, L"");
llutf16string utf16path;
utf16path.resize(len + 1);
len = GetTempPathW(static_cast<DWORD>(utf16path.size()), &utf16path[0]);
utf8path = utf16str_to_utf8str(utf16path);
#else
sep = '/';
char *env = getenv("TMPDIR");
utf8path = env ? env : "/tmp/";
#endif
if (utf8path[utf8path.size() - 1] != sep)
{
utf8path += sep;
}
}
return utf8path.c_str();
}
/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
#if LL_WINDOWS
LLFILE * LLFile::_Fiopen(const std::string& filename,
std::ios::openmode mode)
{ // open a file
static const char *mods[] =
{ // fopen mode strings corresponding to valid[i]
"r", "w", "w", "a", "rb", "wb", "wb", "ab",
"r+", "w+", "a+", "r+b", "w+b", "a+b",
0};
static const int valid[] =
{ // valid combinations of open flags
ios_base::in,
ios_base::out,
ios_base::out | ios_base::trunc,
ios_base::out | ios_base::app,
ios_base::in | ios_base::binary,
ios_base::out | ios_base::binary,
ios_base::out | ios_base::trunc | ios_base::binary,
ios_base::out | ios_base::app | ios_base::binary,
ios_base::in | ios_base::out,
ios_base::in | ios_base::out | ios_base::trunc,
ios_base::in | ios_base::out | ios_base::app,
ios_base::in | ios_base::out | ios_base::binary,
ios_base::in | ios_base::out | ios_base::trunc
| ios_base::binary,
ios_base::in | ios_base::out | ios_base::app
| ios_base::binary,
0};
LLFILE *fp = 0;
int n;
ios_base::openmode atendflag = mode & ios_base::ate;
ios_base::openmode norepflag = mode & ios_base::_Noreplace;
if (mode & ios_base::_Nocreate)
mode |= ios_base::in; // file must exist
mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
; // look for a valid mode
if (valid[n] == 0)
return (0); // no valid mode
else if (norepflag && mode & (ios_base::out || ios_base::app)
&& (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
{ // file must not exist, close and fail
fclose(fp);
return (0);
}
else if (fp != 0 && fclose(fp) != 0)
return (0); // can't close after test open
// should open with protection here, if other than default
else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
return (0); // open failed
if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
return (fp); // no need to seek to end, or seek succeeded
fclose(fp); // can't position at end
return (0);
}
#endif /* LL_WINDOWS */
/************** llstdio file buffer ********************************/
//llstdio_filebuf* llstdio_filebuf::open(const char *_Filename,
// ios_base::openmode _Mode)
//{
//#if LL_WINDOWS
// _Filet *_File;
// if (is_open() || (_File = LLFILE::_Fiopen(_Filename, _Mode)) == 0)
// return (0); // open failed
//
// _Init(_File, _Openfl);
// _Initcvt(&_USE(_Mysb::getloc(), _Cvt));
// return (this); // open succeeded
//#else
// std::filebuf* _file = std::filebuf::open(_Filename, _Mode);
// if (NULL == _file) return NULL;
// return this;
//#endif
//}
// *TODO: Seek the underlying c stream for better cross-platform compatibility?
#if !LL_WINDOWS
llstdio_filebuf::int_type llstdio_filebuf::overflow(llstdio_filebuf::int_type __c)
{
int_type __ret = traits_type::eof();
const bool __testeof = traits_type::eq_int_type(__c, __ret);
const bool __testout = _M_mode & ios_base::out;
if (__testout && !_M_reading)
{
if (this->pbase() < this->pptr())
{
// If appropriate, append the overflow char.
if (!__testeof)
{
*this->pptr() = traits_type::to_char_type(__c);
this->pbump(1);
}
// Convert pending sequence to external representation,
// and output.
if (_convert_to_external(this->pbase(),
this->pptr() - this->pbase()))
{
_M_set_buffer(0);
__ret = traits_type::not_eof(__c);
}
}
else if (_M_buf_size > 1)
{
// Overflow in 'uncommitted' mode: set _M_writing, set
// the buffer to the initial 'write' mode, and put __c
// into the buffer.
_M_set_buffer(0);
_M_writing = true;
if (!__testeof)
{
*this->pptr() = traits_type::to_char_type(__c);
this->pbump(1);
}
__ret = traits_type::not_eof(__c);
}
else
{
// Unbuffered.
char_type __conv = traits_type::to_char_type(__c);
if (__testeof || _convert_to_external(&__conv, 1))
{
_M_writing = true;
__ret = traits_type::not_eof(__c);
}
}
}
return __ret;
}
bool llstdio_filebuf::_convert_to_external(char_type* __ibuf,
std::streamsize __ilen)
{
// Sizes of external and pending output.
streamsize __elen;
streamsize __plen;
if (__check_facet(_M_codecvt).always_noconv())
{
//__elen = _M_file.xsputn(reinterpret_cast<char*>(__ibuf), __ilen);
__elen = fwrite(reinterpret_cast<void*>(__ibuf), 1,
__ilen, _M_file.file());
__plen = __ilen;
}
else
{
// Worst-case number of external bytes needed.
// XXX Not done encoding() == -1.
streamsize __blen = __ilen * _M_codecvt->max_length();
char* __buf = static_cast<char*>(__builtin_alloca(__blen));
char* __bend;
const char_type* __iend;
codecvt_base::result __r;
__r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen,
__iend, __buf, __buf + __blen, __bend);
if (__r == codecvt_base::ok || __r == codecvt_base::partial)
__blen = __bend - __buf;
else if (__r == codecvt_base::noconv)
{
// Same as the always_noconv case above.
__buf = reinterpret_cast<char*>(__ibuf);
__blen = __ilen;
}
else
__throw_ios_failure(__N("llstdio_filebuf::_convert_to_external "
"conversion error"));
//__elen = _M_file.xsputn(__buf, __blen);
__elen = fwrite(__buf, 1, __blen, _M_file.file());
__plen = __blen;
// Try once more for partial conversions.
if (__r == codecvt_base::partial && __elen == __plen)
{
const char_type* __iresume = __iend;
streamsize __rlen = this->pptr() - __iend;
__r = _M_codecvt->out(_M_state_cur, __iresume,
__iresume + __rlen, __iend, __buf,
__buf + __blen, __bend);
if (__r != codecvt_base::error)
{
__rlen = __bend - __buf;
//__elen = _M_file.xsputn(__buf, __rlen);
__elen = fwrite(__buf, 1, __rlen, _M_file.file());
__plen = __rlen;
}
else
{
__throw_ios_failure(__N("llstdio_filebuf::_convert_to_external "
"conversion error"));
}
}
}
return __elen == __plen;
}
llstdio_filebuf::int_type llstdio_filebuf::underflow()
{
int_type __ret = traits_type::eof();
const bool __testin = _M_mode & ios_base::in;
if (__testin)
{
if (_M_writing)
{
if (overflow() == traits_type::eof())
return __ret;
//_M_set_buffer(-1);
//_M_writing = false;
}
// Check for pback madness, and if so switch back to the
// normal buffers and jet outta here before expensive
// fileops happen...
_M_destroy_pback();
if (this->gptr() < this->egptr())
return traits_type::to_int_type(*this->gptr());
// Get and convert input sequence.
const size_t __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1;
// Will be set to true if ::fread() returns 0 indicating EOF.
bool __got_eof = false;
// Number of internal characters produced.
streamsize __ilen = 0;
codecvt_base::result __r = codecvt_base::ok;
if (__check_facet(_M_codecvt).always_noconv())
{
//__ilen = _M_file.xsgetn(reinterpret_cast<char*>(this->eback()),
// __buflen);
__ilen = fread(reinterpret_cast<void*>(this->eback()), 1,
__buflen, _M_file.file());
if (__ilen == 0)
__got_eof = true;
}
else
{
// Worst-case number of external bytes.
// XXX Not done encoding() == -1.
const int __enc = _M_codecvt->encoding();
streamsize __blen; // Minimum buffer size.
streamsize __rlen; // Number of chars to read.
if (__enc > 0)
__blen = __rlen = __buflen * __enc;
else
{
__blen = __buflen + _M_codecvt->max_length() - 1;
__rlen = __buflen;
}
const streamsize __remainder = _M_ext_end - _M_ext_next;
__rlen = __rlen > __remainder ? __rlen - __remainder : 0;
// An imbue in 'read' mode implies first converting the external
// chars already present.
if (_M_reading && this->egptr() == this->eback() && __remainder)
__rlen = 0;
// Allocate buffer if necessary and move unconverted
// bytes to front.
if (_M_ext_buf_size < __blen)
{
char* __buf = new char[__blen];
if (__remainder)
__builtin_memcpy(__buf, _M_ext_next, __remainder);
delete [] _M_ext_buf;
_M_ext_buf = __buf;
_M_ext_buf_size = __blen;
}
else if (__remainder)
__builtin_memmove(_M_ext_buf, _M_ext_next, __remainder);
_M_ext_next = _M_ext_buf;
_M_ext_end = _M_ext_buf + __remainder;
_M_state_last = _M_state_cur;
do
{
if (__rlen > 0)
{
// Sanity check!
// This may fail if the return value of
// codecvt::max_length() is bogus.
if (_M_ext_end - _M_ext_buf + __rlen > _M_ext_buf_size)
{
__throw_ios_failure(__N("llstdio_filebuf::underflow "
"codecvt::max_length() "
"is not valid"));
}
//streamsize __elen = _M_file.xsgetn(_M_ext_end, __rlen);
streamsize __elen = fread(_M_ext_end, 1,
__rlen, _M_file.file());
if (__elen == 0)
__got_eof = true;
else if (__elen == -1)
break;
//_M_ext_end += __elen;
}
char_type* __iend = this->eback();
if (_M_ext_next < _M_ext_end)
{
__r = _M_codecvt->in(_M_state_cur, _M_ext_next,
_M_ext_end, _M_ext_next,
this->eback(),
this->eback() + __buflen, __iend);
}
if (__r == codecvt_base::noconv)
{
size_t __avail = _M_ext_end - _M_ext_buf;
__ilen = std::min(__avail, __buflen);
traits_type::copy(this->eback(),
reinterpret_cast<char_type*>
(_M_ext_buf), __ilen);
_M_ext_next = _M_ext_buf + __ilen;
}
else
__ilen = __iend - this->eback();
// _M_codecvt->in may return error while __ilen > 0: this is
// ok, and actually occurs in case of mixed encodings (e.g.,
// XML files).
if (__r == codecvt_base::error)
break;
__rlen = 1;
} while (__ilen == 0 && !__got_eof);
}
if (__ilen > 0)
{
_M_set_buffer(__ilen);
_M_reading = true;
__ret = traits_type::to_int_type(*this->gptr());
}
else if (__got_eof)
{
// If the actual end of file is reached, set 'uncommitted'
// mode, thus allowing an immediate write without an
// intervening seek.
_M_set_buffer(-1);
_M_reading = false;
// However, reaching it while looping on partial means that
// the file has got an incomplete character.
if (__r == codecvt_base::partial)
__throw_ios_failure(__N("llstdio_filebuf::underflow "
"incomplete character in file"));
}
else if (__r == codecvt_base::error)
__throw_ios_failure(__N("llstdio_filebuf::underflow "
"invalid byte sequence in file"));
else
__throw_ios_failure(__N("llstdio_filebuf::underflow "
"error reading the file"));
}
return __ret;
}
std::streamsize llstdio_filebuf::xsgetn(char_type* __s, std::streamsize __n)
{
// Clear out pback buffer before going on to the real deal...
streamsize __ret = 0;
if (_M_pback_init)
{
if (__n > 0 && this->gptr() == this->eback())
{
*__s++ = *this->gptr();
this->gbump(1);
__ret = 1;
--__n;
}
_M_destroy_pback();
}
// Optimization in the always_noconv() case, to be generalized in the
// future: when __n > __buflen we read directly instead of using the
// buffer repeatedly.
const bool __testin = _M_mode & ios_base::in;
const streamsize __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1;
if (__n > __buflen && __check_facet(_M_codecvt).always_noconv()
&& __testin && !_M_writing)
{
// First, copy the chars already present in the buffer.
const streamsize __avail = this->egptr() - this->gptr();
if (__avail != 0)
{
if (__avail == 1)
*__s = *this->gptr();
else
traits_type::copy(__s, this->gptr(), __avail);
__s += __avail;
this->gbump(__avail);
__ret += __avail;
__n -= __avail;
}
// Need to loop in case of short reads (relatively common
// with pipes).
streamsize __len;
for (;;)
{
//__len = _M_file.xsgetn(reinterpret_cast<char*>(__s), __n);
__len = fread(reinterpret_cast<void*>(__s), 1,
__n, _M_file.file());
if (__len == -1)
__throw_ios_failure(__N("llstdio_filebuf::xsgetn "
"error reading the file"));
if (__len == 0)
break;
__n -= __len;
__ret += __len;
if (__n == 0)
break;
__s += __len;
}
if (__n == 0)
{
_M_set_buffer(0);
_M_reading = true;
}
else if (__len == 0)
{
// If end of file is reached, set 'uncommitted'
// mode, thus allowing an immediate write without
// an intervening seek.
_M_set_buffer(-1);
_M_reading = false;
}
}
else
__ret += __streambuf_type::xsgetn(__s, __n);
return __ret;
}
std::streamsize llstdio_filebuf::xsputn(const char_type* __s, std::streamsize __n)
{
// Optimization in the always_noconv() case, to be generalized in the
// future: when __n is sufficiently large we write directly instead of
// using the buffer.
streamsize __ret = 0;
const bool __testout = _M_mode & ios_base::out;
if (__check_facet(_M_codecvt).always_noconv()
&& __testout && !_M_reading)
{
// Measurement would reveal the best choice.
const streamsize __chunk = 1ul << 10;
streamsize __bufavail = this->epptr() - this->pptr();
// Don't mistake 'uncommitted' mode buffered with unbuffered.
if (!_M_writing && _M_buf_size > 1)
__bufavail = _M_buf_size - 1;
const streamsize __limit = std::min(__chunk, __bufavail);
if (__n >= __limit)
{
const streamsize __buffill = this->pptr() - this->pbase();
const char* __buf = reinterpret_cast<const char*>(this->pbase());
//__ret = _M_file.xsputn_2(__buf, __buffill,
// reinterpret_cast<const char*>(__s), __n);
if (__buffill)
{
__ret = fwrite(__buf, 1, __buffill, _M_file.file());
}
if (__ret == __buffill)
{
__ret += fwrite(reinterpret_cast<const char*>(__s), 1,
__n, _M_file.file());
}
if (__ret == __buffill + __n)
{
_M_set_buffer(0);
_M_writing = true;
}
if (__ret > __buffill)
__ret -= __buffill;
else
__ret = 0;
}
else
__ret = __streambuf_type::xsputn(__s, __n);
}
else
__ret = __streambuf_type::xsputn(__s, __n);
return __ret;
}
int llstdio_filebuf::sync()
{
return (_M_file.sync() == 0 ? 0 : -1);
}
#endif
/************** input file stream ********************************/
llifstream::llifstream() : _M_filebuf(),
#if LL_WINDOWS
std::istream(&_M_filebuf) {}
#else
std::istream()
{
this->init(&_M_filebuf);
}
#endif
// explicit
llifstream::llifstream(const std::string& _Filename,
ios_base::openmode _Mode) : _M_filebuf(),
#if LL_WINDOWS
std::istream(&_M_filebuf)
{
llutf16string wideName = utf8str_to_utf16str( _Filename );
if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0)
{
_Myios::setstate(ios_base::failbit);
}
}
#else
std::istream()
{
this->init(&_M_filebuf);
this->open(_Filename.c_str(), _Mode | ios_base::in);
}
#endif
// explicit
llifstream::llifstream(const char* _Filename,
ios_base::openmode _Mode) : _M_filebuf(),
#if LL_WINDOWS
std::istream(&_M_filebuf)
{
llutf16string wideName = utf8str_to_utf16str( _Filename );
if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0)
{
_Myios::setstate(ios_base::failbit);
}
}
#else
std::istream()
{
this->init(&_M_filebuf);
this->open(_Filename, _Mode | ios_base::in);
}
#endif
#if llstream_LLFILE
// explicit
llifstream::llifstream(_Filet *_File,
ios_base::openmode _Mode, size_t _Size) :
_M_filebuf(_File, _Mode, _Size),
#if LL_WINDOWS
std::istream(&_M_filebuf) {}
#else
std::istream()
{
this->init(&_M_filebuf);
}
#endif
#if !LL_WINDOWS
// explicit
llifstream::llifstream(int __fd,
ios_base::openmode _Mode, size_t _Size) :
_M_filebuf(__fd, _Mode, _Size),
std::istream()
{
this->init(&_M_filebuf);
}
#endif
#endif // llstream_LLFILE
bool llifstream::is_open() const
{ // test if C stream has been opened
return _M_filebuf.is_open();
}
void llifstream::open(const char* _Filename, ios_base::openmode _Mode)
{ // open a C stream with specified mode
#if LL_WINDOWS
llutf16string wideName = utf8str_to_utf16str( _Filename );
if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::in) == 0)
{
_Myios::setstate(ios_base::failbit);
}
else
{
_Myios::clear();
}
#else
if (_M_filebuf.open(_Filename, _Mode | ios_base::in) == 0)
{
this->setstate(ios_base::failbit);
}
else
{
this->clear();
}
#endif
}
void llifstream::close()
{ // close the C stream
if (_M_filebuf.close() == 0)
{
#if LL_WINDOWS
_Myios::setstate(ios_base::failbit);
#else
this->setstate(ios_base::failbit);
#endif
}
}
/************** output file stream ********************************/
llofstream::llofstream() : _M_filebuf(),
#if LL_WINDOWS
std::ostream(&_M_filebuf) {}
#else
std::ostream()
{
this->init(&_M_filebuf);
}
#endif
// explicit
llofstream::llofstream(const std::string& _Filename,
ios_base::openmode _Mode) : _M_filebuf(),
#if LL_WINDOWS
std::ostream(&_M_filebuf)
{
llutf16string wideName = utf8str_to_utf16str( _Filename );
if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
{
_Myios::setstate(ios_base::failbit);
}
}
#else
std::ostream()
{
this->init(&_M_filebuf);
this->open(_Filename.c_str(), _Mode | ios_base::out);
}
#endif
// explicit
llofstream::llofstream(const char* _Filename,
ios_base::openmode _Mode) : _M_filebuf(),
#if LL_WINDOWS
std::ostream(&_M_filebuf)
{
llutf16string wideName = utf8str_to_utf16str( _Filename );
if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
{
_Myios::setstate(ios_base::failbit);
}
}
#else
std::ostream()
{
this->init(&_M_filebuf);
this->open(_Filename, _Mode | ios_base::out);
}
#endif
#if llstream_LLFILE
// explicit
llofstream::llofstream(_Filet *_File,
ios_base::openmode _Mode, size_t _Size) :
_M_filebuf(_File, _Mode, _Size),
#if LL_WINDOWS
std::ostream(&_M_filebuf) {}
#else
std::ostream()
{
this->init(&_M_filebuf);
}
#endif
#if !LL_WINDOWS
// explicit
llofstream::llofstream(int __fd,
ios_base::openmode _Mode, size_t _Size) :
_M_filebuf(__fd, _Mode, _Size),
std::ostream()
{
this->init(&_M_filebuf);
}
#endif
#endif // llstream_LLFILE
bool llofstream::is_open() const
{ // test if C stream has been opened
return _M_filebuf.is_open();
}
void llofstream::open(const char* _Filename, ios_base::openmode _Mode)
{ // open a C stream with specified mode
#if LL_WINDOWS
llutf16string wideName = utf8str_to_utf16str( _Filename );
if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
{
_Myios::setstate(ios_base::failbit);
}
else
{
_Myios::clear();
}
#else
if (_M_filebuf.open(_Filename, _Mode | ios_base::out) == 0)
{
this->setstate(ios_base::failbit);
}
else
{
this->clear();
}
#endif
}
void llofstream::close()
{ // close the C stream
if (_M_filebuf.close() == 0)
{
#if LL_WINDOWS
_Myios::setstate(ios_base::failbit);
#else
this->setstate(ios_base::failbit);
#endif
}
}
/************** helper functions ********************************/
std::streamsize llifstream_size(llifstream& ifstr)
{
if(!ifstr.is_open()) return 0;
std::streampos pos_old = ifstr.tellg();
ifstr.seekg(0, ios_base::beg);
std::streampos pos_beg = ifstr.tellg();
ifstr.seekg(0, ios_base::end);
std::streampos pos_end = ifstr.tellg();
ifstr.seekg(pos_old, ios_base::beg);
return pos_end - pos_beg;
}
std::streamsize llofstream_size(llofstream& ofstr)
{
if(!ofstr.is_open()) return 0;
std::streampos pos_old = ofstr.tellp();
ofstr.seekp(0, ios_base::beg);
std::streampos pos_beg = ofstr.tellp();
ofstr.seekp(0, ios_base::end);
std::streampos pos_end = ofstr.tellp();
ofstr.seekp(pos_old, ios_base::beg);
return pos_end - pos_beg;
}