Skip to content
Snippets Groups Projects
  • Nat Goodspeed's avatar
    ae6440ee
    MAINT-4744: Eliminate viewer dependency on (old) GNU libstdc++. · ae6440ee
    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.
    ae6440ee
    History
    MAINT-4744: Eliminate viewer dependency on (old) GNU libstdc++.
    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.
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;
}