#include "fileno.hpp" #include // declaration of ::fileno #include // for basic_filebuf template #include #if defined(__GLIBCXX__) || (defined(__GLIBCPP__) && __GLIBCPP__>=20020514) // GCC >= 3.1.0 # include #endif #if defined(__GLIBCXX__) // GCC >= 3.4.0 # include #endif //! Similar to fileno(3), but taking a C++ stream as argument instead of a //! FILE*. Note that there is no way for the library to track what you do with //! the descriptor, so be careful. //! \return The integer file descriptor associated with the stream, or -1 if //! that stream is invalid. In the latter case, for the sake of keeping the //! code as similar to fileno(3), errno is set to EBADF. //! \see The upstream page at //! http://www.ginac.de/~kreckel/fileno/ of this code provides more //! detailed information. template inline int fileno_hack(const std::basic_ios& stream) { // Some C++ runtime libraries shipped with ancient GCC, Sun Pro, // Sun WS/Forte 5/6, Compaq C++ supported non-standard file descriptor // access basic_filebuf<>::fd(). Alas, starting from GCC 3.1, the GNU C++ // runtime removes all non-standard std::filebuf methods and provides an // extension template class __gnu_cxx::stdio_filebuf on all systems where // that appears to make sense (i.e. at least all Unix systems). Starting // from GCC 3.4, there is an __gnu_cxx::stdio_sync_filebuf, in addition. // Sorry, darling, I must get brutal to fetch the darn file descriptor! // Please complain to your compiler/libstdc++ vendor... #if defined(__GLIBCXX__) || defined(__GLIBCPP__) // OK, stop reading here, because it's getting obscene. Cross fingers! # if defined(__GLIBCXX__) // >= GCC 3.4.0 // This applies to cin, cout and cerr when not synced with stdio: typedef __gnu_cxx::stdio_filebuf unix_filebuf_t; unix_filebuf_t* fbuf = dynamic_cast(stream.rdbuf()); if (fbuf != NULL) { return fbuf->fd(); } // This applies to filestreams: typedef std::basic_filebuf filebuf_t; filebuf_t* bbuf = dynamic_cast(stream.rdbuf()); if (bbuf != NULL) { // This subclass is only there for accessing the FILE*. Ouuwww, sucks! struct my_filebuf : public std::basic_filebuf { // Note: _M_file is of type __basic_file which has a // FILE* as its first (but private) member variable. FILE* c_file() { return *(FILE**)(&this->_M_file); } }; FILE* c_file = static_cast(bbuf)->c_file(); if (c_file != NULL) { // Could be NULL for failed ifstreams. return ::fileno(c_file); } } // This applies to cin, cout and cerr when synced with stdio: typedef __gnu_cxx::stdio_sync_filebuf sync_filebuf_t; sync_filebuf_t* sbuf = dynamic_cast(stream.rdbuf()); if (sbuf != NULL) { # if (__GLIBCXX__<20040906) // GCC < 3.4.2 // This subclass is only there for accessing the FILE*. // See GCC PR#14600 and PR#16411. struct my_filebuf : public sync_filebuf_t { my_filebuf(); // Dummy ctor keeps the compiler happy. // Note: stdio_sync_filebuf has a FILE* as its first (but private) // member variable. However, it is derived from basic_streambuf<> // and the FILE* is the first non-inherited member variable. FILE* c_file() { return *(FILE**)((char*)this + sizeof(std::basic_streambuf)); } }; return ::fileno(static_cast(sbuf)->c_file()); # else return ::fileno(sbuf->file()); # endif } # else // GCC < 3.4.0 used __GLIBCPP__ # if (__GLIBCPP__>=20020514) // GCC >= 3.1.0 // This applies to cin, cout and cerr: typedef __gnu_cxx::stdio_filebuf unix_filebuf_t; unix_filebuf_t* buf = dynamic_cast(stream.rdbuf()); if (buf != NULL) { return buf->fd(); } // This applies to filestreams: typedef std::basic_filebuf filebuf_t; filebuf_t* bbuf = dynamic_cast(stream.rdbuf()); if (bbuf != NULL) { // This subclass is only there for accessing the FILE*. Ouuwww, sucks! struct my_filebuf : public std::basic_filebuf { // Note: _M_file is of type __basic_file which has a // FILE* as its first (but private) member variable. FILE* c_file() { return *(FILE**)(&this->_M_file); } }; FILE* c_file = static_cast(bbuf)->c_file(); if (c_file != NULL) { // Could be NULL for failed ifstreams. return ::fileno(c_file); } } # else // GCC 3.0.x typedef std::basic_filebuf filebuf_t; filebuf_t* fbuf = dynamic_cast(stream.rdbuf()); if (fbuf != NULL) { struct my_filebuf : public filebuf_t { // Note: basic_filebuf has a __basic_file* as // its first (but private) member variable. Since it is derived // from basic_streambuf we can guess its offset. // __basic_file in turn has a FILE* as its first (but // private) member variable. Get it by brute force. Oh, geez! FILE* c_file() { std::__basic_file* ptr_M_file = *(std::__basic_file**)((char*)this + sizeof(std::basic_streambuf)); # if _GLIBCPP_BASIC_FILE_INHERITANCE // __basic_file inherits from __basic_file_base return *(FILE**)((char*)ptr_M_file + sizeof(std::__basic_file_base)); # else // __basic_file is base class, but with vptr. return *(FILE**)((char*)ptr_M_file + sizeof(void*)); # endif } }; return ::fileno(static_cast(fbuf)->c_file()); } # endif # endif #else # error "Does anybody know how to fetch the bloody file descriptor?" return stream.rdbuf()->fd(); // Maybe a good start? #endif errno = EBADF; return -1; } //! 8-Bit character instantiation: fileno(ios). template <> int fileno(const std::ios& stream) { return fileno_hack(stream); } //! Wide character instantiation: fileno(wios). template <> int fileno(const std::wios& stream) { return fileno_hack(stream); }