VirtualBox

Changeset 95886 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jul 28, 2022 1:39:36 AM (2 years ago)
Author:
vboxsync
Message:

IPRT/stream.cpp: Added an alternative code configuration for the no-CRT mode based on RTFile. Mostly untested. bugref:10261

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/stream.cpp

    r95587 r95886  
    2727
    2828
     29/*********************************************************************************************************************************
     30*   Defined Constants And Macros                                                                                                 *
     31*********************************************************************************************************************************/
     32/** @def RTSTREAM_STANDALONE
     33 * Standalone streams w/o depending on stdio.h, using our RTFile API for
     34 * file/whatever access. */
     35#if defined(IPRT_NO_CRT) || defined(DOXYGEN_RUNNING)
     36# define RTSTREAM_STANDALONE
     37#endif
     38
    2939#if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */
    30 #define HAVE_FWRITE_UNLOCKED
    31 #endif
     40# ifndef RTSTREAM_STANDALONE
     41#  define HAVE_FWRITE_UNLOCKED
     42# endif
     43#endif
     44
     45/** @def RTSTREAM_WITH_TEXT_MODE
     46 * Indicates whether we need to support the 'text' mode files and convert
     47 * CRLF to LF while reading and writing. */
     48#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
     49# define RTSTREAM_WITH_TEXT_MODE
     50#endif
     51
    3252
    3353
     
    4464#include <iprt/string.h>
    4565#include <iprt/assert.h>
    46 #include <iprt/alloc.h>
    4766#include <iprt/ctype.h>
    4867#include <iprt/err.h>
     68#ifdef RTSTREAM_STANDALONE
     69# include <iprt/file.h>
     70#endif
     71#include <iprt/mem.h>
    4972#include <iprt/param.h>
    5073#include <iprt/string.h>
     
    5376#include "internal/magics.h"
    5477
    55 #include <stdio.h>
    56 #include <errno.h>
    57 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    58 # include <io.h>
    59 # include <fcntl.h>
     78#ifndef RTSTREAM_STANDALONE
     79# include <stdio.h>
     80# include <errno.h>
     81# if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
     82#  include <io.h>
     83#  include <fcntl.h>
     84# endif
    6085#endif
    6186#ifdef RT_OS_WINDOWS
    6287# include <iprt/utf16.h>
    6388# include <iprt/win/windows.h>
    64 #else
     89#elif !defined(RTSTREAM_STANDALONE)
    6590# include <termios.h>
    6691# include <unistd.h>
     
    6893#endif
    6994
    70 #ifdef RT_OS_OS2
     95#if defined(RT_OS_OS2) && !defined(RTSTREAM_STANDALONE)
    7196# define _O_TEXT   O_TEXT
    7297# define _O_BINARY O_BINARY
     
    77102*   Structures and Typedefs                                                                                                      *
    78103*********************************************************************************************************************************/
     104#ifdef RTSTREAM_STANDALONE
     105/** The buffer direction. */
     106typedef enum RTSTREAMBUFDIR
     107{
     108    RTSTREAMBUFDIR_NONE = 0,
     109    RTSTREAMBUFDIR_READ,
     110    RTSTREAMBUFDIR_WRITE
     111} RTSTREAMBUFDIR;
     112
     113/** The buffer style. */
     114typedef enum RTSTREAMBUFSTYLE
     115{
     116    RTSTREAMBUFSTYLE_UNBUFFERED = 0,
     117    RTSTREAMBUFSTYLE_LINE,
     118    RTSTREAMBUFSTYLE_FULL
     119} RTSTREAMBUFSTYLE;
     120
     121#endif
     122
    79123/**
    80124 * File stream.
     
    86130    /** File stream error. */
    87131    int32_t volatile    i32Error;
     132#ifndef RTSTREAM_STANDALONE
    88133    /** Pointer to the LIBC file stream. */
    89134    FILE               *pFile;
     135#else
     136    /** Indicates which standard handle this is supposed to be.
     137     * Set to RTHANDLESTD_INVALID if not one of the tree standard streams. */
     138    RTHANDLESTD         enmStdHandle;
     139    /** The IPRT handle backing this stream.
     140     * This is initialized lazily using enmStdHandle for the three standard
     141     * streams. */
     142    RTFILE              hFile;
     143    /** Buffer. */
     144    char               *pchBuf;
     145    /** Buffer allocation size. */
     146    size_t              cbBufAlloc;
     147    /** Offset of the first valid byte in the buffer. */
     148    size_t              offBufFirst;
     149    /** Offset of the end of valid bytes in the buffer (exclusive). */
     150    size_t              offBufEnd;
     151    /** The stream buffer direction.   */
     152    RTSTREAMBUFDIR      enmBufDir;
     153    /** The buffering style (unbuffered, line, full). */
     154    RTSTREAMBUFSTYLE    enmBufStyle;
     155# ifdef RTSTREAM_WITH_TEXT_MODE
     156    /** Indicates that we've got a CR ('\\r') beyond the end of official buffer
     157     * and need to check if there is a LF following it.  This member is ignored
     158     * in binary mode. */
     159    bool                fPendingCr;
     160# endif
     161#endif
    90162    /** Stream is using the current process code set. */
    91163    bool                fCurrentCodeSet;
     
    94166    /** Whether to recheck the stream mode before writing. */
    95167    bool                fRecheckMode;
    96 #ifndef HAVE_FWRITE_UNLOCKED
     168#if !defined(HAVE_FWRITE_UNLOCKED) || defined(RTSTREAM_STANDALONE)
    97169    /** Critical section for serializing access to the stream. */
    98170    PRTCRITSECT         pCritSect;
     
    123195static RTSTREAM    g_StdIn =
    124196{
    125     RTSTREAM_MAGIC,
    126     0,
    127     stdin,
    128     /*.fCurrentCodeSet = */ true,
    129     /*.fBinary = */ false,
    130     /*.fRecheckMode = */ true
     197    /* .u32Magic = */           RTSTREAM_MAGIC,
     198    /* .i32Error = */           0,
     199#ifndef RTSTREAM_STANDALONE
     200    /* .pFile = */              stdin,
     201#else
     202    /* .enmStdHandle = */       RTHANDLESTD_INPUT,
     203    /* .hFile = */              NIL_RTFILE,
     204    /* .pchBuf = */             NULL,
     205    /* .cbBufAlloc = */         0,
     206    /* .offBufFirst = */        0,
     207    /* .offBufEnd = */          0,
     208    /* .enmBufDir = */          RTSTREAMBUFDIR_NONE,
     209    /* .enmBufStyle = */        RTSTREAMBUFSTYLE_UNBUFFERED,
     210# ifdef RTSTREAM_WITH_TEXT_MODE
     211    /* .fPendingCr = */         false,
     212# endif
     213#endif
     214    /* .fCurrentCodeSet = */    true,
     215    /* .fBinary = */            false,
     216    /* .fRecheckMode = */       true,
    131217#ifndef HAVE_FWRITE_UNLOCKED
    132     , NULL
     218    /* .pCritSect = */          NULL
    133219#endif
    134220};
     
    137223static RTSTREAM    g_StdErr =
    138224{
    139     RTSTREAM_MAGIC,
    140     0,
    141     stderr,
    142     /*.fCurrentCodeSet = */ true,
    143     /*.fBinary = */ false,
    144     /*.fRecheckMode = */ true
     225    /* .u32Magic = */           RTSTREAM_MAGIC,
     226    /* .i32Error = */           0,
     227#ifndef RTSTREAM_STANDALONE
     228    /* .pFile = */              stderr,
     229#else
     230    /* .enmStdHandle = */       RTHANDLESTD_ERROR,
     231    /* .hFile = */              NIL_RTFILE,
     232    /* .pchBuf = */             NULL,
     233    /* .cbBufAlloc = */         0,
     234    /* .offBufFirst = */        0,
     235    /* .offBufEnd = */          0,
     236    /* .enmBufDir = */          RTSTREAMBUFDIR_NONE,
     237    /* .enmBufStyle = */        RTSTREAMBUFSTYLE_UNBUFFERED,
     238# ifdef RTSTREAM_WITH_TEXT_MODE
     239    /* .fPendingCr = */         false,
     240# endif
     241#endif
     242    /* .fCurrentCodeSet = */    true,
     243    /* .fBinary = */            false,
     244    /* .fRecheckMode = */       true,
    145245#ifndef HAVE_FWRITE_UNLOCKED
    146     , NULL
     246    /* .pCritSect = */          NULL
    147247#endif
    148248};
     
    151251static RTSTREAM    g_StdOut =
    152252{
    153     RTSTREAM_MAGIC,
    154     0,
    155     stdout,
    156     /*.fCurrentCodeSet = */ true,
    157     /*.fBinary = */ false,
    158     /*.fRecheckMode = */ true
     253    /* .u32Magic = */           RTSTREAM_MAGIC,
     254    /* .i32Error = */           0,
     255#ifndef RTSTREAM_STANDALONE
     256    /* .pFile = */              stderr,
     257#else
     258    /* .enmStdHandle = */       RTHANDLESTD_OUTPUT,
     259    /* .hFile = */              NIL_RTFILE,
     260    /* .pchBuf = */             NULL,
     261    /* .cbBufAlloc = */         0,
     262    /* .offBufFirst = */        0,
     263    /* .offBufEnd = */          0,
     264    /* .enmBufDir = */          RTSTREAMBUFDIR_NONE,
     265    /* .enmBufStyle = */        RTSTREAMBUFSTYLE_LINE,
     266# ifdef RTSTREAM_WITH_TEXT_MODE
     267    /* .fPendingCr = */         false,
     268# endif
     269#endif
     270    /* .fCurrentCodeSet = */    true,
     271    /* .fBinary = */            false,
     272    /* .fRecheckMode = */       true,
    159273#ifndef HAVE_FWRITE_UNLOCKED
    160     , NULL
     274    /* .pCritSect = */          NULL
    161275#endif
    162276};
     
    254368 * @param   pszFilename     Path to the file to open.
    255369 * @param   pszMode         The open mode. See fopen() standard.
    256  *                          Format: <a|r|w>[+][b|t]
     370 *                          Format: <a|r|w>[+][b]
    257371 * @param   ppStream        Where to store the opened stream.
    258372 */
     
    260374{
    261375    /*
    262      * Validate input.
     376     * Validate input and look for things we care for in the pszMode string.
    263377     */
    264     if (!pszMode || !*pszMode)
    265     {
    266         AssertMsgFailed(("No pszMode!\n"));
    267         return VERR_INVALID_PARAMETER;
    268     }
    269     if (!pszFilename)
    270     {
    271         AssertMsgFailed(("No pszFilename!\n"));
    272         return VERR_INVALID_PARAMETER;
    273     }
    274     bool fOk = true;
    275     bool fBinary = false;
     378    AssertReturn(pszMode && *pszMode, VERR_INVALID_FLAGS);
     379    AssertReturn(pszFilename, VERR_INVALID_PARAMETER);
     380
     381    bool        fOk     = true;
     382    bool        fBinary = false;
     383#ifdef RTSTREAM_STANDALONE
     384    uint64_t    fOpen   = RTFILE_O_DENY_NONE;
     385#endif
    276386    switch (*pszMode)
    277387    {
     
    281391            switch (pszMode[1])
    282392            {
     393                case 'b':
     394                    fBinary = true;
     395                    RT_FALL_THRU();
    283396                case '\0':
     397#ifdef RTSTREAM_STANDALONE
     398                    fOpen |= *pszMode == 'a' ? RTFILE_O_OPEN_CREATE    | RTFILE_O_WRITE | RTFILE_O_APPEND
     399                           : *pszMode == 'w' ? RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE
     400                           :                   RTFILE_O_OPEN           | RTFILE_O_READ;
     401#endif
    284402                    break;
    285403
    286404                case '+':
     405#ifdef RTSTREAM_STANDALONE
     406                    fOpen |= *pszMode == 'a' ? RTFILE_O_OPEN_CREATE    | RTFILE_O_READ | RTFILE_O_WRITE | RTFILE_O_APPEND
     407                           : *pszMode == 'w' ? RTFILE_O_CREATE_REPLACE | RTFILE_O_READ | RTFILE_O_WRITE
     408                           :                   RTFILE_O_OPEN           | RTFILE_O_READ | RTFILE_O_WRITE;
     409#endif
    287410                    switch (pszMode[2])
    288411                    {
     
    290413                            break;
    291414
    292                         //case 't':
    293                         //    break;
    294 
    295415                        case 'b':
    296416                            fBinary = true;
     
    301421                            break;
    302422                    }
    303                     break;
    304 
    305                 //case 't':
    306                 //    break;
    307 
    308                 case 'b':
    309                     fBinary = true;
    310423                    break;
    311424
     
    321434    if (!fOk)
    322435    {
    323         AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b|t]'\n", pszMode));
     436        AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b]'\n", pszMode));
    324437        return VINF_SUCCESS;
    325438    }
     
    328441     * Allocate the stream handle and try open it.
    329442     */
    330     PRTSTREAM pStream = (PRTSTREAM)RTMemAlloc(sizeof(*pStream));
     443    int rc = VERR_NO_MEMORY;
     444    PRTSTREAM pStream = (PRTSTREAM)RTMemAllocZ(sizeof(*pStream));
    331445    if (pStream)
    332446    {
    333         pStream->u32Magic = RTSTREAM_MAGIC;
    334         pStream->i32Error = VINF_SUCCESS;
    335         pStream->fCurrentCodeSet = false;
    336         pStream->fBinary  = fBinary;
    337         pStream->fRecheckMode = false;
     447        pStream->u32Magic           = RTSTREAM_MAGIC;
     448#ifdef RTSTREAM_STANDALONE
     449        pStream->enmStdHandle       = RTHANDLESTD_INVALID;
     450        pStream->hFile              = NIL_RTFILE;
     451        pStream->pchBuf             = NULL;
     452        pStream->cbBufAlloc         = 0;
     453        pStream->offBufFirst        = 0;
     454        pStream->offBufEnd          = 0;
     455        pStream->enmBufDir          = RTSTREAMBUFDIR_NONE;
     456        pStream->enmBufStyle        = RTSTREAMBUFSTYLE_FULL;
     457# ifdef RTSTREAM_WITH_TEXT_MODE
     458        pStream->fPendingCr         = false,
     459# endif
     460#endif
     461        pStream->i32Error           = VINF_SUCCESS;
     462        pStream->fCurrentCodeSet    = false;
     463        pStream->fBinary            = fBinary;
     464        pStream->fRecheckMode       = false;
    338465#ifndef HAVE_FWRITE_UNLOCKED
    339         pStream->pCritSect = NULL;
    340 #endif /* HAVE_FWRITE_UNLOCKED */
     466        pStream->pCritSect          = NULL;
     467#endif
     468#ifdef RTSTREAM_STANDALONE
     469        rc = RTFileOpen(&pStream->hFile, pszFilename, fOpen);
     470#else
    341471        pStream->pFile = fopen(pszFilename, pszMode);
     472        rc = pStream->pFile ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
    342473        if (pStream->pFile)
     474#endif
     475        if (RT_SUCCESS(rc))
    343476        {
    344477            *ppStream = pStream;
     
    346479        }
    347480        RTMemFree(pStream);
    348         return RTErrConvertFromErrno(errno);
    349     }
    350     return VERR_NO_MEMORY;
     481    }
     482    return rc;
    351483}
    352484
     
    357489 * @returns iprt status code.
    358490 * @param   pszMode         The open mode. See fopen() standard.
    359  *                          Format: <a|r|w>[+][b|t]
     491 *                          Format: <a|r|w>[+][b]
    360492 * @param   ppStream        Where to store the opened stream.
    361493 * @param   pszFilenameFmt  Filename path format string.
     
    383515 * @returns iprt status code.
    384516 * @param   pszMode         The open mode. See fopen() standard.
    385  *                          Format: <a|r|w>[+][b|t]
     517 *                          Format: <a|r|w>[+][b]
    386518 * @param   ppStream        Where to store the opened stream.
    387519 * @param   pszFilenameFmt  Filename path format string.
     
    406538RTR3DECL(int) RTStrmClose(PRTSTREAM pStream)
    407539{
     540    /*
     541     * Validate input.
     542     */
    408543    if (!pStream)
    409544        return VINF_SUCCESS;
    410     AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
    411 
    412     if (!fclose(pStream->pFile))
    413     {
    414         pStream->u32Magic = 0xdeaddead;
    415         pStream->pFile = NULL;
     545    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     546    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
     547
     548    /* We don't implement closing any of the standard handles at present. */
     549    AssertReturn(pStream != &g_StdIn, VERR_NOT_SUPPORTED);
     550    AssertReturn(pStream != &g_StdOut, VERR_NOT_SUPPORTED);
     551    AssertReturn(pStream != &g_StdErr, VERR_NOT_SUPPORTED);
     552
     553    /*
     554     * Invalidate the stream and destroy the critical section first.
     555     */
     556    pStream->u32Magic = 0xdeaddead;
    416557#ifndef HAVE_FWRITE_UNLOCKED
    417         if (pStream->pCritSect)
    418         {
    419             RTCritSectEnter(pStream->pCritSect);
    420             RTCritSectLeave(pStream->pCritSect);
    421             RTCritSectDelete(pStream->pCritSect);
    422             RTMemFree(pStream->pCritSect);
    423             pStream->pCritSect = NULL;
    424         }
    425 #endif
    426         RTMemFree(pStream);
    427         return VINF_SUCCESS;
    428     }
    429 
    430     return RTErrConvertFromErrno(errno);
     558    if (pStream->pCritSect)
     559    {
     560        RTCritSectEnter(pStream->pCritSect);
     561        RTCritSectLeave(pStream->pCritSect);
     562        RTCritSectDelete(pStream->pCritSect);
     563        RTMemFree(pStream->pCritSect);
     564        pStream->pCritSect = NULL;
     565    }
     566#endif
     567
     568    /*
     569     * Flush and close the underlying file.
     570     */
     571#ifdef RTSTREAM_STANDALONE
     572    int const rc1 = RTStrmFlush(pStream);
     573    AssertRC(rc1);
     574    int const rc2 = RTFileClose(pStream->hFile);
     575    AssertRC(rc2);
     576    int const rc = RT_SUCCESS(rc1) ? rc2 : rc1;
     577#else
     578    int const rc = !fclose(pStream->pFile) ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
     579#endif
     580
     581    /*
     582     * Destroy the stream.
     583     */
     584#ifdef RTSTREAM_STANDALONE
     585    pStream->hFile          = NIL_RTFILE;
     586    RTMemFree(pStream->pchBuf);
     587    pStream->pchBuf         = NULL;
     588    pStream->cbBufAlloc     = 0;
     589    pStream->offBufFirst    = 0;
     590    pStream->offBufEnd      = 0;
     591#else
     592    pStream->pFile          = NULL;
     593#endif
     594    RTMemFree(pStream);
     595    return rc;
    431596}
    432597
     
    440605RTR3DECL(int) RTStrmError(PRTSTREAM pStream)
    441606{
    442     AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
     607    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     608    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
    443609    return pStream->i32Error;
    444610}
     
    456622RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream)
    457623{
    458     AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
    459 
     624    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     625    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
     626
     627#ifndef RTSTREAM_STANDALONE
    460628    clearerr(pStream->pFile);
     629#endif
    461630    ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
    462631    return VINF_SUCCESS;
     
    487656}
    488657
    489 
    490 RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars)
    491 {
    492     int rc = VINF_SUCCESS;
    493 
    494     AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
    495     AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
    496     AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER);
    497 
    498     int fh = fileno(pStream->pFile);
    499     if (isatty(fh))
    500     {
    501 #ifdef RT_OS_WINDOWS
    502         DWORD dwMode;
    503         HANDLE hCon = (HANDLE)_get_osfhandle(fh);
    504         if (GetConsoleMode(hCon, &dwMode))
    505             *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT);
    506         else
    507             rc = RTErrConvertFromWin32(GetLastError());
     658#ifdef RTSTREAM_STANDALONE
     659
     660/**
     661 * Deals with NIL_RTFILE in rtStrmGetFile.
     662 */
     663DECL_NO_INLINE(static, RTFILE) rtStrmGetFileNil(PRTSTREAM pStream)
     664{
     665# ifdef RT_OS_WINDOWS
     666    DWORD dwStdHandle;
     667    switch (pStream->enmStdHandle)
     668    {
     669        case RTHANDLESTD_INPUT:     dwStdHandle = STD_INPUT_HANDLE; break;
     670        case RTHANDLESTD_OUTPUT:    dwStdHandle = STD_OUTPUT_HANDLE; break;
     671        case RTHANDLESTD_ERROR:     dwStdHandle = STD_ERROR_HANDLE; break;
     672        default:                    return NIL_RTFILE;
     673    }
     674    HANDLE hHandle = GetStdHandle(dwStdHandle);
     675    if (hHandle != INVALID_HANDLE_VALUE && hHandle != NULL)
     676    {
     677        int rc = RTFileFromNative(&pStream->hFile, (uintptr_t)hHandle);
     678        if (RT_SUCCESS(rc))
     679        {
     680            /* Switch to full buffering if not a console handle. */
     681            DWORD dwMode;
     682            if (!GetConsoleMode(hHandle, &dwMode))
     683                pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL;
     684
     685            return pStream->hFile;
     686        }
     687    }
     688
     689# else
     690    uintptr_t uNative;
     691    switch (pStream->enmStdHandle)
     692    {
     693        case RTHANDLESTD_INPUT:     uNative = RTFILE_NATIVE_STDIN; break;
     694        case RTHANDLESTD_OUTPUT:    uNative = RTFILE_NATIVE_STDOUT; break;
     695        case RTHANDLESTD_ERROR:     uNative = RTFILE_NATIVE_STDERR; break;
     696        default:                    return NIL_RTFILE;
     697    }
     698    int rc = RTFileFromNative(&pStream->hFile, uNative);
     699    if (RT_SUCCESS(rc))
     700    {
     701        /* Switch to full buffering if not a console handle. */
     702        if (!isatty((int)uNative))
     703            pStream->enmBufStyle = RTSTREAMBUFDIR_FULL;
     704
     705        return pStream->hFile;
     706    }
     707
     708# endif
     709    return NIL_RTFILE;
     710}
     711
     712/**
     713 * For lazily resolving handles for the standard streams.
     714 */
     715DECLINLINE(RTFILE) rtStrmGetFile(PRTSTREAM pStream)
     716{
     717    RTFILE hFile = pStream->hFile;
     718    if (hFile != NIL_RTFILE)
     719        return hFile;
     720    return rtStrmGetFileNil(pStream);
     721}
     722
     723#endif /* RTSTREAM_STANDALONE */
     724
     725
     726/**
     727 * Wrapper around isatty, assumes caller takes care of stream locking/whatever
     728 * is needed.
     729 */
     730DECLINLINE(bool) rtStrmIsTerminal(PRTSTREAM pStream)
     731{
     732#ifdef RTSTREAM_STANDALONE
     733    RTFILE hFile = rtStrmGetFile(pStream);
     734    if (hFile != NIL_RTFILE)
     735    {
     736        HANDLE hNative = (HANDLE)RTFileToNative(hFile);
     737        DWORD dwType = GetFileType(hNative);
     738        if (dwType == FILE_TYPE_CHAR)
     739        {
     740            DWORD dwMode;
     741            if (GetConsoleMode(hNative, &dwMode))
     742                return true;
     743        }
     744    }
     745    return false;
     746
    508747#else
    509         struct termios Termios;
    510 
    511         int rcPosix = tcgetattr(fh, &Termios);
    512         if (!rcPosix)
    513             *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);
    514         else
    515             rc = RTErrConvertFromErrno(errno);
    516 #endif
    517     }
    518     else
    519         rc = VERR_INVALID_HANDLE;
    520 
    521     return rc;
    522 }
    523 
    524 
    525 RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)
    526 {
    527     int rc = VINF_SUCCESS;
    528 
    529     AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
    530     AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
    531 
    532     int fh = fileno(pStream->pFile);
    533     if (isatty(fh))
    534     {
    535 #ifdef RT_OS_WINDOWS
    536         DWORD dwMode;
    537         HANDLE hCon = (HANDLE)_get_osfhandle(fh);
    538         if (GetConsoleMode(hCon, &dwMode))
    539         {
    540             if (fEchoChars)
    541                 dwMode |= ENABLE_ECHO_INPUT;
    542             else
    543                 dwMode &= ~ENABLE_ECHO_INPUT;
    544             if (!SetConsoleMode(hCon, dwMode))
    545                 rc = RTErrConvertFromWin32(GetLastError());
    546         }
    547         else
    548             rc = RTErrConvertFromWin32(GetLastError());
    549 #else
    550         struct termios Termios;
    551 
    552         int rcPosix = tcgetattr(fh, &Termios);
    553         if (!rcPosix)
    554         {
    555             if (fEchoChars)
    556                 Termios.c_lflag |= ECHO;
    557             else
    558                 Termios.c_lflag &= ~ECHO;
    559 
    560             rcPosix = tcsetattr(fh, TCSAFLUSH, &Termios);
    561             if (rcPosix != 0)
    562                 rc = RTErrConvertFromErrno(errno);
    563         }
    564         else
    565             rc = RTErrConvertFromErrno(errno);
    566 #endif
    567     }
    568     else
    569         rc = VERR_INVALID_HANDLE;
    570 
    571     return rc;
    572 }
    573 
    574 
    575 RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)
    576 {
    577     AssertPtrReturn(pStream, false);
    578     AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);
    579 
    580748    if (pStream->pFile)
    581749    {
    582750        int fh = fileno(pStream->pFile);
    583         if (isatty(fh))
    584         {
    585 #ifdef RT_OS_WINDOWS
     751        if (isatty(fh) != 0)
     752        {
     753# ifdef RT_OS_WINDOWS
    586754            DWORD  dwMode;
    587755            HANDLE hCon = (HANDLE)_get_osfhandle(fh);
    588756            if (GetConsoleMode(hCon, &dwMode))
    589757                return true;
     758# else
     759            return true;
     760# endif
     761        }
     762    }
     763    return false;
     764#endif
     765}
     766
     767
     768static int rtStrmInputGetEchoCharsNative(uintptr_t hNative, bool *pfEchoChars)
     769{
     770#ifdef RT_OS_WINDOWS
     771    DWORD dwMode;
     772    if (GetConsoleMode((HANDLE)hNative, &dwMode))
     773        *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT);
     774    else
     775    {
     776        DWORD dwErr = GetLastError();
     777        if (dwErr == ERROR_INVALID_HANDLE)
     778            return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE;
     779        return RTErrConvertFromWin32(dwErr);
     780    }
    590781#else
    591             return true;
    592 #endif
    593         }
    594     }
    595     return false;
     782    struct termios Termios;
     783    int rcPosix = tcgetattr((int)fh, &Termios);
     784    if (!rcPosix)
     785        *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);
     786    else
     787        return errno == ENOTTY ? VERR_INVALID_FUNCTION :  RTErrConvertFromErrno(errno);
     788#endif
     789    return VINF_SUCCESS;
     790}
     791
     792
     793
     794RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars)
     795{
     796    AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
     797    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
     798    AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER);
     799
     800#ifdef RTSTREAM_STANDALONE
     801    return rtStrmInputGetEchoCharsNative(RTFileToNative(pStream->hFile), pfEchoChars);
     802#else
     803    int rc;
     804    int fh = fileno(pStream->pFile);
     805    if (isatty(fh))
     806    {
     807# ifdef RT_OS_WINDOWS
     808        rc = rtStrmInputGetEchoCharsNative(_get_osfhandle(fh), pfEchoChars);
     809# else
     810        rc = rtStrmInputGetEchoCharsNative(fh, pfEchoChars);
     811# endif
     812    }
     813    else
     814        rc = VERR_INVALID_FUNCTION;
     815    return rc;
     816#endif
     817}
     818
     819
     820static int rtStrmInputSetEchoCharsNative(uintptr_t hNative, bool fEchoChars)
     821{
     822    int rc;
     823#ifdef RT_OS_WINDOWS
     824    DWORD dwMode;
     825    if (GetConsoleMode((HANDLE)hNative, &dwMode))
     826    {
     827        if (fEchoChars)
     828            dwMode |= ENABLE_ECHO_INPUT;
     829        else
     830            dwMode &= ~ENABLE_ECHO_INPUT;
     831        if (SetConsoleMode((HANDLE)hNative, dwMode))
     832            rc = VINF_SUCCESS;
     833        else
     834            rc = RTErrConvertFromWin32(GetLastError());
     835    }
     836    else
     837    {
     838        DWORD dwErr = GetLastError();
     839        if (dwErr == ERROR_INVALID_HANDLE)
     840            return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE;
     841        return RTErrConvertFromWin32(dwErr);
     842    }
     843#else
     844    struct termios Termios;
     845    int rcPosix = tcgetattr(fh, &Termios);
     846    if (!rcPosix)
     847    {
     848        if (fEchoChars)
     849            Termios.c_lflag |= ECHO;
     850        else
     851            Termios.c_lflag &= ~ECHO;
     852
     853        rcPosix = tcsetattr(fh, TCSAFLUSH, &Termios);
     854        if (rcPosix == 0)
     855            rc = VINF_SUCCESS;
     856        else
     857            rc = RTErrConvertFromErrno(errno);
     858    }
     859    else
     860        rc = errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno);
     861#endif
     862    return rc;
     863}
     864
     865
     866RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)
     867{
     868    AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
     869    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
     870
     871#ifdef RTSTREAM_STANDALONE
     872    return rtStrmInputSetEchoCharsNative(RTFileToNative(pStream->hFile), fEchoChars);
     873#else
     874    int rc;
     875    int fh = fileno(pStream->pFile);
     876    if (isatty(fh))
     877    {
     878# ifdef RT_OS_WINDOWS
     879        rc = rtStrmInputSetEchoCharsNative(_get_osfhandle(fh), fEchoChars);
     880# else
     881        rc = rtStrmInputSetEchoCharsNative(fh, fEchoChars);
     882# endif
     883    }
     884    else
     885        rc = VERR_INVALID_FUNCTION;
     886    return rc;
     887#endif
     888}
     889
     890
     891RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)
     892{
     893    AssertPtrReturn(pStream, false);
     894    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);
     895
     896    return rtStrmIsTerminal(pStream);
    596897}
    597898
     
    605906    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
    606907
    607     if (pStream->pFile)
    608     {
    609         int fh = fileno(pStream->pFile);
    610         if (isatty(fh))
    611         {
     908    if (rtStrmIsTerminal(pStream))
     909    {
    612910#ifdef RT_OS_WINDOWS
    613             CONSOLE_SCREEN_BUFFER_INFO Info;
    614             HANDLE hCon = (HANDLE)_get_osfhandle(fh);
    615             RT_ZERO(Info);
    616             if (GetConsoleScreenBufferInfo(hCon, &Info))
     911# ifdef RTSTREAM_STANDALONE
     912        HANDLE hCon = (HANDLE)RTFileToNative(pStream->hFile);
     913# else
     914        HANDLE hCon = (HANDLE)_get_osfhandle(fileno(pStream->pFile));
     915# endif
     916        CONSOLE_SCREEN_BUFFER_INFO Info;
     917        RT_ZERO(Info);
     918        if (GetConsoleScreenBufferInfo(hCon, &Info))
     919        {
     920            *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;
     921            return VINF_SUCCESS;
     922        }
     923        return RTErrConvertFromWin32(GetLastError());
     924
     925#elif defined(RT_OS_OS2) && !defined(TIOCGWINSZ) /* only OS/2 should currently miss this */
     926        return VINF_SUCCESS; /* just pretend for now. */
     927
     928#else
     929        struct winsize Info;
     930        RT_ZERO(Info);
     931        int rc = ioctl(fileno(pStream->pFile), TIOCGWINSZ, &Info);
     932        if (rc >= 0)
     933        {
     934            *pcchWidth = Info.ws_col ? Info.ws_col : 80;
     935            return VINF_SUCCESS;
     936        }
     937        return RTErrConvertFromErrno(errno);
     938#endif
     939    }
     940    return VERR_INVALID_FUNCTION;
     941}
     942
     943
     944#ifdef RTSTREAM_STANDALONE
     945
     946DECLINLINE(void) rtStrmBufInvalidate(PRTSTREAM pStream)
     947{
     948    pStream->enmBufDir   = RTSTREAMBUFDIR_NONE;
     949    pStream->offBufEnd   = 0;
     950    pStream->offBufFirst = 0;
     951}
     952
     953
     954static int rtStrmBufFlushWrite(PRTSTREAM pStream, size_t cbToFlush)
     955{
     956    Assert(cbToFlush <= pStream->offBufEnd - pStream->offBufFirst);
     957
     958    /** @todo do nonblocking & incomplete writes?   */
     959    size_t offBufFirst = pStream->offBufFirst;
     960    int rc = RTFileWrite(rtStrmGetFile(pStream), &pStream->pchBuf[offBufFirst], cbToFlush, NULL);
     961    if (RT_SUCCESS(rc))
     962    {
     963        offBufFirst += cbToFlush;
     964        if (offBufFirst >= pStream->offBufEnd)
     965            pStream->offBufEnd = 0;
     966        else
     967        {
     968            /* Shift up the remaining content so the next write can take full
     969               advantage of the buffer size. */
     970            size_t cbLeft = pStream->offBufEnd - offBufFirst;
     971            memmove(pStream->pchBuf, &pStream->pchBuf[offBufFirst], cbLeft);
     972            pStream->offBufEnd = cbLeft;
     973        }
     974        pStream->offBufFirst = 0;
     975        return VINF_SUCCESS;
     976    }
     977    return rc;
     978}
     979
     980
     981static int rtStrmBufFlushWriteMaybe(PRTSTREAM pStream, bool fInvalidate)
     982{
     983    if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE)
     984    {
     985        size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
     986        if (cbInBuffer > 0)
     987        {
     988            int rc = rtStrmBufFlushWrite(pStream, cbInBuffer);
     989            if (fInvalidate)
     990                pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
     991            return rc;
     992        }
     993    }
     994    if (fInvalidate)
     995        rtStrmBufInvalidate(pStream);
     996    return VINF_SUCCESS;
     997}
     998
     999
     1000/**
     1001 * Worker for rtStrmBufCheckErrorAndSwitchToReadMode and
     1002 * rtStrmBufCheckErrorAndSwitchToWriteMode that allocates a buffer.
     1003 *
     1004 * Only updates cbBufAlloc and pchBuf, callers deals with error fallout.
     1005 */
     1006static int rtStrmBufAlloc(PRTSTREAM pStream)
     1007{
     1008    size_t cbBuf = pStream->enmBufStyle == RTSTREAMBUFSTYLE_FULL ? _64K : _16K;
     1009    do
     1010    {
     1011        pStream->pchBuf = (char *)RTMemAllocZ(cbBuf);
     1012        if (RT_LIKELY(pStream->pchBuf))
     1013        {
     1014            pStream->cbBufAlloc = cbBuf;
     1015            return VINF_SUCCESS;
     1016        }
     1017        cbBuf /= 2;
     1018    } while (cbBuf >= 256);
     1019    return VERR_NO_MEMORY;
     1020}
     1021
     1022
     1023/**
     1024 * Checks the stream error status, flushed any pending writes, ensures there is
     1025 * a buffer allocated and switches the stream to the read direction.
     1026 *
     1027 * @returns IPRT status code (same as i32Error).
     1028 * @param   pStream             The stream.
     1029 */
     1030static int rtStrmBufCheckErrorAndSwitchToReadMode(PRTSTREAM pStream)
     1031{
     1032    int rc = pStream->i32Error;
     1033    if (RT_SUCCESS(rc))
     1034    {
     1035        /*
     1036         * We're very likely already in read mode and can return without doing
     1037         * anything here.
     1038         */
     1039        if (pStream->enmBufDir == RTSTREAMBUFDIR_READ)
     1040            return VINF_SUCCESS;
     1041
     1042        /*
     1043         * Flush any pending writes before switching the buffer to read:
     1044         */
     1045        rc = rtStrmBufFlushWriteMaybe(pStream, false /*fInvalidate*/);
     1046        if (RT_SUCCESS(rc))
     1047        {
     1048            pStream->enmBufDir   = RTSTREAMBUFDIR_READ;
     1049            pStream->offBufEnd   = 0;
     1050            pStream->offBufFirst = 0;
     1051            pStream->fPendingCr  = false;
     1052
     1053            /*
     1054             * Read direction implies a buffer, so make sure we've got one and
     1055             * change to NONE direction if allocating one fails.
     1056             */
     1057            if (pStream->pchBuf)
    6171058            {
    618                 *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;
     1059                Assert(pStream->cbBufAlloc >= 256);
    6191060                return VINF_SUCCESS;
    6201061            }
    621             return RTErrConvertFromWin32(GetLastError());
    622 
    623 #elif defined(TIOCGWINSZ) || !defined(RT_OS_OS2) /* only OS/2 should currently miss this */
    624             struct winsize Info;
    625             RT_ZERO(Info);
    626             int rc = ioctl(fh, TIOCGWINSZ, &Info);
    627             if (rc >= 0)
     1062
     1063            rc = rtStrmBufAlloc(pStream);
     1064            if (RT_SUCCESS(rc))
     1065                return VINF_SUCCESS;
     1066
     1067            pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
     1068        }
     1069        ASMAtomicWriteS32(&pStream->i32Error, rc);
     1070    }
     1071    return rc;
     1072}
     1073
     1074
     1075/**
     1076 * Checks the stream error status, ensures there is a buffer allocated and
     1077 * switches the stream to the write direction.
     1078 *
     1079 * @returns IPRT status code (same as i32Error).
     1080 * @param   pStream             The stream.
     1081 */
     1082static int rtStrmBufCheckErrorAndSwitchToWriteMode(PRTSTREAM pStream)
     1083{
     1084    int rc = pStream->i32Error;
     1085    if (RT_SUCCESS(rc))
     1086    {
     1087        /*
     1088         * We're very likely already in write mode and can return without doing
     1089         * anything here.
     1090         */
     1091        if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE)
     1092            return VINF_SUCCESS;
     1093
     1094        /*
     1095         * A read buffer does not need any flushing, so we just have to make
     1096         * sure there is a buffer present before switching to the write direction.
     1097         */
     1098        pStream->enmBufDir   = RTSTREAMBUFDIR_WRITE;
     1099        pStream->offBufEnd   = 0;
     1100        pStream->offBufFirst = 0;
     1101        if (pStream->pchBuf)
     1102        {
     1103            Assert(pStream->cbBufAlloc >= 256);
     1104            return VINF_SUCCESS;
     1105        }
     1106
     1107        rc = rtStrmBufAlloc(pStream);
     1108        if (RT_SUCCESS(rc))
     1109            return VINF_SUCCESS;
     1110
     1111        pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
     1112        ASMAtomicWriteS32(&pStream->i32Error, rc);
     1113    }
     1114    return rc;
     1115}
     1116
     1117
     1118/**
     1119 * Reads more bytes into the buffer.
     1120 *
     1121 * @returns IPRT status code (same as i32Error).
     1122 * @param   pStream             The stream.
     1123 */
     1124static int rtStrmBufFill(PRTSTREAM pStream)
     1125{
     1126    /*
     1127     * Check preconditions
     1128     */
     1129    Assert(pStream->i32Error    == VINF_SUCCESS);
     1130    Assert(pStream->enmBufDir   == RTSTREAMBUFDIR_READ);
     1131    AssertPtr(pStream->pchBuf);
     1132    Assert(pStream->cbBufAlloc  >= 256);
     1133    Assert(pStream->offBufFirst <= pStream->cbBufAlloc);
     1134    Assert(pStream->offBufEnd   <= pStream->cbBufAlloc);
     1135    Assert(pStream->offBufFirst <= pStream->offBufEnd);
     1136
     1137    /*
     1138     * If there is data in the buffer, move it up to the start.
     1139     */
     1140    size_t cbInBuffer;
     1141    if (!pStream->offBufFirst)
     1142        cbInBuffer = pStream->offBufEnd;
     1143    else
     1144    {
     1145        cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
     1146        if (cbInBuffer)
     1147            memmove(pStream->pchBuf, &pStream->pchBuf[pStream->offBufFirst], cbInBuffer);
     1148        pStream->offBufFirst = 0;
     1149        pStream->offBufEnd   = cbInBuffer;
     1150    }
     1151
     1152    /*
     1153     * Add pending CR to the buffer.
     1154     */
     1155    size_t const offCrLfConvStart = cbInBuffer;
     1156    Assert(cbInBuffer + 2 <= pStream->cbBufAlloc);
     1157    if (!pStream->fPendingCr || pStream->fBinary)
     1158    { /* likely */ }
     1159    else
     1160    {
     1161        pStream->pchBuf[cbInBuffer] = '\r';
     1162        pStream->fPendingCr         = false;
     1163        pStream->offBufEnd          = ++cbInBuffer;
     1164    }
     1165
     1166    /*
     1167     * Read data till the buffer is full.
     1168     */
     1169    size_t cbRead = 0;
     1170    int rc = RTFileRead(rtStrmGetFile(pStream), &pStream->pchBuf[cbInBuffer], pStream->cbBufAlloc - cbInBuffer, &cbRead);
     1171    if (RT_SUCCESS(rc))
     1172    {
     1173        cbInBuffer        += cbRead;
     1174        pStream->offBufEnd = cbInBuffer;
     1175
     1176        if (cbInBuffer != 0)
     1177        {
     1178            if (pStream->fBinary)
     1179                return VINF_SUCCESS;
     1180        }
     1181        else
     1182        {
     1183            /** @todo this shouldn't be sticky, should it? */
     1184            ASMAtomicWriteS32(&pStream->i32Error, VERR_EOF);
     1185            return VERR_EOF;
     1186        }
     1187
     1188        /*
     1189         * Do CRLF -> LF conversion in the buffer.
     1190         */
     1191        char  *pchCur = &pStream->pchBuf[offCrLfConvStart];
     1192        size_t cbLeft = cbInBuffer - offCrLfConvStart;
     1193        while (cbLeft > 0)
     1194        {
     1195            Assert(&pchCur[cbLeft] == &pStream->pchBuf[pStream->offBufEnd]);
     1196            char *pchCr = (char *)memchr(pchCur, '\r', cbLeft);
     1197            if (pchCr)
    6281198            {
    629                 *pcchWidth = Info.ws_col ? Info.ws_col : 80;
    630                 return VINF_SUCCESS;
     1199                size_t offCur = (size_t)(pchCr - pchCur);
     1200                if (offCur + 1 < cbLeft)
     1201                {
     1202                    if (pchCr[1] == '\n')
     1203                    {
     1204                        /* Found one '\r\n' sequence. Look for more before shifting the buffer content. */
     1205                        cbLeft -= offCur;
     1206                        pchCur  = pchCr;
     1207
     1208                        do
     1209                        {
     1210                            *pchCur++  = '\n'; /* dst */
     1211                            cbLeft    -= 2;
     1212                            pchCr     += 2;    /* src */
     1213                        } while  (cbLeft >= 2 && pchCr[0] == '\r' && pchCr[1] == '\n');
     1214
     1215                        memmove(&pchCur, pchCr, cbLeft);
     1216                    }
     1217                    else
     1218                    {
     1219                        cbLeft -= offCur + 1;
     1220                        pchCur  = pchCr  + 1;
     1221                    }
     1222                }
     1223                else
     1224                {
     1225                    Assert(pchCr == &pStream->pchBuf[pStream->offBufEnd - 1]);
     1226                    pStream->fPendingCr = true;
     1227                    pStream->offBufEnd  = --cbInBuffer;
     1228                    break;
     1229                }
    6311230            }
    632             return RTErrConvertFromErrno(errno);
    633 #endif
    634         }
    635     }
    636     return VERR_INVALID_FUNCTION;
    637 }
    638 
     1231            else
     1232                break;
     1233        }
     1234
     1235        return VINF_SUCCESS;
     1236    }
     1237
     1238    /*
     1239     * If there is data in the buffer, don't raise the error till it has all
     1240     * been consumed, ASSUMING that another fill call will follow and that the
     1241     * error condition will reoccur then.
     1242     *
     1243     * Note! We may currently end up not converting a CRLF pair, if it's
     1244     *       split over a temporary EOF condition, since we forces the caller
     1245     *       to read the CR before requesting more data.  However, it's not a
     1246     *       very likely scenario, so we'll just leave it like that for now.
     1247     */
     1248    if (cbInBuffer)
     1249        return VINF_SUCCESS;
     1250    ASMAtomicWriteS32(&pStream->i32Error, rc);
     1251    return rc;
     1252}
     1253
     1254
     1255/**
     1256 * Copies @a cbSrc bytes from @a pvSrc and into the buffer, flushing as needed
     1257 * to make space available.
     1258 *
     1259 *
     1260 * @returns IPRT status code (errors not assigned to i32Error).
     1261 * @param   pStream             The stream.
     1262 * @param   pvSrc               The source buffer.
     1263 * @param   cbSrc               Number of bytes to copy from @a pvSrc.
     1264 * @param   pcbTotal            A total counter to update with what was copied.
     1265 */
     1266static int rtStrmBufCopyTo(PRTSTREAM pStream, const void *pvSrc, size_t cbSrc, size_t *pcbTotal)
     1267{
     1268    Assert(cbSrc > 0);
     1269    for (;;)
     1270    {
     1271        size_t cbToCopy = RT_MIN(pStream->cbBufAlloc - pStream->offBufEnd, cbSrc);
     1272        if (cbToCopy)
     1273        {
     1274            memcpy(&pStream->pchBuf[pStream->offBufEnd], pvSrc, cbToCopy);
     1275            pStream->offBufEnd += cbToCopy;
     1276            pvSrc               = (const char *)pvSrc + cbToCopy;
     1277            *pcbTotal          += cbToCopy;
     1278            cbSrc              -= cbToCopy;
     1279            if (!cbSrc)
     1280                break;
     1281        }
     1282
     1283        int rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
     1284        if (RT_FAILURE(rc))
     1285            return rc;
     1286    }
     1287    return VINF_SUCCESS;
     1288}
     1289
     1290#endif /* RTSTREAM_STANDALONE */
    6391291
    6401292
     
    6561308    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
    6571309
    658     int rc;
     1310#ifdef RTSTREAM_STANDALONE
     1311    rtStrmLock(pStream);
     1312    int const rc1 = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
     1313    int const rc2 = RTFileSeek(rtStrmGetFile(pStream), 0, RTFILE_SEEK_BEGIN, NULL);
     1314    int rc = RT_SUCCESS(rc1) ? rc2 : rc1;
     1315    ASMAtomicWriteS32(&pStream->i32Error, rc);
     1316    rtStrmUnlock(pStream);
     1317#else
    6591318    clearerr(pStream->pFile);
    6601319    errno = 0;
     1320    int rc;
    6611321    if (!fseek(pStream->pFile, 0, SEEK_SET))
    662     {
    663         ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
    6641322        rc = VINF_SUCCESS;
    665     }
    6661323    else
    667     {
    6681324        rc = RTErrConvertFromErrno(errno);
    669         ASMAtomicWriteS32(&pStream->i32Error, rc);
    670     }
    671 
     1325    ASMAtomicWriteS32(&pStream->i32Error, rc);
     1326#endif
    6721327    return rc;
    6731328}
     
    6811336static void rtStreamRecheckMode(PRTSTREAM pStream)
    6821337{
    683 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
     1338#if (defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)) && !defined(RTSTREAM_STANDALONE)
    6841339    int fh = fileno(pStream->pFile);
    6851340    if (fh >= 0)
     
    7071362 * @param   pvBuf           Where to put the read bits.
    7081363 *                          Must be cbRead bytes or more.
    709  * @param   cbRead          Number of bytes to read.
     1364 * @param   cbToRead        Number of bytes to read.
    7101365 * @param   pcbRead         Where to store the number of bytes actually read.
    7111366 *                          If NULL cbRead bytes are read or an error is returned.
    7121367 */
    713 RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead)
    714 {
    715     AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
    716 
     1368RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
     1369{
     1370    AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
     1371    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
     1372
     1373#ifdef RTSTREAM_STANDALONE
     1374    rtStrmLock(pStream);
     1375    int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream);
     1376#else
    7171377    int rc = pStream->i32Error;
     1378#endif
    7181379    if (RT_SUCCESS(rc))
    7191380    {
     
    7211382            rtStreamRecheckMode(pStream);
    7221383
     1384#ifdef RTSTREAM_STANDALONE
     1385
     1386        /*
     1387         * Copy data thru the read buffer for now as that'll handle both binary
     1388         * and text modes seamlessly.  We could optimize larger reads here when
     1389         * in binary mode, that can wait till the basics work, I think.
     1390         */
     1391        size_t cbTotal = 0;
     1392        if (cbToRead > 0)
     1393            for (;;)
     1394            {
     1395                size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
     1396                if (cbInBuffer > 0)
     1397                {
     1398                    size_t cbToCopy = RT_MIN(cbInBuffer, cbToRead);
     1399                    memcpy(pvBuf, &pStream->pchBuf[pStream->offBufFirst], cbToCopy);
     1400                    cbTotal  += cbToRead;
     1401                    cbToRead -= cbToCopy;
     1402                    pvBuf     = (char *)pvBuf + cbToCopy;
     1403                    if (!cbToRead)
     1404                        break;
     1405                }
     1406                rc = rtStrmBufFill(pStream);
     1407                if (RT_SUCCESS(rc))
     1408                { /* likely */ }
     1409                else
     1410                {
     1411                    if (rc == VERR_EOF && pcbRead && cbTotal > 0)
     1412                        rc = VINF_EOF;
     1413                    break;
     1414                }
     1415            }
     1416        if (pcbRead)
     1417            *pcbRead = cbTotal;
     1418
     1419#else  /* !RTSTREAM_STANDALONE */
    7231420        if (pcbRead)
    7241421        {
     
    7261423             * Can do with a partial read.
    7271424             */
    728             *pcbRead = fread(pvBuf, 1, cbRead, pStream->pFile);
    729             if (    *pcbRead == cbRead
     1425            *pcbRead = fread(pvBuf, 1, cbToRead, pStream->pFile);
     1426            if (    *pcbRead == cbToRead
    7301427                || !ferror(pStream->pFile))
    731                 return VINF_SUCCESS;
    732             if (feof(pStream->pFile))
    733             {
    734                 if (*pcbRead)
    735                     return VINF_EOF;
    736                 rc = VERR_EOF;
    737             }
     1428                rc = VINF_SUCCESS;
     1429            else if (feof(pStream->pFile))
     1430                rc = *pcbRead ? VINF_EOF : VERR_EOF;
    7381431            else if (ferror(pStream->pFile))
    7391432                rc = VERR_READ_ERROR;
     
    7491442             * Must read it all!
    7501443             */
    751             if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1)
    752                 return VINF_SUCCESS;
    753 
     1444            if (fread(pvBuf, cbToRead, 1, pStream->pFile) == 1)
     1445                rc = VINF_SUCCESS;
    7541446            /* possible error/eof. */
    755             if (feof(pStream->pFile))
     1447            else if (feof(pStream->pFile))
    7561448                rc = VERR_EOF;
    7571449            else if (ferror(pStream->pFile))
     
    7631455            }
    7641456        }
    765         ASMAtomicWriteS32(&pStream->i32Error, rc);
    766     }
     1457#endif /* !RTSTREAM_STANDALONE */
     1458        if (RT_FAILURE(rc))
     1459            ASMAtomicWriteS32(&pStream->i32Error, rc);
     1460    }
     1461#ifdef RTSTREAM_STANDALONE
     1462    rtStrmUnlock(pStream);
     1463#endif
    7671464    return rc;
    7681465}
     
    7851482
    7861483
    787 #ifdef RT_OS_WINDOWS
     1484#if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE)
     1485
    7881486/**
    7891487 * Check if the stream is for a Window console.
     
    8081506    return false;
    8091507}
     1508
     1509
     1510static int rtStrmWriteWinConsoleLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, HANDLE hCon)
     1511{
     1512    int rc;
     1513# ifdef HAVE_FWRITE_UNLOCKED
     1514    if (!fflush_unlocked(pStream->pFile))
     1515# else
     1516    if (!fflush(pStream->pFile))
     1517# endif
     1518    {
     1519        /** @todo Consider buffering later. For now, we'd rather correct output than
     1520         *        fast output. */
     1521        DWORD    cwcWritten = 0;
     1522        PRTUTF16 pwszSrc = NULL;
     1523        size_t   cwcSrc = 0;
     1524        rc = RTStrToUtf16Ex((const char *)pvBuf, cbToWrite, &pwszSrc, 0, &cwcSrc);
     1525        if (RT_SUCCESS(rc))
     1526        {
     1527            if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
     1528            {
     1529                /* try write char-by-char to avoid heap problem. */
     1530                cwcWritten = 0;
     1531                while (cwcWritten != cwcSrc)
     1532                {
     1533                    DWORD cwcThis;
     1534                    if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
     1535                    {
     1536                        if (!pcbWritten || cwcWritten == 0)
     1537                            rc = RTErrConvertFromErrno(GetLastError());
     1538                        break;
     1539                    }
     1540                    if (cwcThis != 1) /* Unable to write current char (amount)? */
     1541                        break;
     1542                    cwcWritten++;
     1543                }
     1544            }
     1545            if (RT_SUCCESS(rc))
     1546            {
     1547                if (cwcWritten == cwcSrc)
     1548                {
     1549                    if (pcbWritten)
     1550                        *pcbWritten = cbToWrite;
     1551                }
     1552                else if (pcbWritten)
     1553                {
     1554                    PCRTUTF16   pwszCur = pwszSrc;
     1555                    const char *pszCur  = (const char *)pvBuf;
     1556                    while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
     1557                    {
     1558                        RTUNICP CpIgnored;
     1559                        RTUtf16GetCpEx(&pwszCur, &CpIgnored);
     1560                        RTStrGetCpEx(&pszCur, &CpIgnored);
     1561                    }
     1562                    *pcbWritten = pszCur - (const char *)pvBuf;
     1563                }
     1564                else
     1565                    rc = VERR_WRITE_ERROR;
     1566            }
     1567            RTUtf16Free(pwszSrc);
     1568        }
     1569    }
     1570    else
     1571        rc = RTErrConvertFromErrno(errno);
     1572    return rc;
     1573}
     1574
    8101575#endif /* RT_OS_WINDOWS */
     1576
     1577static int rtStrmWriteWorkerLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fMustWriteAll)
     1578{
     1579#ifdef RTSTREAM_STANDALONE
     1580    /*
     1581     * Check preconditions.
     1582     */
     1583    Assert(pStream->enmBufDir    == RTSTREAMBUFDIR_WRITE);
     1584    Assert(pStream->cbBufAlloc   >= 256);
     1585    Assert(pStream->offBufFirst  <= pStream->cbBufAlloc);
     1586    Assert(pStream->offBufEnd    <= pStream->cbBufAlloc);
     1587    Assert(pStream->offBufFirst  <= pStream->offBufEnd);
     1588
     1589    /*
     1590     * We write everything via the buffer, letting the buffer flushing take
     1591     * care of console output hacks and similar.
     1592     */
     1593    RT_NOREF(fMustWriteAll);
     1594    int    rc      = VINF_SUCCESS;
     1595    size_t cbTotal = 0;
     1596    if (cbToWrite > 0)
     1597    {
     1598# ifdef RTSTREAM_WITH_TEXT_MODE
     1599        const char *pchLf;
     1600        if (   !pStream->fBinary
     1601            && (pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite)) != NULL)
     1602            for (;;)
     1603            {
     1604                /* Deal with everything up to the newline. */
     1605                size_t const cbToLf = (size_t)(pchLf - (const char *)pvBuf);
     1606                if (cbToLf > 0)
     1607                {
     1608                    rc = rtStrmBufCopyTo(pStream, pvBuf, cbToLf, &cbTotal);
     1609                    if (RT_FAILURE(rc))
     1610                        break;
     1611                }
     1612
     1613                /* Copy the CRLF sequence into the buffer in one go to avoid complications. */
     1614                if (pStream->cbBufAlloc - pStream->offBufEnd < 2)
     1615                {
     1616                    rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
     1617                    if (RT_FAILURE(rc))
     1618                        break;
     1619                    Assert(pStream->cbBufAlloc - pStream->offBufEnd >= 2);
     1620                }
     1621                pStream->pchBuf[pStream->offBufEnd++] = '\r';
     1622                pStream->pchBuf[pStream->offBufEnd++] = '\n';
     1623
     1624                /* Advance past the newline. */
     1625                pvBuf               = (const char *)pvBuf + 1 + cbToLf;
     1626                cbTotal            += 1 + cbToLf;
     1627                cbToWrite          -= 1 + cbToLf;
     1628                if (!cbToWrite)
     1629                    break;
     1630
     1631                /* More newlines? */
     1632                pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite);
     1633                if (!pchLf)
     1634                {
     1635                    rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal);
     1636                    break;
     1637                }
     1638            }
     1639        else
     1640# endif
     1641            rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal);
     1642
     1643        /*
     1644         * If line buffered or unbuffered, we probably have to do some flushing now.
     1645         */
     1646        if (RT_SUCCESS(rc) && pStream->enmBufStyle != RTSTREAMBUFSTYLE_FULL)
     1647        {
     1648            Assert(pStream->enmBufStyle == RTSTREAMBUFSTYLE_LINE || pStream->enmBufStyle == RTSTREAMBUFSTYLE_UNBUFFERED);
     1649            size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
     1650            if (cbInBuffer > 0)
     1651            {
     1652                if (   pStream->enmBufStyle != RTSTREAMBUFSTYLE_LINE
     1653                    || pStream->pchBuf[pStream->offBufEnd - 1] == '\n')
     1654                    rc = rtStrmBufFlushWrite(pStream, cbInBuffer);
     1655                else
     1656                {
     1657                    const char *pchToFlush = &pStream->pchBuf[pStream->offBufFirst];
     1658                    const char *pchLastLf  = (const char *)memrchr(pchToFlush, '\n', cbInBuffer);
     1659                    if (pchLastLf)
     1660                        rc = rtStrmBufFlushWrite(pStream, (size_t)(&pchLastLf[1] - pchToFlush));
     1661                }
     1662            }
     1663        }
     1664    }
     1665    if (pcbWritten)
     1666        *pcbWritten = cbTotal;
     1667    return rc;
     1668
     1669
     1670#else
     1671    if (!fMustWriteAll)
     1672    {
     1673        IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
     1674# ifdef HAVE_FWRITE_UNLOCKED
     1675        *pcbWritten = fwrite_unlocked(pvBuf, 1, cbToWrite, pStream->pFile);
     1676# else
     1677        *pcbWritten = fwrite(pvBuf, 1, cbToWrite, pStream->pFile);
     1678# endif
     1679        IPRT_ALIGNMENT_CHECKS_ENABLE();
     1680        if (    *pcbWritten == cbToWrite
     1681# ifdef HAVE_FWRITE_UNLOCKED
     1682            ||  !ferror_unlocked(pStream->pFile))
     1683# else
     1684            ||  !ferror(pStream->pFile))
     1685# endif
     1686            return VINF_SUCCESS;
     1687    }
     1688    else
     1689    {
     1690        /* Must write it all! */
     1691        IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
     1692# ifdef HAVE_FWRITE_UNLOCKED
     1693        size_t cbWritten = fwrite_unlocked(pvBuf, cbToWrite, 1, pStream->pFile);
     1694# else
     1695        size_t cbWritten = fwrite(pvBuf, cbToWrite, 1, pStream->pFile);
     1696# endif
     1697        if (pcbWritten)
     1698            *pcbWritten = cbWritten;
     1699        IPRT_ALIGNMENT_CHECKS_ENABLE();
     1700        if (cbWritten == 1)
     1701            return VINF_SUCCESS;
     1702# ifdef HAVE_FWRITE_UNLOCKED
     1703        if (!ferror_unlocked(pStream->pFile))
     1704# else
     1705        if (!ferror(pStream->pFile))
     1706# endif
     1707            return VINF_SUCCESS; /* WEIRD! But anyway... */
     1708    }
     1709    return VERR_WRITE_ERROR;
     1710#endif
     1711}
    8111712
    8121713
     
    8171718 * @param   pStream             The stream.
    8181719 * @param   pvBuf               What to write.
    819  * @param   cbWrite             How much to write.
     1720 * @param   cbToWrite           How much to write.
    8201721 * @param   pcbWritten          Where to optionally return the number of bytes
    8211722 *                              written.
    8221723 * @param   fSureIsText         Set if we're sure this is UTF-8 text already.
    8231724 */
    824 static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten,
    825                               bool fSureIsText)
    826 {
     1725static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText)
     1726{
     1727#ifdef RTSTREAM_STANDALONE
     1728    int rc = rtStrmBufCheckErrorAndSwitchToWriteMode(pStream);
     1729#else
    8271730    int rc = pStream->i32Error;
     1731#endif
    8281732    if (RT_FAILURE(rc))
    8291733        return rc;
     
    8311735        rtStreamRecheckMode(pStream);
    8321736
    833 #ifdef RT_OS_WINDOWS
     1737#if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE)
    8341738    /*
    8351739     * Use the unicode console API when possible in order to avoid stuff
     
    8381742    HANDLE hCon;
    8391743    if (rtStrmIsConsoleUnlocked(pStream, &hCon))
    840     {
    841 # ifdef HAVE_FWRITE_UNLOCKED
    842         if (!fflush_unlocked(pStream->pFile))
    843 # else
    844         if (!fflush(pStream->pFile))
    845 # endif
    846         {
    847             /** @todo Consider buffering later. For now, we'd rather correct output than
    848              *        fast output. */
    849             DWORD    cwcWritten = 0;
    850             PRTUTF16 pwszSrc = NULL;
    851             size_t   cwcSrc = 0;
    852             rc = RTStrToUtf16Ex((const char *)pvBuf, cbWrite, &pwszSrc, 0, &cwcSrc);
    853             if (RT_SUCCESS(rc))
    854             {
    855                 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
    856                 {
    857                     /* try write char-by-char to avoid heap problem. */
    858                     cwcWritten = 0;
    859                     while (cwcWritten != cwcSrc)
    860                     {
    861                         DWORD cwcThis;
    862                         if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
    863                         {
    864                             if (!pcbWritten || cwcWritten == 0)
    865                                 rc = RTErrConvertFromErrno(GetLastError());
    866                             break;
    867                         }
    868                         if (cwcThis != 1) /* Unable to write current char (amount)? */
    869                             break;
    870                         cwcWritten++;
    871                     }
    872                 }
    873                 if (RT_SUCCESS(rc))
    874                 {
    875                     if (cwcWritten == cwcSrc)
    876                     {
    877                         if (pcbWritten)
    878                             *pcbWritten = cbWrite;
    879                     }
    880                     else if (pcbWritten)
    881                     {
    882                         PCRTUTF16   pwszCur = pwszSrc;
    883                         const char *pszCur  = (const char *)pvBuf;
    884                         while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
    885                         {
    886                             RTUNICP CpIgnored;
    887                             RTUtf16GetCpEx(&pwszCur, &CpIgnored);
    888                             RTStrGetCpEx(&pszCur, &CpIgnored);
    889                         }
    890                         *pcbWritten = pszCur - (const char *)pvBuf;
    891                     }
    892                     else
    893                         rc = VERR_WRITE_ERROR;
    894                 }
    895                 RTUtf16Free(pwszSrc);
    896             }
    897         }
    898         else
    899             rc = RTErrConvertFromErrno(errno);
    900         if (RT_FAILURE(rc))
    901             ASMAtomicWriteS32(&pStream->i32Error, rc);
    902         return rc;
    903     }
    904 #endif /* RT_OS_WINDOWS */
     1744        rc = rtStrmWriteWinConsoleLocked(pStream, pvBuf, cbToWrite, pcbWritten, hCon);
     1745#endif /* RT_OS_WINDOWS && !RTSTREAM_STANDALONE */
    9051746
    9061747    /*
     
    9121753     */
    9131754    /** @todo Skip this if the current code set is UTF-8. */
    914     if (   pStream->fCurrentCodeSet
    915         && !pStream->fBinary
    916         && (   fSureIsText
    917             || rtStrmIsUtf8Text(pvBuf, cbWrite))
    918        )
     1755    else if (   pStream->fCurrentCodeSet
     1756             && !pStream->fBinary
     1757             && (   fSureIsText
     1758                 || rtStrmIsUtf8Text(pvBuf, cbToWrite))
     1759            )
    9191760    {
    9201761        char       *pszSrcFree = NULL;
    9211762        const char *pszSrc     = (const char *)pvBuf;
    922         if (pszSrc[cbWrite - 1])
    923         {
    924             pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbWrite);
     1763        if (pszSrc[cbToWrite - 1])
     1764        {
     1765            pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbToWrite);
    9251766            if (pszSrc == NULL)
    9261767                rc = VERR_NO_STR_MEMORY;
     
    9331774            {
    9341775                size_t  cchSrcCurCP = strlen(pszSrcCurCP);
    935                 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
    936 #ifdef HAVE_FWRITE_UNLOCKED
    937                 ssize_t cbWritten = fwrite_unlocked(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
    938 #else
    939                 ssize_t cbWritten = fwrite(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
    940 #endif
    941                 IPRT_ALIGNMENT_CHECKS_ENABLE();
    942                 if (cbWritten == 1)
    943                 {
    944                     if (pcbWritten)
    945                         *pcbWritten = cbWrite;
    946                 }
    947 #ifdef HAVE_FWRITE_UNLOCKED
    948                 else if (!ferror_unlocked(pStream->pFile))
    949 #else
    950                 else if (!ferror(pStream->pFile))
    951 #endif
    952                 {
    953                     if (pcbWritten)
    954                         *pcbWritten = 0;
    955                 }
    956                 else
    957                     rc = VERR_WRITE_ERROR;
     1776                size_t  cbWritten = 0;
     1777                rc = rtStrmWriteWorkerLocked(pStream, pszSrcCurCP, cchSrcCurCP, &cbWritten, true /*fMustWriteAll*/);
     1778                if (pcbWritten)
     1779                    *pcbWritten = cbWritten == cchSrcCurCP ? cbToWrite : 0;
    9581780                RTStrFree(pszSrcCurCP);
    9591781            }
    9601782            RTStrFree(pszSrcFree);
    9611783        }
    962 
    963         if (RT_FAILURE(rc))
    964             ASMAtomicWriteS32(&pStream->i32Error, rc);
    965         return rc;
    966     }
    967 
     1784    }
    9681785    /*
    9691786     * Otherwise, just write it as-is.
    9701787     */
    971     if (pcbWritten)
    972     {
    973         IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
    974 #ifdef HAVE_FWRITE_UNLOCKED
    975         *pcbWritten = fwrite_unlocked(pvBuf, 1, cbWrite, pStream->pFile);
    976 #else
    977         *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);
    978 #endif
    979         IPRT_ALIGNMENT_CHECKS_ENABLE();
    980         if (    *pcbWritten == cbWrite
    981 #ifdef HAVE_FWRITE_UNLOCKED
    982             ||  !ferror_unlocked(pStream->pFile))
    983 #else
    984             ||  !ferror(pStream->pFile))
    985 #endif
    986             return VINF_SUCCESS;
    987         rc = VERR_WRITE_ERROR;
    988     }
    9891788    else
    990     {
    991         /* Must write it all! */
    992         IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
    993 #ifdef HAVE_FWRITE_UNLOCKED
    994         size_t cbWritten = fwrite_unlocked(pvBuf, cbWrite, 1, pStream->pFile);
    995 #else
    996         size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile);
    997 #endif
    998         IPRT_ALIGNMENT_CHECKS_ENABLE();
    999         if (cbWritten == 1)
    1000             return VINF_SUCCESS;
    1001 #ifdef HAVE_FWRITE_UNLOCKED
    1002         if (!ferror_unlocked(pStream->pFile))
    1003 #else
    1004         if (!ferror(pStream->pFile))
    1005 #endif
    1006             return VINF_SUCCESS; /* WEIRD! But anyway... */
    1007 
    1008         rc = VERR_WRITE_ERROR;
    1009     }
    1010     ASMAtomicWriteS32(&pStream->i32Error, rc);
    1011 
     1789        rc = rtStrmWriteWorkerLocked(pStream, pvBuf, cbToWrite, pcbWritten, pcbWritten == NULL);
     1790
     1791    /*
     1792     * Update error status on failure and return.
     1793     */
     1794    if (RT_FAILURE(rc))
     1795        ASMAtomicWriteS32(&pStream->i32Error, rc);
    10121796    return rc;
    10131797}
     
    10201804 * @param   pStream             The stream.
    10211805 * @param   pvBuf               What to write.
    1022  * @param   cbWrite             How much to write.
     1806 * @param   cbToWrite           How much to write.
    10231807 * @param   pcbWritten          Where to optionally return the number of bytes
    10241808 *                              written.
    10251809 * @param   fSureIsText         Set if we're sure this is UTF-8 text already.
    10261810 */
    1027 static int rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, bool fSureIsText)
     1811DECLINLINE(int) rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText)
    10281812{
    10291813    rtStrmLock(pStream);
    1030     int rc = rtStrmWriteLocked(pStream, pvBuf, cbWrite, pcbWritten, fSureIsText);
     1814    int rc = rtStrmWriteLocked(pStream, pvBuf, cbToWrite, pcbWritten, fSureIsText);
    10311815    rtStrmUnlock(pStream);
    10321816    return rc;
     
    10401824 * @param   pStream         The stream.
    10411825 * @param   pvBuf           Where to get the bits to write from.
    1042  * @param   cbWrite         Number of bytes to write.
     1826 * @param   cbToWrite       Number of bytes to write.
    10431827 * @param   pcbWritten      Where to store the number of bytes actually written.
    1044  *                          If NULL cbWrite bytes are written or an error is returned.
    1045  */
    1046 RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
     1828 *                          If NULL cbToWrite bytes are written or an error is
     1829 *                          returned.
     1830 */
     1831RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
    10471832{
    10481833    AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
    1049     return rtStrmWrite(pStream, pvBuf, cbWrite, pcbWritten, false);
     1834    return rtStrmWrite(pStream, pvBuf, cbToWrite, pcbWritten, false);
    10501835}
    10511836
     
    10991884RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString)
    11001885{
    1101     AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
    1102     int rc;
    1103     if (pszString && cbString > 1)
    1104     {
    1105         rc = pStream->i32Error;
    1106         if (RT_SUCCESS(rc))
    1107         {
    1108             cbString--;            /* save space for the terminator. */
    1109             rtStrmLock(pStream);
    1110             for (;;)
     1886    AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
     1887    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
     1888    AssertReturn(pszString, VERR_INVALID_POINTER);
     1889    AssertReturn(cbString >= 2, VERR_INVALID_PARAMETER);
     1890
     1891    rtStrmLock(pStream);
     1892
     1893#ifdef RTSTREAM_STANDALONE
     1894    int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream);
     1895#else
     1896    int rc = pStream->i32Error;
     1897#endif
     1898    if (RT_SUCCESS(rc))
     1899    {
     1900        cbString--;            /* Reserve space for the terminator. */
     1901
     1902#ifdef RTSTREAM_STANDALONE
     1903        char * const pszStringStart = pszString;
     1904#endif
     1905        for (;;)
     1906        {
     1907#ifdef RTSTREAM_STANDALONE
     1908            /* Make sure there is at least one character in the buffer: */
     1909            size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
     1910            if (cbInBuffer == 0)
    11111911            {
    1112 #ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
    1113                 int ch = fgetc_unlocked(pStream->pFile);
    1114 #else
    1115                 int ch = fgetc(pStream->pFile);
    1116 #endif
    1117 
    1118                 /* Deal with \r\n sequences here. We'll return lone CR, but
    1119                    treat CRLF as LF. */
    1120                 if (ch == '\r')
    1121                 {
    1122 #ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
    1123                     ch = fgetc_unlocked(pStream->pFile);
    1124 #else
    1125                     ch = fgetc(pStream->pFile);
    1126 #endif
    1127                     if (ch == '\n')
    1128                         break;
    1129 
    1130                     *pszString++ = '\r';
    1131                     if (--cbString <= 0)
    1132                     {
    1133                         /* yeah, this is an error, we dropped a character. */
    1134                         rc = VERR_BUFFER_OVERFLOW;
    1135                         break;
    1136                     }
    1137                 }
    1138 
    1139                 /* Deal with end of file. */
    1140                 if (ch == EOF)
    1141                 {
    1142 #ifdef HAVE_FWRITE_UNLOCKED
    1143                     if (feof_unlocked(pStream->pFile))
    1144 #else
    1145                     if (feof(pStream->pFile))
    1146 #endif
    1147                     {
    1148                         rc = VERR_EOF;
    1149                         break;
    1150                     }
    1151 #ifdef HAVE_FWRITE_UNLOCKED
    1152                     if (ferror_unlocked(pStream->pFile))
    1153 #else
    1154                     if (ferror(pStream->pFile))
    1155 #endif
    1156                         rc = VERR_READ_ERROR;
    1157                     else
    1158                     {
    1159                         AssertMsgFailed(("This shouldn't happen\n"));
    1160                         rc = VERR_INTERNAL_ERROR;
    1161                     }
     1912                rc = rtStrmBufFill(pStream);
     1913                if (RT_SUCCESS(rc))
     1914                    cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
     1915                else
    11621916                    break;
    1163                 }
    1164 
    1165                 /* Deal with null terminator and (lone) new line. */
    1166                 if (ch == '\0' || ch == '\n')
     1917            }
     1918
     1919            /* Scan the buffer content terminating on a '\n', '\r\n' and '\0' sequence. */
     1920            const char *pchSrc     = &pStream->pchBuf[pStream->offBufFirst];
     1921            const char *pchNewline = (const char *)memchr(pchSrc, '\n', cbInBuffer);
     1922            const char *pchTerm    = (const char *)memchr(pchSrc, '\0', cbInBuffer);
     1923            size_t      cbCopy;
     1924            size_t      cbAdvance;
     1925            bool        fStop      = pchNewline || pchTerm;
     1926            if (!fStop)
     1927                cbAdvance = cbCopy = cbInBuffer;
     1928            else if (!pchTerm || (pchNewline && pchTerm && (uintptr_t)pchNewline < (uintptr_t)pchTerm))
     1929            {
     1930                cbCopy    = (size_t)(pchNewline - pchSrc);
     1931                cbAdvance = cbCopy + 1;
     1932                if (cbCopy && pchNewline[-1] == '\r')
     1933                    cbCopy--;
     1934                else if (cbCopy == 0 && (uintptr_t)pszString > (uintptr_t)pszStringStart && pszString[-1] == '\r')
     1935                    pszString--, cbString++; /* drop trailing '\r' that it turns out was followed by '\n' */
     1936            }
     1937            else
     1938            {
     1939                cbCopy    = (size_t)(pchTerm - pchSrc);
     1940                cbAdvance = cbCopy + 1;
     1941            }
     1942
     1943            /* Adjust for available space in the destination buffer, copy over the string
     1944               characters and advance the buffer position (even on overflow). */
     1945            if (cbCopy <= cbString)
     1946                pStream->offBufFirst += cbAdvance;
     1947            else
     1948            {
     1949                rc        = VERR_BUFFER_OVERFLOW;
     1950                fStop     = true;
     1951                cbCopy    = cbString;
     1952                pStream->offBufFirst += cbString;
     1953            }
     1954
     1955            memcpy(pszString, pchSrc, cbCopy);
     1956            pszString += cbCopy;
     1957            cbString  -= cbCopy;
     1958
     1959            if (fStop)
     1960                break;
     1961
     1962#else  /* !RTSTREAM_STANDALONE */
     1963# ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
     1964            int ch = fgetc_unlocked(pStream->pFile);
     1965# else
     1966            int ch = fgetc(pStream->pFile);
     1967# endif
     1968
     1969            /* Deal with \r\n sequences here. We'll return lone CR, but
     1970               treat CRLF as LF. */
     1971            if (ch == '\r')
     1972            {
     1973# ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
     1974                ch = fgetc_unlocked(pStream->pFile);
     1975# else
     1976                ch = fgetc(pStream->pFile);
     1977# endif
     1978                if (ch == '\n')
    11671979                    break;
    11681980
    1169                 /* No special character, append it to the return string. */
    1170                 *pszString++ = ch;
     1981                *pszString++ = '\r';
    11711982                if (--cbString <= 0)
    11721983                {
    1173                     rc = VINF_BUFFER_OVERFLOW;
     1984                    /* yeah, this is an error, we dropped a character. */
     1985                    rc = VERR_BUFFER_OVERFLOW;
    11741986                    break;
    11751987                }
    11761988            }
    1177             rtStrmUnlock(pStream);
    1178 
    1179             *pszString = '\0';
    1180             if (RT_FAILURE(rc))
    1181                 ASMAtomicWriteS32(&pStream->i32Error, rc);
    1182         }
    1183     }
    1184     else
    1185     {
    1186         AssertMsgFailed(("no buffer or too small buffer!\n"));
    1187         rc = VERR_INVALID_PARAMETER;
    1188     }
     1989
     1990            /* Deal with end of file. */
     1991            if (ch == EOF)
     1992            {
     1993# ifdef HAVE_FWRITE_UNLOCKED
     1994                if (feof_unlocked(pStream->pFile))
     1995# else
     1996                if (feof(pStream->pFile))
     1997# endif
     1998                {
     1999                    rc = VERR_EOF;
     2000                    break;
     2001                }
     2002# ifdef HAVE_FWRITE_UNLOCKED
     2003                if (ferror_unlocked(pStream->pFile))
     2004# else
     2005                if (ferror(pStream->pFile))
     2006# endif
     2007                    rc = VERR_READ_ERROR;
     2008                else
     2009                {
     2010                    AssertMsgFailed(("This shouldn't happen\n"));
     2011                    rc = VERR_INTERNAL_ERROR;
     2012                }
     2013                break;
     2014            }
     2015
     2016            /* Deal with null terminator and (lone) new line. */
     2017            if (ch == '\0' || ch == '\n')
     2018                break;
     2019
     2020            /* No special character, append it to the return string. */
     2021            *pszString++ = ch;
     2022            if (--cbString <= 0)
     2023            {
     2024                rc = VINF_BUFFER_OVERFLOW;
     2025                break;
     2026            }
     2027#endif /* !RTSTREAM_STANDALONE */
     2028        }
     2029
     2030        *pszString = '\0';
     2031        if (RT_FAILURE(rc))
     2032            ASMAtomicWriteS32(&pStream->i32Error, rc);
     2033    }
     2034
     2035    rtStrmUnlock(pStream);
    11892036    return rc;
    11902037}
     
    11992046RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
    12002047{
     2048    AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
     2049    AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
     2050
     2051#ifdef RTSTREAM_STANDALONE
     2052    rtStrmLock(pStream);
     2053    int rc = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
     2054    rtStrmUnlock(pStream);
     2055    return rc;
     2056
     2057#else
    12012058    if (!fflush(pStream->pFile))
    12022059        return VINF_SUCCESS;
    12032060    return RTErrConvertFromErrno(errno);
     2061#endif
    12042062}
    12052063
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette