/** * @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 #include // Windows errno #else #include #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 > _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(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(__ibuf), __ilen); __elen = fwrite(reinterpret_cast(__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(__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(__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(this->eback()), // __buflen); __ilen = fread(reinterpret_cast(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 (_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(__s), __n); __len = fread(reinterpret_cast(__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(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(this->pbase()); //__ret = _M_file.xsputn_2(__buf, __buffill, // reinterpret_cast(__s), __n); if (__buffill) { __ret = fwrite(__buf, 1, __buffill, _M_file.file()); } if (__ret == __buffill) { __ret += fwrite(reinterpret_cast(__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 // 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 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 // 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 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; }