stdcxx-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From elemi...@apache.org
Subject svn commit: r652646 [2/2] - /stdcxx/branches/4.2.x/tests/iostream/27.filebuf.cpp
Date Thu, 01 May 2008 20:15:49 GMT

Added: stdcxx/branches/4.2.x/tests/iostream/27.filebuf.cpp
URL: http://svn.apache.org/viewvc/stdcxx/branches/4.2.x/tests/iostream/27.filebuf.cpp?rev=652646&view=auto
==============================================================================
--- stdcxx/branches/4.2.x/tests/iostream/27.filebuf.cpp (added)
+++ stdcxx/branches/4.2.x/tests/iostream/27.filebuf.cpp Thu May  1 13:15:48 2008
@@ -0,0 +1,2804 @@
+/***************************************************************************
+ *
+ * 27.filebuf.cpp - test exercising class template basic_filebuf
+ *
+ * $Id$
+ *
+ ***************************************************************************
+ *
+ * Licensed to the Apache Software  Foundation (ASF) under one or more
+ * contributor  license agreements.  See  the NOTICE  file distributed
+ * with  this  work  for  additional information  regarding  copyright
+ * ownership.   The ASF  licenses this  file to  you under  the Apache
+ * License, Version  2.0 (the  "License"); you may  not use  this file
+ * except in  compliance with the License.   You may obtain  a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the  License is distributed on an  "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
+ * implied.   See  the License  for  the  specific language  governing
+ * permissions and limitations under the License.
+ *
+ * Copyright 1994-2008 Rogue Wave Software.
+ *
+ **************************************************************************/
+
+#include <cerrno>    // for errno
+#include <csignal>   // for signal()
+#include <cstdio>    // for FILE, fopen(), remove()
+#include <cstring>   // for memcmp()
+#include <cwchar>    // for mbstate_t
+
+#include <fstream>
+
+#if !defined (_WIN32) && !defined (_WIN64)
+
+#  ifdef __SUNPRO_CC
+     // working around a SunOS/SunPro bug (PR #26255)
+#    undef _TIME_T
+#  endif
+
+#  include <cstdlib>      // for exit()
+#  include <fcntl.h>      // for open(), O_XXX constants
+#  include <unistd.h>     // for fork()
+#  include <sys/stat.h>   // for mkfifo()
+#  include <sys/types.h>  // for pid_t
+#  include <sys/wait.h>   // for wait()
+#else
+#  include <fcntl.h>      // for O_XXX constants
+#  include <io.h>         // for open()
+#endif   // _WIN{32,64}
+
+#ifndef SIGPIPE
+#  define SIGPIPE   13   /* HP-UX, Linux, and SunOS value */
+#endif   // SIGPIPE
+
+#include <driver.h>
+#include <file.h>
+#include <valcmp.h>
+
+
+#define REMOVE_FILE(tmpfname) \
+    rw_warn (0 == std::remove (tmpfname), __FILE__, __LINE__, \
+             "std::remove(\"%s\") failed: %m", tmpfname)
+
+/**************************************************************************/
+
+
+template <class charT>
+static void
+test_ctors (const char* tname)
+{
+    typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
+
+    int fdcount [2];
+    int next_fd [2];
+
+    //////////////////////////////////////////////////////////////////
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::basic_filebuf()", tname);
+
+    next_fd [0] = rw_nextfd (fdcount + 0);
+
+    {
+        Filebuf fb;
+
+        // verify the postcondition in 27.8.1.2, p2
+        rw_assert (!fb.is_open (), __FILE__, __LINE__,
+                   "basic_filebuf<%s>::basic_filebuf().is_open() == "
+                   "false, got true", tname);
+
+        // verify that no file descriptor has been allocated
+        next_fd [1] = rw_nextfd (fdcount + 1);
+
+        rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
+                   __FILE__, __LINE__,
+                   "%d file descriptor leak(s) detected after construction",
+                   fdcount [1] - fdcount [0]);
+    }
+
+    // verify that no file descriptor has been closed
+    next_fd [1] = rw_nextfd (fdcount + 1);
+
+    //////////////////////////////////////////////////////////////////
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::~basic_filebuf()", tname);
+
+    rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
+               __FILE__, __LINE__,
+               "%d file descriptor leak(s) detected after destruction",
+               fdcount [1] - fdcount [0]);
+
+
+    const char* const tmpfname = rw_tmpnam (0);
+    if (!tmpfname) {
+        return;
+    }
+
+    {
+        Filebuf fb;
+
+        next_fd [0] = rw_nextfd (fdcount + 0);
+
+        Filebuf *tmp = fb.open (tmpfname, std::ios::out);
+
+        // verify that open() succeeds and allocates a file descriptor
+        rw_assert (0 != tmp, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::open(\"%s\", std::ios_base::out) "
+                   "failed", tname, tmpfname);
+
+        next_fd [1] = rw_nextfd (fdcount + 1);
+
+        rw_assert (next_fd [0] != next_fd [1]
+                   && fdcount [0] + 1 == fdcount [1], __FILE__, __LINE__,
+                   "%d file descriptor mismatch detected after open()",
+                   fdcount [1] - fdcount [0]);
+    }
+
+    // verify that dtor closes the file descriptor
+    next_fd [1] = rw_nextfd (fdcount + 1);
+
+    rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
+               __FILE__, __LINE__,
+               "%d file descriptor leak(s) detected after destruction",
+               fdcount [1] - fdcount [0]);
+
+
+#ifndef _RWSTD_NO_EXT_FILEBUF
+
+    //////////////////////////////////////////////////////////////////
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::basic_filebuf(FILE*) "
+             "[extension]", tname);
+
+#  ifdef stdin
+
+    {
+        std::FILE* const fp = std::fopen (tmpfname, "w");
+
+        next_fd [0] = rw_nextfd (fdcount + 0);
+
+        // object takes over the ownership of the file pointer
+        Filebuf fb (fp);
+
+        rw_assert (fb.is_open (), __FILE__, __LINE__,
+                   "basic_filebuf<%s>::basic_filebuf (FILE*).is_open() "
+                   "== true, got false", tname);
+
+        next_fd [1] = rw_nextfd (fdcount + 1);
+
+        rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
+                   __FILE__, __LINE__,
+                   "%d file descriptor mismatch detected after open()",
+                   fdcount [1] - fdcount [0]);
+    }
+
+    // verify that dtor closes the file descriptor
+    next_fd [1] = rw_nextfd (fdcount + 1);
+
+    rw_assert (fdcount [0] == fdcount [1] + 1, __FILE__, __LINE__,
+               "%d file descriptor leak(s) detected after destruction",
+               fdcount [1] - fdcount [0]);
+
+#  else   // if !defined (stdin)
+
+    rw_assert (false, __FILE__, __LINE__,
+               "macro stdin unexpectedly not #defined, "
+               "basic_filebuf<%s>::basic_filebuf (FILE*) "
+               "extension not tested", tname);
+
+#  endif   // stdin
+
+    //////////////////////////////////////////////////////////////////
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::basic_filebuf(int) "
+             "[extension]", tname);
+
+    Filebuf *pfb = 0;
+
+    {
+        std::FILE* const fp = std::fopen (tmpfname, "w");
+
+        // object takes over the ownership of the file pointer
+        pfb = new Filebuf (fp);
+
+        next_fd [0] = rw_nextfd (fdcount + 0);
+
+        // object takes over the ownership of the file descriptor
+        Filebuf fb (pfb->fd ());
+
+        next_fd [1] = rw_nextfd (fdcount + 1);
+
+        rw_assert (fb.is_open (), __FILE__, __LINE__,
+                   "basic_filebuf<%s>::basic_filebuf (int).is_open() "
+                   "== true, got false", tname);
+
+        rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
+                   __FILE__, __LINE__,
+                   "%d file descriptor mismatch detected after open()",
+                   fdcount [1] - fdcount [0]);
+    }
+
+    // verify that dtor closes the file descriptor
+    next_fd [1] = rw_nextfd (fdcount + 1);
+
+    rw_assert (fdcount [0] == fdcount [1] + 1, __FILE__, __LINE__,
+               "%d file descriptor leak(s) detected after destruction",
+               fdcount [1] - fdcount [0]);
+
+    delete pfb;
+
+#endif   // _RWSTD_NO_EXT_FILEBUF
+
+    REMOVE_FILE (tmpfname);
+}
+
+/***************************************************************************/
+
+extern "C" {
+
+// instead of calling signal(SIGPIPE, SIG_IGN)
+void ignore_signal (int)
+{
+    std::signal (SIGPIPE, ignore_signal);
+}
+
+}   // extern "C"
+
+
+template <class charT>
+static void
+test_open (const char* tname)
+{
+    typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
+
+    const char* const tmpfname = rw_tmpnam (0);
+
+    if (!tmpfname) {
+        return;
+    }
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::is_open ()", tname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::open (const char*, "
+             "ios_base::openmode)", tname);
+
+    /*****************************************************************
+
+    Table 92
+
+    +-----+-----+-----+-----+-----+-----+
+    | bin | in  | out |trunc| app |stdio|
+    +=====+=====+=====+=====+=====+=====+
+    |     |     |  +  |     |     | "w" |
+    +-----+-----+-----+-----+-----+-----+
+    |     |     |  +  |     |  +  | "a" |
+    +-----+-----+-----+-----+-----+-----+
+    |     |     |  +  |  +  |     | "w" |
+    +-----+-----+-----+-----+-----+-----+
+    |     |  +  |     |     |     | "r" |
+    +-----+-----+-----+-----+-----+-----+
+    |     |  +  |  +  |     |     |"r+" |
+    +-----+-----+-----+-----+-----+-----+
+    |     |  +  |  +  |  +  |     |"w+" |
+    +=====+=====+=====+=====+=====+=====+
+    |  +  |     |  +  |     |     |"wb" |
+    +-----+-----+-----+-----+-----+-----+
+    |  +  |     |  +  |     |  +  |"ab" |
+    +-----+-----+-----+-----+-----+-----+
+    |  +  |     |  +  |  +  |     |"wb" |
+    +-----+-----+-----+-----+-----+-----+
+    |  +  |  +  |     |     |     |"rb" |
+    +-----+-----+-----+-----+-----+-----+
+    |  +  |  +  |  +  |     |     |"r+b"|
+    +-----+-----+-----+-----+-----+-----+
+    |  +  |  +  |  +  |  +  |     |"w+b"|
+    +-----+-----+-----+-----+-----+-----+
+
+    r          open text file for reading
+    w          truncate to zero length or create text file for writing
+    a          append; open or create text file for writing at EOF
+    rb         open binary file for reading
+    wb         truncate to zero length or create binary file for writing
+    ab         append; open or create binary file for writing at EOF
+    r+         open text file for update (reading and writing)
+    w+         truncate to zero length or create text file for update
+    a+         append; open or create text file for update at EOF
+    r+b or rb+ open binary file for update (reading and writing)
+    w+b or wb+ truncate to zero length or create binary file for update
+    a+b or ab+ append; open or create binary file for update at EOF
+
+    ******************************************************************/
+
+#define BEGIN_MODE(openmode, ext)                                \
+    mode = (openmode),                                           \
+    rw_info (0, __FILE__, __LINE__, "%{Io} %s", mode, ext)
+
+#define ASSERT_OPEN(expr, mode, txt)                             \
+    rw_assert (expr, __FILE__, __LINE__,                         \
+               "basic_filebuf<%s>::open (\"%s\", %{Io}) %s ",    \
+               tname, tmpfname, mode, txt)                       \
+
+    std::ios_base::openmode mode;
+
+    const std::ios::openmode iomodes[] = {
+        std::ios::openmode ()
+
+#ifndef _RWSTD_NO_EXT_STDIO
+
+        , std::ios::stdio
+
+#endif   // _RWSTD_NO_EXT_STDIO
+
+    };
+
+    const std::size_t niomodes = sizeof iomodes / sizeof *iomodes;
+
+    const char* buf  = 0;
+    std::size_t size = 0;
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::out | iomodes [minx], "");
+
+        {
+            Filebuf ().open (tmpfname, mode);
+        }
+
+        buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+        ASSERT_OPEN (0 != buf, mode, "failed to create a new file");
+
+        if (buf)
+            ASSERT_OPEN (!size, mode,
+                         "unexpectedly created a non-empty file");
+
+        rw_fwrite (tmpfname, "foobar");
+
+        {
+            Filebuf ().open (tmpfname, mode);
+        }
+
+        buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+        ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
+
+        if (buf)
+            ASSERT_OPEN (!size, mode,
+                         "unexpectedly created a non-empty file");
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out | ios::app)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::out | std::ios::app | iomodes [minx], "");
+
+        REMOVE_FILE (tmpfname);
+
+        {
+            Filebuf ().open (tmpfname, mode);
+        }
+
+        buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+        ASSERT_OPEN (0 != buf, mode, "failed to create a new file");
+
+        if (buf)
+            ASSERT_OPEN (!size, mode, "unexpectedly created a non-empty file");
+
+        rw_fwrite (tmpfname, "foobar");
+
+        {
+            Filebuf ().open (tmpfname, mode);
+        }
+
+        buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+        ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
+
+        if (buf)
+            ASSERT_OPEN (6 == size, mode,
+                         "unexpectedly truncated a non-empty file");
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out | ios::trunc)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::out | std::ios::trunc | iomodes [minx], "");
+
+        rw_fwrite (tmpfname, "foobar");
+
+        {
+            // open a file for writing and truncate it to 0 size
+            Filebuf ().open (tmpfname, mode);
+        }
+
+        // read the contents of the file and verify that they are empty
+        buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+        // file must exist
+        ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
+
+        // the size of the file must be 0
+        if (buf)
+            ASSERT_OPEN (!size, mode, "failed to truncate a non-empty file");
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::nocreate)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        // ios::nocreate is meaningless by itself
+        BEGIN_MODE (std::ios::nocreate | iomodes [minx], "[extension]");
+
+        Filebuf fb;
+
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "unexpectedly succeeded to open a non-existent file");
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out | ios::nocreate)
+
+    BEGIN_MODE (std::ios::out | std::ios::nocreate, "[extension]");
+
+    // ios::nocreate will prevent the call to open from creating
+    // the file if it doesn't exist
+
+    REMOVE_FILE (tmpfname);
+
+    {
+        Filebuf fb;
+
+        // try to open a file that doesn't exist for output but prevent
+        // the call from creating it by setting the nocreate bit
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "unexpectedly succeeded to open a non-existent file");
+    }
+
+    buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+    ASSERT_OPEN (0 == buf, mode, "unexpectedly created a file");
+
+    // the same as above but with an existing file
+    rw_fwrite (tmpfname, "foobar");
+
+    {
+        Filebuf fb;
+
+        // try to open an existing file
+        fb.open (tmpfname, mode);
+
+        // verify that the call succeeded
+        ASSERT_OPEN (fb.is_open (), mode,
+                     "unexpectedly succeeded to open an existing file");
+    }
+
+    buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+    ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
+
+#ifndef _RWSTD_NO_EXT_STDIO
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::stdio| ios::out | ios::nocreate)
+
+    BEGIN_MODE (std::ios::stdio | std::ios::out | std::ios::nocreate,
+                "[extension]");
+
+    REMOVE_FILE (tmpfname);
+
+    // ios::nocreate can't be implemented with ios::stdio
+    // and the call must fail
+    {
+        Filebuf fb;
+
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "unexpectedly succeeded to open a non-existent file");
+    }
+
+    // verify that the file wasn't created
+    buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+    ASSERT_OPEN (0 == buf, mode, "unexpectedly created a file");
+
+#endif   // _RWSTD_NO_EXT_STDIO
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out | ios::nocreate | ios::trunc)
+
+    // ios::trunc has no effect on ios::nocreate
+    BEGIN_MODE (std::ios::out | std::ios::nocreate | std::ios::trunc,
+                "[extension]");
+
+    // remove file in case it was unexpectedly created above
+    std::remove (tmpfname);
+
+    {
+        Filebuf fb;
+
+        // try to open a file that doesn't exist for output but prevent
+        // the call from creating it by setting the nocreate bit
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "unexpectedly succeeded to open a non-existent file");
+    }
+
+    buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+    ASSERT_OPEN (0 == buf, mode, "unexpectedly created a file");
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::noreplace)
+
+    // remove file in case it was unexpectedly created above
+    std::remove (tmpfname);
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        // ios::noreplace is meaningless by itself
+        BEGIN_MODE (std::ios::noreplace | iomodes [minx], "[extension]");
+
+        Filebuf fb;
+
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "unexpectedly succeeded to open a non-existent file");
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out | ios::noreplace)
+
+    // ios::noreplace will cause the call to open to succeed only
+    // if the file doesn't exist, otherwise the call will fail
+    BEGIN_MODE (std::ios::out | std::ios::noreplace, "[extension]");
+
+    rw_fwrite (tmpfname, "foobar");
+
+    {
+        Filebuf fb;
+
+        // try to open an existing file for output but prevent
+        // the call from replacing it by setting the noreplace bit
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "unexpectedly succeeded to open an existing file");
+    }
+
+    buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+    ASSERT_OPEN (0 != buf, mode, "unexpectedly removed an existing file");
+
+    if (buf)
+        ASSERT_OPEN (0 != size, mode, "unexpectdly truncated a non-empty file");
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::out | ios::trunc | ios::noreplace)
+
+    BEGIN_MODE (std::ios::out | std::ios::trunc | std::ios::noreplace,
+                "[extension]");
+
+    rw_fwrite (tmpfname, "foobar");
+
+    {
+        Filebuf fb;
+
+        // try to open an existing file for output
+        fb.open (tmpfname, mode);
+
+        // verify that the call failed
+        ASSERT_OPEN (!fb.is_open (), mode,
+                     "failed to open an existing file");
+    }
+
+    buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
+
+    ASSERT_OPEN (0 != buf, mode, "unexpectedly removed an existing file");
+
+    if (buf)
+        ASSERT_OPEN (0 != size, mode, "failed to truncate a non-empty file");
+
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::in)
+
+    charT readbuf [256];
+    const std::size_t readbuf_size = sizeof readbuf / sizeof *readbuf;
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::in | iomodes [minx], "");
+
+        rw_fwrite (tmpfname, "foobar");
+
+        Filebuf fb;
+
+        const charT foobar[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
+
+        if (fb.open (tmpfname, mode)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            std::memset (readbuf, 0, sizeof readbuf);
+            fb.sgetn (readbuf, readbuf_size);
+
+            ASSERT_OPEN (!std::memcmp (readbuf, foobar, sizeof foobar),
+                         mode, "failed to open an existing file for reading");
+        }
+        else {
+            ASSERT_OPEN (0, mode, "failed to open an existing file");
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::in | ios::out)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::in | std::ios::out | iomodes [minx], "");
+
+        rw_fwrite (tmpfname, "foobar");
+
+        Filebuf fb;
+
+        if (fb.open (tmpfname, mode)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            const charT FOO[] = { 'F', 'O', 'O' };
+            const charT bar[] = { 'b', 'a', 'r' };
+
+            if (3 != fb.sputn (FOO, sizeof FOO / sizeof *FOO))
+                ASSERT_OPEN (false, mode,
+                             "failed to open an existing file for writing "
+                             "(sputn() failed)");
+
+            // an input operation must be separated from a
+            // preceding output operation by a seek() or flush()
+            fb.pubsync ();
+
+            std::memset (readbuf, 0, sizeof readbuf);
+            const std::streamsize got = fb.sgetn (readbuf, readbuf_size);
+
+            if (3 == got) {
+                rw_assert (!std::memcmp (readbuf, bar, sizeof bar),
+                           __FILE__, __LINE__,
+                           "sgetn() retrieved the wrong data)");
+            }
+            else
+                rw_assert (false, __FILE__, __LINE__,
+                           "sgetn() == 3, got %td: %{*.*Ac}, "
+                           "expected %{*.*Ac}",
+                           got, int (sizeof (charT)), got, readbuf,
+                           int (sizeof (charT)),
+                           int (sizeof bar / sizeof *bar), bar);
+        }
+        else {
+            ASSERT_OPEN (0, mode, "failed to open an existing file");
+        }
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::in | ios::out | ios::trunc)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (  std::ios::in | std::ios::out | std::ios::trunc
+                    | iomodes [minx], "");
+
+        rw_fwrite (tmpfname, "foobar");
+
+        Filebuf fb;
+
+        if (fb.open (tmpfname, mode)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            const charT FOO[] = { 'F', 'O', 'O' };
+
+            std::memset (readbuf, 0, sizeof readbuf);
+            if (0 != fb.sgetn (readbuf, readbuf_size))
+                ASSERT_OPEN (false, mode,
+                             "failed to truncate a non-empty file");
+
+            if (3 != fb.sputn (FOO, sizeof FOO / sizeof *FOO))
+                ASSERT_OPEN (false, mode,
+                             "failed to open an existing file for writing "
+                             "(sputn() failed)");
+
+            // an input operation must be separated from a
+            // preceding output operation by a seek() or flush()
+            fb.pubseekoff (0, std::ios::beg);
+
+            std::memset (readbuf, 0, sizeof readbuf);
+            if (3 != fb.sgetn (readbuf, readbuf_size))
+                ASSERT_OPEN (false, mode,
+                             "failed to open an existing file for reading "
+                             "(sgetn() failed after a sputn())");
+
+            ASSERT_OPEN (!std::memcmp (readbuf, FOO, sizeof FOO), mode,
+                         "failed to open an existing file for reading "
+                         "and writing (sgetn() retrieved the wrong data)");
+        }
+        else {
+            ASSERT_OPEN (0, mode, "failed to open an existing file");
+        }
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::binary | ios::in)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::binary | std::ios::in | iomodes [minx], "");
+
+        // create a file containing various combinations of new-line
+        // and line-feed and read the file in using the ios::binary
+        // flag, expecting the original contents of the file on
+        // output (i.e., no CR/LF conversion on Win32)
+        const char bindata[] = "\nf\ro\n\ro\r\n\b\n\ra\r\rr";
+
+        // rw_fwrite() returns the number of bytes successfully written
+        size = rw_fwrite (tmpfname, bindata, sizeof bindata - 1, "wb");
+
+        Filebuf fb;
+
+        if (fb.open (tmpfname, mode)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            std::memset (readbuf, 0, sizeof readbuf);
+            const std::streamsize N = fb.sgetn (readbuf, readbuf_size);
+
+            if (N != std::streamsize (size))
+                rw_assert (false, __FILE__, __LINE__,
+                           "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                           "got %d", tname, readbuf,
+                           readbuf_size, size, N);
+
+            for (std::streamsize i = 0; i != N; ++i) {
+
+                if (char (readbuf [i]) != bindata [i]) {
+
+                    rw_assert (false, __FILE__, __LINE__,
+                               "basic_filebuf<%s>::sgetn() data "
+                               "mismatch: got \"%s\", expected \"%s\"",
+                               tname, readbuf, bindata);
+                    break;
+                }
+            }
+        }
+        else {
+            ASSERT_OPEN (0, mode, "failed to open an existing file");
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::binary | ios::out)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::binary | std::ios::out | iomodes [minx], "");
+
+        Filebuf fb;
+
+        if (fb.open (tmpfname, mode)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            const charT XYZ[] = {
+                '\n', 'X', '\r', 'Y', '\n', '\r', 'Z', '\r', '\n', '\0'
+            };
+
+            const std::streamsize N = sizeof XYZ / sizeof *XYZ - 1;
+
+            if (N != fb.sputn (XYZ, N))
+                ASSERT_OPEN (false, mode,
+                             "failed to open an existing file for writing "
+                             "(sputn() failed)");
+
+            fb.close ();
+
+            size = 0;
+            buf  = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size, "rb"));
+
+            rw_assert (std::size_t (N) == size, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sputn() wrote %d bytes, "
+                       "%d expected",
+                       tname, size, N * sizeof (charT));
+
+            for (std::streamsize i = 0; i != N; ++i) {
+
+                if (buf [i] != char (XYZ [i])) {
+
+                    rw_assert (false, __FILE__, __LINE__,
+                               "basic_filebuf<%s>::sputn() data "
+                               "mismatch: wrote \"%s\", expected \"%s\"",
+                               tname, buf, XYZ);
+                    break;
+                }
+            }
+        }
+        else {
+            ASSERT_OPEN (0, mode, "failed to open an existing file");
+        }
+    }
+
+    REMOVE_FILE (tmpfname);
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open ((const char*)0, ios::in)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::in | iomodes [minx],
+                    ", file name = 0 [extension]");
+
+        // verify that open() succeeds when the first argument
+        // is the null pointer (the call creates a temporary
+        // file and opens it for reading -- such a file may not
+        // be very useful but since it's harmless there's no
+        // reason it shouldn't be possible)
+
+        Filebuf fb;
+        fb.open ((const char*)0, mode);
+
+        rw_assert (fb.is_open (), __FILE__, __LINE__,
+                   "basic_filebuf<%s>::is_open()", tname);
+
+        // FIXME: verify that the call to close removes the file
+        fb.close ();
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open ((const char*)0, ios::out)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::out | iomodes [minx],
+                    ", file name = 0 [extension]");
+
+        // verify that open() succeeds when the first argument
+        // is the null pointer (the call creates a temporary
+        // file and opens it for writing)
+
+        Filebuf fb;
+        fb.open ((const char*)0, mode);
+
+        rw_assert (fb.is_open (), __FILE__, __LINE__,
+                   "basic_filebuf<%s>::is_open()", tname);
+
+        // FIXME: verify that the call to close removes the file
+        fb.close ();
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open ((const char*)0, ios::in | ios::out)
+
+    for (std::size_t minx = 0; minx != niomodes; ++minx) {
+
+        BEGIN_MODE (std::ios::in | std::ios::out | iomodes [minx],
+                    ", file name = 0 [extension]");
+
+        Filebuf fb;
+        fb.open ((const char*)0, mode);
+
+        rw_assert (fb.is_open (), __FILE__, __LINE__,
+                   "basic_filebuf<%s>::is_open()", tname);
+
+        // FIXME: verify that the call to close removes the file
+        fb.close ();
+    }
+
+
+#if !defined (_WIN32) && !defined (_WIN64)
+
+    //////////////////////////////////////////////////////////////////
+    // exercise open (..., ios::ate)
+
+    BEGIN_MODE (std::ios::ate | std::ios::out, "");
+
+    std::signal (SIGPIPE, ignore_signal);
+
+    if (mkfifo (tmpfname, S_IRWXU))
+        rw_assert (false, __FILE__, __LINE__,
+                   "mkfifo (\"%s\", S_IRWXU) failed", tmpfname);
+    else {
+
+        const pid_t childpid = fork ();
+
+        if (childpid > 0) {   // parent process
+
+            Filebuf fb;
+
+            const Filebuf* const fbp = fb.open (tmpfname, mode);
+
+            // verify that open(ate) fails for an existing but unseekable file
+            if (fbp) {
+                rw_assert (false, __FILE__, __LINE__,
+                           "basic_filebuf<%s>::open(\"%s\", ios::ate | "
+                           "ios::out) == 0 after a failed seek to end",
+                           tname, tmpfname);
+            }
+
+            // verify that is_open() returns false after a failed open
+            if (fb.is_open ()) {
+                rw_assert (false, __FILE__, __LINE__,
+                           "basic_filebuf<%s>::is_open() == false"
+                           " after a failed call to open", tname);
+            }
+
+            // reap our child's exit status
+            wait (0);
+        }
+        else if (0 == childpid) {   // child process
+
+            Filebuf fb;
+
+            if (fb.open (tmpfname, std::ios::in))
+                fb.sgetc ();
+            else
+                rw_assert (false, __FILE__, __LINE__,
+                           "basic_filebuf<%s>::open(\"%s\", "
+                           "ios::in) failed in child process",
+                           tname, tmpfname);
+
+            std::exit (0);
+        }
+        else
+            rw_assert (false, __FILE__, __LINE__, "fork() failed");
+
+        REMOVE_FILE (tmpfname);
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise close ()
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::close ()", tname);
+
+    std::signal (SIGPIPE, ignore_signal);
+
+    if (mkfifo (tmpfname, S_IRWXU))
+        rw_assert (false, __FILE__, __LINE__,
+                   "mkfifo (\"%s\", S_IRWXU) failed", tmpfname);
+    else {
+
+        // verify that close() returns 0 on failure by opening and
+        // writing into a pipe in the parent process that the child
+        // prematurely closes
+
+        const pid_t childpid = fork ();
+
+        if (childpid > 0) {   // parent process
+
+            Filebuf fb;
+
+            if (fb.open (tmpfname, std::ios::out)) {
+
+                // write into the file buffer
+                fb.sputc (charT ());
+
+                // wait for child to open and close the pipe
+                wait (0);
+
+                // close() will try to flush the contents of the buffer
+                // into the pipe which should fail, since the other end
+                // of the pipe has already been closed
+                if (fb.close ())
+                    rw_assert (false, __FILE__, __LINE__,
+                               "basic_filebuf<%s>::close() unexpectedly "
+                               "suceeded", tname);
+            }
+            else
+                rw_assert (false, __FILE__, __LINE__,
+                           "basic_filebuf<%s>::open (\"%s\", ios::out) "
+                           " != 0; got 0", tname, tmpfname);
+        }
+        else if (0 == childpid) {   // child process
+
+            Filebuf fb;
+
+            if (!fb.open (tmpfname, std::ios::in))
+                rw_assert (false, __FILE__, __LINE__,
+                           "basic_filebuf<%s>::open(\"%s\", "
+                           "ios::in) failed in child process",
+                           tname, tmpfname);
+
+            std::exit (0);
+        }
+        else
+            rw_assert (false, __FILE__, __LINE__, "fork() failed");
+
+        REMOVE_FILE (tmpfname);
+    }
+
+#endif   // _WIN{32,64}
+
+}
+
+/***************************************************************************/
+
+template <class charT>
+static void
+test_sync (const char* tname)
+{
+    typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::sync ()", tname);
+
+    const char* const tmpfname = rw_tmpnam (0);
+    if (!tmpfname) {
+        return;
+    }
+
+    {
+        Filebuf fb;
+
+        static const charT foo[] = { 'f', 'o', 'o' };
+        static const charT BAR[] = { 'B', 'A', 'R' };
+
+        // create a non-empty file
+        rw_fwrite (tmpfname, "foobar");
+
+        if (fb.open (tmpfname, std::ios::in)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            charT readbuf [256] = { 0 };
+
+            // read the first part of the file; filebuf will actually
+            // end up reading and caching the entire contents of the
+            // file
+            std::streamsize got = fb.sgetn (readbuf, 3);
+
+            // verify the contents of buffer match that of the file
+            if (3 != got)
+                rw_assert (false, __FILE__, __LINE__,
+                           "sgetn() == 3, got %d", got);
+
+            rw_assert (!std::memcmp (readbuf, foo, sizeof foo),
+                       __FILE__, __LINE__,
+                       "sgetn() retrieved \"%s\", expected \"%s\")",
+                       readbuf, foo);
+
+            // overwrite the contents of the file
+            rw_fwrite (tmpfname, "FOOBAR");
+
+            // call sync, excpecting filebuf to synchromnize with
+            // the new contents of the file while maintaining
+            // the correct position
+            const int res = fb.pubsync ();
+
+            rw_assert (0 == res, __FILE__, __LINE__,
+                       "pubsync() == 0, got %d", res);
+
+            std::memset (readbuf, 0, sizeof readbuf);
+
+            // continue to read the file where the first sgetn()
+            // call left off
+            got = fb.sgetn (readbuf, 3);
+
+            // verify that the new contents of the file have been read
+            if (3 != got)
+                rw_assert (false, __FILE__, __LINE__,
+                           "sgetn() == 3, got %d", got);
+
+            rw_assert (!std::memcmp (readbuf, BAR, sizeof BAR),
+                       __FILE__, __LINE__,
+                       "sgetn() retrieved \"%s\", expected \"%s\")",
+                       readbuf, BAR);
+        }
+        else {
+            rw_assert (false, __FILE__, __LINE__,
+                       "failed to open an existing file");
+        }
+    }
+
+    {
+        Filebuf fb;
+
+        static const charT foo[] = { 'f', 'o', 'o' };
+
+        // create a non-empty file
+        rw_fwrite (tmpfname, "foobar");
+
+        if (fb.open (tmpfname, std::ios::in)) {
+
+            rw_assert (fb.is_open (), __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open()", tname);
+
+            charT readbuf [256] = { 0 };
+
+            // read the first part of the file; filebuf will actually
+            // end up reading and caching the entire contents of the
+            // file
+            std::streamsize got = fb.sgetn (readbuf, 3);
+
+            // verify the contents of buffer match that of the file
+            if (3 != got)
+                rw_assert (false, __FILE__, __LINE__,
+                           "sgetn() == 3, got %d", got);
+
+            rw_assert (!std::memcmp (readbuf, foo, sizeof foo),
+                       __FILE__, __LINE__,
+                       "sgetn() retrieved \"%s\", expected \"%s\")",
+                       readbuf, foo);
+
+            // overwrite the contents of the file with less data
+            rw_fwrite (tmpfname, "BA");
+
+            // call sync, excpecting filebuf to synchromnize with
+            // the new contents of the file
+            // since there is less data in the file than the current
+            // offset, verify that the offset is adjusted to be the
+            // same as the end of the actual file
+            const int res = fb.pubsync ();
+
+            rw_assert (0 == res, __FILE__, __LINE__,
+                       "pubsync() == 0, got %d", res);
+
+            std::memset (readbuf, 0, sizeof readbuf);
+
+            // try to continue to read the file where the first sgetn()
+            // call left off, expecting a failure
+            got = fb.sgetn (readbuf, 3);
+
+            // verify that nothing has been read
+            if (0 != got)
+                rw_assert (false, __FILE__, __LINE__,
+                           "sgetn() == 0, got %d", got);
+
+            // verify that the end of the file is correctly reported
+            const typename Filebuf::off_type off =
+                fb.pubseekoff (0, std::ios::end);
+
+            rw_assert (off == 2, __FILE__, __LINE__,
+                       "pubseekoff(0, ios::end) == 2, got %d", off);
+        }
+        else {
+            rw_assert (false, __FILE__, __LINE__,
+                       "failed to open an existing file");
+        }
+    }
+}
+
+/***************************************************************************/
+
+
+template <class charT>
+static void
+test_attach (const char* tname)
+{
+#ifndef _RWSTD_NO_EXT_FILEBUF
+
+    //////////////////////////////////////////////////////////////////
+    // exercise attach(int) and fd()
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::attach (int) [extension]", tname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::fd () [extension]", tname);
+
+    typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
+
+    // Extension:
+    // basic_filebuf* attach(int fd)
+    //   Connects *this to an open file descriptor, fd. Unless detach() is
+    //   called, the file descriptor will be closed during the first call
+    //   to close() or when the object is destroyed. Returns this on success,
+    //   0 on failure (e.g., when this->is_open() evaluates to true).
+
+    int lastfd = -1;
+
+    {
+        Filebuf fb;
+
+        // get a new valid file descriptor
+        const int fd = open (DEV_NULL, O_RDWR);
+
+        if (fd < 0) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "open (\"%s\", O_RDWR) < 0; "
+                       "aborting test", DEV_NULL);
+            return;
+        }
+
+        lastfd = fd;
+
+        // attach the filebuf object to the file descriptor
+        Filebuf *pfb = fb.attach (fd);
+
+        // verify that attach() succeeded
+        if (&fb != pfb)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) == %p, got 0",
+                       tname, fd, &fb);
+
+        // verify that fd() returns the attached file descriptor
+        if (fd != fb.fd ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::fd() == %d, got %d",
+                       tname, fd, fb.fd ());
+
+        // verify that the filebuf object is open
+        if (!fb.is_open ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open() == true, "
+                       "got false after a successful call to attach(%d)",
+                       tname, fd);
+
+        // Filebuf dtor should close the file descriptor (checked below)
+    }
+
+    {
+        Filebuf fb;
+
+        // create a new file descriptor
+        const int fd = open (DEV_NULL, O_RDWR);
+
+        // verify that the dtor in the block above closed the filebuf's fd
+        if (fd != lastfd)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) caused "
+                       "a file descriptor leak after object destruction",
+                       tname, lastfd);
+
+        lastfd = fd;
+
+        // attach this object to the new file descriptor
+        Filebuf *pfb = fb.attach (fd);
+
+        // verify that attach() succeeded
+        if (&fb != pfb)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) == %p, got 0",
+                       tname, fd, &fb);
+
+        // verify that fd() returns the attached file descriptor
+        if (fd != fb.fd ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::fd() == %d, got %d",
+                       tname, fd, fb.fd ());
+
+        // verify that the filebuf object is open
+        if (!fb.is_open ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open() == true, "
+                       "got false after a successful call to attach(%d)",
+                       tname, fd);
+
+        // call close
+        pfb = fb.close ();
+
+        // verify that the call to close() succeeded
+        if (&fb != pfb)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::close() == %p, got 0",
+                       tname, &fb);
+
+        // verify that fd() returns an invalid file descriptor after
+        // a successful call to close()
+        if (0 <= fb.fd ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::fd() < 0, got %d after "
+                       "a call to close()",
+                       tname, fb.fd ());
+
+        // verify that is_open() returns false after the call to close()
+        if (fb.is_open ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open() == false, "
+                       "got true after a successful call to close()",
+                       tname);
+    }
+
+    {
+        Filebuf fb;
+
+        // get a new valid file descriptor
+        const int fd = open (DEV_NULL, O_RDWR);
+
+        // verify that the dtor in the block above closed the filebuf's fd
+        if (fd != lastfd)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) caused "
+                       "a file descriptor leak after object destruction",
+                       tname, lastfd);
+
+        lastfd = fd;
+
+        // attach the filebuf object to the file descriptor
+        Filebuf *pfb = fb.attach (fd);
+
+        // verify that attach() succeeded
+        if (&fb != pfb)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) == %p, got 0",
+                       tname, fd, &fb);
+
+        pfb = fb.attach (fd);
+
+        // verify that a subsequent call to attach() (with the same
+        // or different file descriptor) failed
+        if (&fb == pfb)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) == 0, got %p",
+                       tname, fd, &fb);
+
+        const int new_fd = open (DEV_NULL, O_RDWR);
+
+        pfb = fb.attach (fd);
+
+        if (&fb == pfb)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) == 0, got %p",
+                       tname, fd, &fb);
+
+        close (new_fd);
+
+        // Filebuf dtor should close the file descriptor (checked below)
+    }
+
+    //////////////////////////////////////////////////////////////////
+    // exercise detach()
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s>::detach () [extension]", tname);
+
+    // Extension:
+    // int detach()
+    //   Flushes any waiting output to the file associated with the file
+    //   descriptor, and disconnects the file descriptor from *this so
+    //   that subsequent calls to close() will not close the file
+    //   descriptor. Returns the detached file descriptor on success,
+    //   -1 on failure.
+
+    {
+        Filebuf fb;
+
+        // get a new valid file descriptor
+        const int fd = open (DEV_NULL, O_RDWR);
+
+        // verify that the dtor in the block above closed the filebuf's fd
+        if (fd != lastfd)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::attach(int = %d) caused "
+                       "a file descriptor leak after object destruction",
+                       tname, lastfd);
+
+        lastfd = fd;
+
+        // attach the filebuf object to the file descriptor
+        fb.attach (fd);
+
+        const int old_fd = fb.detach ();
+
+        // verify that detach() returned the original file descriptor
+        if (fd != old_fd)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() == %d, got %d",
+                       tname, fd, old_fd);
+
+        // verify that is_open() returns false after the call to detach()
+        if (fb.is_open ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open() == false, "
+                       "got true after a call to detach()", tname);
+
+        // verify that fd() returns an invalid file descriptor after
+        // a successful call to detach()
+        if (0 <= fb.fd ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::fd() < 0, got %d after "
+                       "a call to detach()()", tname, fb.fd ());
+
+        close (old_fd);
+
+        errno = 0;
+    }
+
+    // verify that the dtor in the block above didn't try to close
+    // the already explicitly closed file descriptor
+    if (errno)
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::~basic_filebuf() attempted to "
+                   "close a detached file descriptor", tname);
+
+    {
+        Filebuf fb;
+
+        if (fb.fd () >= 0)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::fd() < 0, got %d for an "
+                       "object that's not open", tname, fb.fd ());
+
+        const int old_fd = fb.detach ();
+
+        // verify detach() returns an invalid file descriptor when called
+        // on a filebuf object that's not open
+        if (0 <= old_fd)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() < 0, got %d",
+                       tname, old_fd);
+    }
+
+    const char* const tmpfname = rw_tmpnam (0);
+
+    if (!tmpfname) {
+        return;
+    }
+
+    {
+        Filebuf fb;
+
+        // open a writeable file
+        const int fd = open (tmpfname, O_CREAT | O_WRONLY, 0666);
+
+        if (fd < 0) {
+            rw_warn (false, __FILE__, __LINE__,
+                     "open (\"%s\", O_CREAT | O_WRONLY, 0666) "
+                     "failed: %m", tmpfname);
+        }
+
+        // attach filebuf to the file descriptor
+        fb.attach (fd);
+
+        const charT data[] = { '0', '1', '2', '\0' };
+        const std::size_t nelems = sizeof data / sizeof *data - 1;
+
+        // write into the filebuf object
+        const std::size_t wrote = fb.sputn (data, nelems);
+
+        if (wrote != nelems)
+            rw_assert (false, __FILE__, __LINE__,
+                       "sputn(\"%s\", %d) == %d, got %d",
+                       data, nelems, nelems, wrote);
+
+        // and detach
+        const int old_fd = fb.detach ();
+        if (fd != old_fd)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() == %d, got %d",
+                       fd, old_fd);
+
+        // verify by reading the contents of the named file that
+        // the call to detach() wrote the contents of the filebuf
+        // object's data buffer into the file before detaching
+        std::size_t nbytes = 0;
+        const char* const buf =
+            _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &nbytes));
+
+        if (   !buf || nbytes != nelems
+            || rw_strncmp (buf, data, nelems))
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() failed to flush "
+                       "data to the attached file descriptor; "
+                       "read %lu bytes: %s, expected \"012\"",
+                       tname, nbytes, buf);
+
+        REMOVE_FILE (tmpfname);
+    }
+
+    {
+        Filebuf fb;
+
+        // create a new file and open it for writing
+        const int fd = open (tmpfname, O_CREAT | O_WRONLY, 0666);
+
+        // attach filebuf to the file descriptor (will set mode to ios::out
+        // based on the O_WRONLY open mode of the file descriptor)
+        fb.attach (fd);
+
+        // close and reopen the same file descriptor as read-only
+        close (fd);
+        if (fd != open (tmpfname, O_RDONLY)) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "open (\"%s\", O_RDONLY) != %d; aborting test",
+                       tmpfname, fd);
+            return;
+        }
+
+        static const charT data[] = { '0', '1', '2', '\0' };
+        static const std::size_t nelems = sizeof data / sizeof *data - 1;
+
+        // write into the filebuf object's buffer
+        if (nelems != std::size_t (fb.sputn (data, nelems)))
+            rw_assert (false, __FILE__, __LINE__, "sputn() failed");
+
+        // and try to detach (must try to flush data into file)
+        const int bad_fd = fb.detach ();
+
+        // verify that detach() failed (due to the failure
+        // to write to a read-only file descriptor)
+        if (bad_fd >= 0)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() < 0, got %d after "
+                       "a failure to write to read-only file descriptor",
+                       tname, bad_fd);
+
+        // verify that the call to detach() failed to flush the contents
+        // of the filebuf object's data buffer into a read-only file by
+        // testing that the size of the file is unchanged (i.e., 0)
+        std::size_t nbytes = 0;
+
+        const char* buf  =
+            _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, 0));
+
+        if (buf && nbytes)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() unexpectedly wrote "
+                       "%u bytes to a read-only file: \"%s\"",
+                       tname, nbytes, buf);
+
+        // close and reopen the same file descriptor as write-only
+        close (fd);
+        if (fd != open (tmpfname, O_WRONLY)) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "open (\"%s\", O_WRONLY) != %d; aborting test",
+                       tmpfname, fd);
+            return;
+        }
+
+        // and detach (must successfully flush data into file)
+        const int good_fd = fb.detach ();
+
+        // verify that the call to detach() was successful
+        if (good_fd < 0)
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() == %d, got %d",
+                       tname, fd, good_fd);
+
+        // verify that the filebuf object is no longer open
+        if (fb.is_open ())
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::is_open() unexpectedly "
+                       "true after a successful call to detach()",
+                       tname);
+
+        // verify that this call to detach() wrote out the contents of the
+        // filebuf object's data buffer into the writeable file by checking
+        // the size and contents of the file
+        buf = (char*)rw_fread (tmpfname, &nbytes);
+
+        if (   !buf || nbytes != nelems
+            || rw_strncmp (buf, data, nelems))
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::detach() failed to flush "
+                       "data to the attached file descriptor; "
+                       "read %lu bytes: %s, expected %s",
+                       tname, nbytes, buf, data);
+
+        // close the file descriptor before removing the file
+        close (good_fd);
+
+        REMOVE_FILE (tmpfname);
+    }
+
+#else   // if defined (_RWSTD_NO_EXT_FILEBUF)
+
+    _RWSTD_UNUSED (tname);
+
+#endif   // _RWSTD_NO_EXT_FILEBUF
+
+}
+
+/***************************************************************************/
+
+// CodeCvt<charT> performs a state-dependent conversion
+// the first byte of the state_type object encodes the state,
+// any remaining bytes must be 0
+// internal characters are externally represented as sequences
+// of 1 to 22 chars, escape sequences are 11 to 13 chars long
+// and all start with the "<ESC-" prefix
+//
+// for example, the string "Hello, World!\n" will be externally
+// represented by the following sequence of narrow characters:
+//
+// "<ESC-UPPER>H<ESC-LOWER>ello<ESC-PUNCT><comma><SP><ESC-UPPER>"
+// "W<ESC-LOWER>orld<ESC-PUNCT><exclamation-mark><ESC-CNTRL>\n"
+
+template <class charT>
+struct CodeCvt: std::codecvt<charT, char, std::mbstate_t>
+{
+    typedef std::codecvt<charT, char, std::mbstate_t> Base;
+
+    enum { cntrl, punct, digit, upper, lower, hexcode };
+
+public:
+
+    typedef typename Base::intern_type intern_type;
+    typedef typename Base::extern_type extern_type;
+    typedef typename Base::state_type  state_type;
+
+    explicit CodeCvt (std::size_t ref = 0)
+        : Base (ref) { }
+
+protected:
+
+    virtual std::codecvt_base::result
+    do_out (state_type&,
+            const intern_type*, const intern_type*, const intern_type*&,
+            extern_type*, extern_type*, extern_type*&) const;
+
+    virtual std::codecvt_base::result
+    do_in (state_type&,
+           const extern_type*, const extern_type*, const extern_type*&,
+           intern_type*, intern_type*, intern_type*&) const;
+
+    virtual std::codecvt_base::result
+    do_unshift (state_type&, extern_type*, extern_type*, extern_type*&) const;
+
+    virtual int do_encoding () const _THROWS (()) {
+        return -1;   // state-dependent encoding
+    }
+
+    virtual bool do_always_noconv () const _THROWS (()) {
+        return false;   // conversion always necessary
+    }
+
+    // returns the maximum `N' of extern chars in the range [from, from_end)
+    // such that N represents max or fewer internal chars
+    virtual int
+    do_length (state_type&, const extern_type*,
+               const extern_type*, std::size_t) const;
+
+    // returns the max value do_length (s, from, from_end, 1) can return
+    // for any valid range [from, from_end) - see LWG issue 74
+    virtual int do_max_length () const _THROWS (()) {
+        return -1;
+    }
+};
+
+
+extern const char* const escapes[] = {
+    "<ESC-CNTRL>", "<ESC-PUNCT>", "<ESC-DIGIT>",
+    "<ESC-UPPER>", "<ESC-LOWER>", "<ESC-HEXCODE>"
+};
+
+
+extern const char* const charnames[] = {
+    // control characters (<ESC-CNTRL>)
+    "<NUL>", "<SOH>", "<STX>", "<ETX>", "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
+    "<BS>", "<HT>", "\n", "<VT>", "<FF>", "<CR>" /* '\r' */, "<SO>", "<SI>",
+    "<DLE>", "<DC1>", "<DC2>", "<DC3>", "<DC4>", "<NAK>", "<SYN>", "<ETB>",
+    "<CAN>", "<EM>", "<SUB>", "<ESC>", "<FS>", "<GS>", "<RS>", "<US>",
+    // punctuators (<ESC-PUNCT>)
+    "<SP>",
+    "<exclamation-mark>",
+    "<quotation-mark>",
+    "<number-sign>",
+    "<dollar-sign>",
+    "<percent-sign>",
+    "<ampersand>",
+    "<apostrophe>",
+    "<left-parenthesis>",
+    "<right-parenthesis>",
+    "<asterisk>",
+    "<plus-sign>",
+    "<comma>",
+    "<hyphen>",
+    "<period>",
+    "<slash>",
+    // digits (<ESC-DIGIT>)
+    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+    // punctuators (<ESC-PUNCT>)
+    "<colon>",
+    "<semicolon>",
+    "<less-than-sign>",
+    "<equals-sign>",
+    "<greater-than-sign>",
+    "<question-mark>",
+    "<commercial-at>",
+    // uppercase letters (<ESC_UPPER>)
+    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
+    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+    // punctuators (<ESC-PUNCT>)
+    "<left-square-bracket>",
+    "<backslash>",
+    "<right-square-bracket>",
+    "<circumflex>",
+    "<underscore>",
+    "<grave-accent>",
+    // lowercase letters (<ESC_LOWER>)
+    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+    "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+    // punctuators (<ESC-PUNCT>)
+    "<left-curly-bracket>",
+    "<vertical-line>",
+    "<right-curly-bracket>",
+    "<tilde>",
+    // control characters (<ESC-CNTRL>)
+    "<DEL>"
+    // characters above '\x7f' are represented in hex (<ESC-HEXCODE>)
+};
+
+
+template <class charT>
+std::codecvt_base::result
+CodeCvt<charT>::do_out (      state_type  &state,
+                        const intern_type *from,
+                        const intern_type *from_end,
+                        const intern_type *&from_next,
+                              extern_type *to,
+                              extern_type *to_end,
+                              extern_type *&to_next) const
+{
+    std::codecvt_base::result res = std::codecvt_base::ok;
+
+    char hexbuf [5] = "";
+
+    int int_state = hexcode;
+
+    for (from_next = from, to_next = to; from_next != from_end; ++from_next) {
+
+        typedef std::char_traits<intern_type> Traits;
+
+        const std::size_t uch = std::size_t (Traits::to_int_type (*from_next));
+
+        if (uch < ' ') {
+            int_state = cntrl;
+        }
+        else if (uch >= ' ' && uch <= '/') {
+            int_state = punct;
+        }
+        else if (uch >= '0' && uch <= '9') {
+            int_state = digit;
+        }
+        else if (uch >= ':' && uch <= '@') {
+            int_state = punct;
+        }
+        else if (uch >= 'A' && uch <= 'Z') {
+            int_state = upper;
+        }
+        else if (uch >= '[' && uch <= '`') {
+            int_state = punct;
+        }
+        else if (uch >= 'a' && uch <= 'z') {
+            int_state = lower;
+        }
+        else if (uch >= '{' && uch <= '~') {
+            int_state = punct;
+        }
+        else if (uch == '\x7f') {
+            int_state = cntrl;
+        }
+        else if (uch <= 0xff) {
+            int_state = hexcode;
+
+            if (to_end - to_next < 4) {
+                res = std::codecvt_base::partial;
+                break;
+            }
+
+            // convert `uch' to a hexadecimal escape sequence
+            static const char hexdigits[] = "0123456789abcdef";
+
+            hexbuf [0] = '\\';
+            hexbuf [1] = 'x';
+            hexbuf [2] = hexdigits [uch >> 8];
+            hexbuf [3] = hexdigits [uch & 0x0fU];
+            hexbuf [4] = '\0';
+        }
+        else {
+            res = std::codecvt_base::error;
+            break;
+        }
+
+        if (int (*(char*)&state) != int_state) {
+
+            const std::size_t len = std::strlen (escapes [int_state]);
+
+            if (len > std::size_t (to_end - to_next)) {
+                res = std::codecvt_base::partial;
+                break;
+            }
+
+            std::memcpy (to_next, escapes [int_state], len);
+            to_next += len;
+
+            *(char*)&state = char (int_state);
+        }
+
+        const char* const outs = *hexbuf ? hexbuf : charnames [uch];
+
+        const std::size_t len = std::strlen (outs);
+
+        if (len > std::size_t (to_end - to_next)) {
+            res = std::codecvt_base::partial;
+            break;
+        }
+
+        std::memcpy (to_next, outs, len);
+        to_next += len;
+    }
+
+    return res;
+}
+
+
+// looks for a character whose name starts at `*pbeg' in the table
+// of character names, `chartbl' containing `tblsize' elements
+// if a unique (even partial) match is found, returns the index of
+// the character in the table; if the unique match is complete,
+// also advances `*pbeg' by the length of the match
+std::size_t find_char (const char **pbeg, const char *end,
+                       const char* const chartbl[], std::size_t tblsize)
+{
+    const std::size_t nchars = sizeof charnames / sizeof *charnames;
+
+    std::size_t hits [nchars] = { 0 };
+
+    const char* cur = *pbeg;
+
+    for (; cur != end; ++cur) {
+
+        const std::size_t inx = cur - *pbeg;
+
+        std::size_t nmatches = 0;
+
+        for (std::size_t i = 0; i != tblsize; ++i) {
+            if (hits [i] == inx && chartbl [i] && *cur == chartbl [i][inx]) {
+                ++hits [i];
+                ++nmatches;
+            }
+        }
+
+        if (!nmatches)
+            break;
+    }
+
+    std::size_t nmatches = 0;
+    std::size_t imaxhits = 0;
+    std::size_t inx      = std::size_t (-1);
+
+    for (std::size_t i = 0; i != tblsize; ++i) {
+
+        if (!chartbl [i])
+            continue;
+
+        if (hits [i] > hits [imaxhits])
+            imaxhits = i;
+
+        if (hits [i] == std::strlen (chartbl [i])) {
+
+            if (std::size_t (-1) == inx)
+                inx = 0;
+
+            if (hits [i] == hits [inx]) {
+                ++nmatches;
+                inx = i;
+            }
+            else if (hits [i] > hits [inx]) {
+                nmatches = 1;
+                inx = i;
+            }
+        }
+        else if (std::size_t (-1) != inx && hits [i] > hits [inx]) {
+            nmatches = 0;
+            inx = i;
+        }
+    }
+
+    if (inx != std::size_t (-1) && 1 == nmatches)
+        *pbeg = cur;
+
+    if (hits [imaxhits] == std::size_t (end - *pbeg))
+        inx = imaxhits;
+
+    return inx;
+}
+
+
+template <class charT>
+std::codecvt_base::result
+CodeCvt<charT>::do_in (      state_type  &state,
+                       const extern_type *from,
+                       const extern_type *from_end,
+                       const extern_type *&from_next,
+                             intern_type *to,
+                             intern_type *to_end,
+                             intern_type *&to_next) const
+{
+    std::codecvt_base::result res = std::codecvt_base::ok;
+
+    for (to_next = to, from_next = from; to_next != to_end; ++to_next) {
+
+    top_of_loop:
+
+        if (from_next == from_end)
+            break;
+
+        const std::size_t navail = from_end - from_next;
+
+        // check the beginning of the sequence to see if
+        // it may possibly start with an escape sequence
+
+        if (navail <= 5 && !std::memcmp (from_next, "<ESC-", navail))
+            return std::codecvt_base::partial;
+
+        if (navail > 5 && !std::memcmp (from_next, "<ESC-", 5)) {
+
+            // found the beginning of what might be an escape sequence
+
+            if (navail >= 11) {
+
+                const std::size_t nescapes = sizeof escapes / sizeof *escapes;
+
+                for (std::size_t i = 0; i != nescapes; ++i) {
+
+                    const std::size_t len = std::strlen (escapes [i]);
+
+                    if (!std::memcmp (escapes [i], from_next, len)) {
+
+                        // found an escape sequence
+
+                        // set the state variable
+                        *(char*)&state = char (i);
+
+                        // advance past the escape sequence
+                        from_next += len;
+
+                        // continue iterating over the rest of the sequence
+                        goto top_of_loop;
+                    }
+                }
+            }
+            else {
+                // not enough external elements to convert
+                // to an internal character
+                return std::codecvt_base::partial;
+            }
+        }
+
+        std::size_t chinx = std::size_t (-1);   // character index
+        std::size_t choff = 0;                  // character offset
+
+        const char* const from_next_save = from_next;
+
+        switch (*(char*)&state) {
+
+        case cntrl:
+            chinx = find_char (&from_next, from_end, charnames, ' ' + 1U);
+            if (   std::size_t (-1) == chinx
+                && 5 <= from_end - from_next
+                && !std::memcmp (from_next, "<DEL>", 5)) {
+                chinx = '\x7f';
+                from_next += 5;
+            }
+            break;
+
+        case punct: {
+
+            static const char* const pun[] = {
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                "<SP>",
+                "<exclamation-mark>",
+                "<quotation-mark>",
+                "<number-sign>",
+                "<dollar-sign>",
+                "<percent-sign>",
+                "<ampersand>",
+                "<apostrophe>",
+                "<left-parenthesis>",
+                "<right-parenthesis>",
+                "<asterisk>",
+                "<plus-sign>",
+                "<comma>",
+                "<hyphen>",
+                "<period>",
+                "<slash>",
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                "<colon>",
+                "<semicolon>",
+                "<less-than-sign>",
+                "<equals-sign>",
+                "<greater-than-sign>",
+                "<question-mark>",
+                "<commercial-at>",
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                "<left-square-bracket>",
+                "<backslash>",
+                "<right-square-bracket>",
+                "<circumflex>",
+                "<underscore>",
+                "<grave-accent>",
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                "<left-curly-bracket>",
+                "<vertical-line>",
+                "<right-curly-bracket>",
+                "<tilde>",
+                0
+            };
+
+            chinx = find_char (&from_next, from_end,
+                               pun, sizeof pun / sizeof *pun);
+
+            break;
+        }
+
+        case digit:
+            choff = '0';
+
+            if (*from_next >= '0' && *from_next <= '9')
+                chinx = *from_next++ - '0';
+
+            // commented out code replaced by the above for efficiency
+            // chinx = find_char (&from_next, from_end, charnames + choff, 10);
+            break;
+
+        case lower:
+            choff = 'a';
+
+            // assume ASCII
+            if (*from_next >= 'a' && *from_next <= 'z')
+                chinx = *from_next++ - 'a';
+
+            // commented out code replaced by the above for efficiency
+            // chinx = find_char (&from_next, from_end, charnames + choff, 26);
+            break;
+
+        case upper:
+            choff = 'A';
+
+            // assume ASCII
+            if (*from_next >= 'A' && *from_next <= 'Z')
+                chinx = *from_next++ - 'A';
+
+            // commented out code replaced by the above for efficiency
+            // chinx = find_char (&from_next, from_end, charnames + choff, 26);
+            break;
+
+        case hexcode: {
+            if (from_end - from_next < 4)
+                return std::codecvt_base::partial;
+
+            if (   from_next [0] != '\\' || from_next [1] != 'x'
+                || !(   from_next [2] >= '0' && from_next [2] <= '9'
+                     || from_next [2] >= 'A' && from_next [2] <= 'F'
+                     || from_next [2] >= 'a' && from_next [2] <= 'f')
+                || !(   from_next [3] >= '0' && from_next [3] <= '9'
+                     || from_next [3] >= 'A' && from_next [3] <= 'F'
+                     || from_next [3] >= 'a' && from_next [3] <= 'f'))
+                return std::codecvt_base::error;
+
+            // convert a hex literal to a number
+            chinx = 0;
+            for (unsigned i = 2; i != 4; ++i) {
+                chinx <<= 4;
+                if (from_next [i] >= '0' && from_next [i] <= '9')
+                    chinx += from_next [i] - '0';
+                else if (from_next [i] >= 'A' && from_next [i] <= 'F')
+                    chinx += 10 + (from_next [i] - 'A');
+                else
+                    chinx += 10 + (from_next [i] - 'a');
+            }
+
+            break;
+        }
+
+        default:
+            chinx = std::size_t (-1);
+            break;
+        }
+
+        if (std::size_t (-1) == chinx) {
+            res = std::codecvt_base::error;
+            break;
+        }
+
+        // the combination of a valid `chinx' and an unmodified
+        // `from_next' pointer indicates a partial match
+        if (from_next == from_next_save) {
+            res = std::codecvt_base::partial;
+            break;
+        }
+
+        *to_next = charT (chinx + choff);
+    }
+
+    return res;
+}
+
+
+template <class charT>
+std::codecvt_base::result
+CodeCvt<charT>::
+do_unshift (state_type  &state,
+            extern_type *to,
+            extern_type *to_end,
+            extern_type *&to_next) const
+{
+    to_next = to;
+
+    if (!*(char*)&state)
+        return std::codecvt_base::noconv;
+
+    const std::size_t len = std::strlen (escapes [0]);
+
+    if (len > std::size_t (to_end - to_next))
+        return std::codecvt_base::partial;
+
+    std::memcpy (to_next, escapes [0], len);
+    to_next += len;
+
+    std::memset (&state, 0, sizeof state);
+
+    return std::codecvt_base::ok;
+}
+
+
+template <class charT>
+int
+CodeCvt<charT>::
+do_length (state_type&, const extern_type*,
+           const extern_type*, std::size_t) const
+{
+    return 0;
+}
+
+/***************************************************************************/
+
+static const char*
+get_codecvt_result (std::codecvt_base::result res)
+{
+    switch (res) {
+    case std::codecvt_base::error: return "std::codecvt_base::error";
+    case std::codecvt_base::noconv: return "std::codecvt_base::noconv";
+    case std::codecvt_base::ok: return "std::codecvt_base::ok";
+    case std::codecvt_base::partial: return "std::codecvt_base::partial";
+    }
+
+    return "unknown";
+}
+
+
+template <class charT>
+static void
+test_codecvt (const char* tname)
+{
+    rw_info (0, __FILE__, __LINE__,
+             "std::basic_filebuf<%s> with a state-dependent encoding",
+             tname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "sgetn() with state-dependent code conversion");
+
+    // read in the text of this source file as plain text
+
+    typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
+    typedef typename Filebuf::off_type                          off_type;
+
+    Filebuf noconv_in;
+
+    // use ios_base::binary to avoid CR/LF conversion issues
+    if (!noconv_in.open (__FILE__, std::ios::binary | std::ios::in)) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::open (\"%s\", "
+                   "ios_base::binary | ios_base::in) failed",
+                   tname, __FILE__);
+        return;
+    }
+
+    // seek to the end of the file to get its size in bytes
+    const std::streamsize noconv_fsize =
+        noconv_in.pubseekoff (0, std::ios::end);
+
+    if (noconv_fsize <= 0) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::pubseekoff "
+                   "(0, ios_base::end) >= 0, got %i",
+                   tname, noconv_fsize);
+        return;
+    }
+
+    if (0 != noconv_in.pubseekoff (0, std::ios::beg)) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::pubseekoff "
+                   "(0, ios_base::beg) failed", tname);
+        return;
+    }
+
+    const char* const tmpfname = rw_tmpnam (0);
+
+    if (!tmpfname) {
+        return;
+    }
+
+    // allocate a buffer large enough to hold the contents of the
+    // file in internal representation without codeset conversion
+    charT *noconv_intbuf = new charT [noconv_fsize];
+
+    // sgetn() returns the number of internal characters read in
+    // which must equal the number of external characters read by
+    // the `noconv_in' file buffer (since it does no conversion)
+    std::streamsize nread = noconv_in.sgetn (noconv_intbuf, noconv_fsize);
+
+    if (nread != noconv_fsize) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::sgetn (%p, %i) == %i), got %i",
+                   tname, noconv_intbuf,
+                   noconv_fsize, noconv_fsize, nread);
+        delete[] noconv_intbuf;
+        return;
+    }
+
+    const CodeCvt<charT> cvt (1);
+
+    // write out the text of this file using the test codecvt facet
+    // that performs state-dependent encoding
+    Filebuf conv_out;
+
+    conv_out.pubimbue (std::locale (std::locale::classic (), &cvt));
+
+    if (!conv_out.open (tmpfname, std::ios::binary | std::ios::out)) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::open (\"%s\", ios_base::out) "
+                   "failed", tname, tmpfname);
+        delete[] noconv_intbuf;
+        return;
+    }
+
+    // sputn() returns the number of internal characters written out
+    // which must equal the number of external characters read by
+    // the `noconv_in' file buffer (since it does no conversion)
+    const std::streamsize nwrote = conv_out.sputn (noconv_intbuf, nread);
+
+    if (nread != nwrote) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::sputn (\"%s\", %i) == %i), "
+                   "got %i", tname,
+                   noconv_intbuf, nread, nread, nwrote);
+        delete[] noconv_intbuf;
+        return;
+    }
+
+    // close file buffer to flush it out
+    if (!conv_out.close ())
+        rw_warn (false, __FILE__, __LINE__,
+                 "basic_filebuf<%s>(\"%s\").close() failed",
+                 tname, tmpfname);
+
+    std::filebuf nin;   // narrow input stream buffer
+
+    if (!nin.open (tmpfname, std::ios::binary | std::ios::in)) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::open (\"%s\", ios_base::in) failed",
+                   tname, tmpfname);
+        delete[] noconv_intbuf;
+        return;
+    }
+
+    // get the size of the converted file in external characters
+    const std::streamsize conv_fsize = nin.pubseekoff (0, std::ios::end);
+
+    if (conv_fsize <= noconv_fsize) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "filebuf::pubseekoff (0, ios_base::end) > %i, got %i",
+                   noconv_fsize, conv_fsize);
+        delete[] noconv_intbuf;
+        return;
+    }
+
+    if (0 != nin.pubseekoff (0, std::ios::beg)) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "filebuf::pubseekoff (0, ios_base::beg) failed");
+        delete[] noconv_intbuf;
+        return;
+    }
+
+    // allocate a buffer large enough to hold the contents of the
+    // file in external representation without codeset conversion
+    char *noconv_extbuf = new char [conv_fsize];
+    std::memset (noconv_extbuf, 0, conv_fsize);
+
+    // read in the (unconverted) contents of the file
+    nread = nin.sgetn (noconv_extbuf, conv_fsize);
+
+    if (nread != conv_fsize) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "filebuf::sgetn (%p, %i) == %i, got %i",
+                   noconv_extbuf, conv_fsize, conv_fsize, nread);
+        delete[] noconv_intbuf;
+        delete[] noconv_extbuf;
+        return;
+    }
+
+    if (!nin.close ())
+        rw_warn (false, __FILE__, __LINE__,
+                 "basic_filebuf<%s>(\"%s\").close() failed",
+                 tname, tmpfname);
+
+    std::mbstate_t state;
+    std::memset (&state, 0, sizeof state);
+
+    const char *from      = noconv_extbuf;
+    const char *from_end  = noconv_extbuf + nread;
+    const char *from_next = 0;
+
+    // allocate a buffer large enough to hold the contents of the
+    // file in internal representation with codeset conversion
+    charT *conv_intbuf = new charT [noconv_fsize];
+    std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
+
+    charT *to      = conv_intbuf;
+    charT *to_end  = conv_intbuf + noconv_fsize;
+    charT *to_next = 0;
+
+    // convert the contents of the file using the test codecvt facet
+    const std::codecvt_base::result cvtres =
+        cvt.in (state, from, from_end, from_next, to, to_end, to_next);
+
+    if (std::codecvt_base::ok != cvtres) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "CodeCvt<%s>::in ({ %d, ... },  %p, %p, %p, %p, %p, %p) "
+                   "== std::codecvt_base::ok, got %s",
+                   tname, *(unsigned char*)&state,
+                   from, from_end, from_next, to, to_end, to_next,
+                   get_codecvt_result (cvtres));
+        delete[] noconv_intbuf;
+        delete[] noconv_extbuf;
+        delete[] conv_intbuf;
+        return;
+    }
+
+    const std::streamsize n_ext_cvt = from_next - from;
+    const std::streamsize n_int_cvt = to_next - to;
+
+    rw_assert (n_ext_cvt == conv_fsize, __FILE__, __LINE__,
+               "CodeCvt<%s>::in() converted %i external characters, "
+               "expected %i", tname, n_ext_cvt, conv_fsize);
+
+    rw_assert (n_int_cvt == noconv_fsize, __FILE__, __LINE__,
+               "CodeCvt<%s>::in() produced %i internal characters, "
+               "expected %i", tname, n_int_cvt, noconv_fsize);
+
+    // verify that the contents of the unconverted internal buffer
+    // (i.e., the text of this file in internal representation) are
+    // the same as the contents of the converted internal buffer
+    // (i.e., the text of this file converted to the external
+    // state-dependent representation and then converted back to
+    // the internal encoding)
+    rw_assert (!std::memcmp (noconv_intbuf, conv_intbuf, n_int_cvt),
+               __FILE__, __LINE__, "code conversion mismatch");
+
+
+    // read in the text of the temporary file encoded in
+    // a state-dependent encoding and convert it to its
+    // internal representation
+    Filebuf conv_in;
+
+    conv_in.pubimbue (std::locale (std::locale::classic (), &cvt));
+
+    conv_in.open (tmpfname, std::ios::in);
+
+    std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
+
+    nread = conv_in.sgetn (conv_intbuf, noconv_fsize);
+
+    if (nread != noconv_fsize) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "basic_filebuf<%s>::sgetn (%p, %i) == %i, got %i",
+                   tname, conv_intbuf, noconv_fsize,
+                   noconv_fsize, nread);
+        delete[] noconv_intbuf;
+        delete[] noconv_extbuf;
+        delete[] conv_intbuf;
+        return;
+    }
+
+    rw_assert (!std::memcmp (noconv_intbuf, conv_intbuf,
+                             n_int_cvt * sizeof *noconv_intbuf),
+               __FILE__, __LINE__, "code conversion mismatch");
+
+    if (!noconv_in.close ())
+        rw_warn (false, __FILE__, __LINE__,
+                 "basic_filebuf<%s>(" __FILE__ ").close() failed",
+                 tname);
+
+    if (!conv_in.close ())
+        rw_warn (false, __FILE__, __LINE__,
+                 "basic_filebuf<%s>(\"%s\").close() failed",
+                 tname, tmpfname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "pubseekoff() and pubseekpos() with state-dependent "
+             "code conversion");
+
+    noconv_in.open (__FILE__, std::ios::binary | std::ios::in);
+    conv_in.open (tmpfname, std::ios::binary | std::ios::in);
+
+    for (std::streamsize i = 0; i < noconv_fsize; i += noconv_fsize / 13) {
+
+        typedef typename Filebuf::pos_type pos_type;
+
+        noconv_in.pubseekoff (0, std::ios::beg);
+        conv_in.pubseekoff (0, std::ios::beg);
+
+        std::memset (noconv_intbuf, 0, noconv_fsize * sizeof *noconv_intbuf);
+        std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
+
+        nread = noconv_in.sgetn (noconv_intbuf, i);
+
+        if (i != nread) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                       "got %d", tname, noconv_intbuf, i, i, nread);
+            break;
+        }
+
+        nread = conv_in.sgetn (conv_intbuf, i);
+
+        if (i != nread) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                       "got %d", tname, conv_intbuf, i, i, nread);
+            break;
+        }
+
+        if (std::memcmp (noconv_intbuf, conv_intbuf,
+                         i * sizeof *conv_intbuf)) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "%d. code conversion mismatch", i);
+            break;
+        }
+
+        const pos_type noconv_pos =
+            noconv_in.pubseekoff (0, std::ios::cur);
+
+        const pos_type conv_pos =
+            conv_in.pubseekoff (0, std::ios::cur);
+
+        std::memset (noconv_intbuf, 0, noconv_fsize * sizeof *noconv_intbuf);
+        std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
+
+        nread = noconv_in.sgetn (noconv_intbuf, noconv_fsize);
+
+        if (noconv_fsize - i != nread) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                       "got %d", tname, noconv_intbuf,
+                       noconv_fsize, noconv_fsize - i, nread);
+            break;
+        }
+
+        nread = conv_in.sgetn (conv_intbuf, noconv_fsize);
+
+        if (noconv_fsize - i != nread) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                       "got %d", tname, conv_intbuf,
+                       noconv_fsize, noconv_fsize - i, nread);
+            break;
+        }
+
+        if (std::memcmp (noconv_intbuf, conv_intbuf,
+                         nread * sizeof *noconv_intbuf)) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "%d. data mismatch after conversion", i);
+            break;
+        }
+
+        if (!noconv_in.close ())
+            rw_warn (false, __FILE__, __LINE__,
+                     "basic_filebuf<%s>(" __FILE__ ").close() failed",
+                     tname);
+
+        if (!conv_in.close ())
+            rw_warn (false, __FILE__, __LINE__,
+                     "basic_filebuf<%s>(\"%s\").close() failed",
+                     tname, tmpfname);
+
+        noconv_in.open (__FILE__, std::ios::binary | std::ios::in);
+        conv_in.open (tmpfname, std::ios::binary | std::ios::in);
+
+        const pos_type noconv_pos_new = noconv_in.pubseekpos (noconv_pos);
+
+        if (noconv_pos_new != noconv_pos) {
+
+            std::mbstate_t noconv_state     = noconv_pos.state ();
+            std::mbstate_t noconv_state_new = noconv_pos_new.state ();
+
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::pubseekpos ({ %d, %d }, "
+                       "ios_base::cur) == { %d, %d }, got { %d, %d }",
+                       tname,
+                       off_type (noconv_pos), *(char*)&noconv_state,
+                       off_type (noconv_pos), *(char*)&noconv_state,
+                       off_type (noconv_pos_new),
+                       *(char*)&noconv_state_new);
+            break;
+        }
+
+        const pos_type conv_pos_new = conv_in.pubseekpos (conv_pos);
+
+        if (conv_pos_new != conv_pos) {
+            std::mbstate_t conv_state     = noconv_pos.state ();
+            std::mbstate_t conv_state_new = noconv_pos_new.state ();
+
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::pubseekpos ({ %d, %d }, "
+                       "ios_base::cur) == { %d, %d }, got { %d, %d }",
+                       tname,
+                       off_type (conv_pos), *(char*)&conv_state,
+                       off_type (conv_pos), *(char*)&conv_state,
+                       off_type (conv_pos_new),
+                       *(char*)&conv_state_new);
+            break;
+        }
+
+        std::memset (noconv_intbuf, 0, noconv_fsize * sizeof *noconv_intbuf);
+        nread = noconv_in.sgetn (noconv_intbuf, noconv_fsize);
+
+        if (noconv_fsize - i != nread) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                       "got %d (no code conversion)",
+                       tname, noconv_intbuf,
+                       noconv_fsize, noconv_fsize - i, nread);
+            break;
+        }
+
+        if (std::memcmp (noconv_intbuf, conv_intbuf,
+                         nread * sizeof *noconv_intbuf)) {
+            rw_assert (false, __FILE__, __LINE__,
+                      "data mismatch (no code conversion)");
+            break;
+        }
+
+        std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
+        nread = conv_in.sgetn (conv_intbuf, noconv_fsize);
+
+        if (noconv_fsize - i != nread) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
+                       "got %d (with code conversion)",
+                       tname, conv_intbuf,
+                       noconv_fsize, noconv_fsize - i, nread);
+            break;
+        }
+
+        if (std::memcmp (noconv_intbuf, conv_intbuf,
+                         nread * sizeof *noconv_intbuf)) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "code conversion mismatch");
+            break;
+        }
+    }
+
+    delete[] noconv_intbuf;
+    delete[] noconv_extbuf;
+    delete[] conv_intbuf;
+
+    if (!noconv_in.close ())
+        rw_warn (false, __FILE__, __LINE__,
+                 "basic_filebuf<%s>(" __FILE__ ").close() failed",
+                 tname);
+
+    if (!conv_in.close ())
+        rw_warn (false, __FILE__, __LINE__,
+                 "basic_filebuf<%s>(\"%s\").close() failed",
+                 tname, tmpfname);
+
+    REMOVE_FILE (tmpfname);

[... 180 lines stripped ...]


Mime
View raw message