VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/stream.cpp

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 82.6 KB
RevLine 
[1]1/* $Id: stream.cpp 103005 2024-01-23 23:55:58Z vboxsync $ */
2/** @file
[8245]3 * IPRT - I/O Stream.
[1]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[5999]11 *
[96407]12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
[5999]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
[5999]29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[1]35 */
36
37
38
[95886]39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42/** @def RTSTREAM_STANDALONE
43 * Standalone streams w/o depending on stdio.h, using our RTFile API for
44 * file/whatever access. */
[95888]45#if (defined(IPRT_NO_CRT) && defined(RT_OS_WINDOWS)) || defined(DOXYGEN_RUNNING)
[95886]46# define RTSTREAM_STANDALONE
47#endif
48
[32464]49#if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */
[95886]50# ifndef RTSTREAM_STANDALONE
51# define HAVE_FWRITE_UNLOCKED
52# endif
[32464]53#endif
54
[95886]55/** @def RTSTREAM_WITH_TEXT_MODE
56 * Indicates whether we need to support the 'text' mode files and convert
57 * CRLF to LF while reading and writing. */
58#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
59# define RTSTREAM_WITH_TEXT_MODE
60#endif
[57358]61
[95886]62
63
[57358]64/*********************************************************************************************************************************
65* Header Files *
66*********************************************************************************************************************************/
[1]67#include <iprt/stream.h>
[25510]68#include "internal/iprt.h"
69
[103005]70#include <iprt/asm-mem.h>
[25510]71#include <iprt/asm.h>
[32464]72#ifndef HAVE_FWRITE_UNLOCKED
73# include <iprt/critsect.h>
74#endif
[1]75#include <iprt/string.h>
76#include <iprt/assert.h>
[95586]77#include <iprt/ctype.h>
[1]78#include <iprt/err.h>
[95977]79# include <iprt/file.h>
[95886]80#ifdef RTSTREAM_STANDALONE
[95903]81# include <iprt/list.h>
[95886]82#endif
83#include <iprt/mem.h>
[95903]84#ifdef RTSTREAM_STANDALONE
85# include <iprt/once.h>
86#endif
[1]87#include <iprt/param.h>
88#include <iprt/string.h>
[20823]89
90#include "internal/alignmentchecks.h"
[1816]91#include "internal/magics.h"
[96511]92#if defined(IPRT_NO_CRT) || defined(IN_RT_STATIC)
[96442]93# include "internal/initterm.h"
94#endif
[1]95
[95894]96#ifdef RTSTREAM_STANDALONE
97# ifdef _MSC_VER
98# define IPRT_COMPILER_VCC_WITH_C_INIT_TERM_SECTIONS
99# include "internal/compiler-vcc.h"
100# endif
101#else
[95886]102# include <stdio.h>
103# include <errno.h>
104# if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
105# include <io.h>
106# include <fcntl.h>
107# endif
[40304]108#endif
109#ifdef RT_OS_WINDOWS
[76409]110# include <iprt/utf16.h>
[62592]111# include <iprt/win/windows.h>
[95886]112#elif !defined(RTSTREAM_STANDALONE)
[54724]113# include <termios.h>
114# include <unistd.h>
[68837]115# include <sys/ioctl.h>
[54724]116#endif
[1]117
[95886]118#if defined(RT_OS_OS2) && !defined(RTSTREAM_STANDALONE)
[40304]119# define _O_TEXT O_TEXT
120# define _O_BINARY O_BINARY
121#endif
[1]122
[40304]123
[57358]124/*********************************************************************************************************************************
125* Structures and Typedefs *
126*********************************************************************************************************************************/
[95886]127#ifdef RTSTREAM_STANDALONE
128/** The buffer direction. */
129typedef enum RTSTREAMBUFDIR
130{
131 RTSTREAMBUFDIR_NONE = 0,
132 RTSTREAMBUFDIR_READ,
133 RTSTREAMBUFDIR_WRITE
134} RTSTREAMBUFDIR;
135
136/** The buffer style. */
137typedef enum RTSTREAMBUFSTYLE
138{
139 RTSTREAMBUFSTYLE_UNBUFFERED = 0,
140 RTSTREAMBUFSTYLE_LINE,
141 RTSTREAMBUFSTYLE_FULL
142} RTSTREAMBUFSTYLE;
143
144#endif
145
[1]146/**
147 * File stream.
148 */
149typedef struct RTSTREAM
150{
151 /** Magic value used to validate the stream. (RTSTREAM_MAGIC) */
152 uint32_t u32Magic;
153 /** File stream error. */
154 int32_t volatile i32Error;
[95886]155#ifndef RTSTREAM_STANDALONE
[1]156 /** Pointer to the LIBC file stream. */
[25510]157 FILE *pFile;
[95886]158#else
159 /** Indicates which standard handle this is supposed to be.
160 * Set to RTHANDLESTD_INVALID if not one of the tree standard streams. */
161 RTHANDLESTD enmStdHandle;
162 /** The IPRT handle backing this stream.
163 * This is initialized lazily using enmStdHandle for the three standard
164 * streams. */
165 RTFILE hFile;
166 /** Buffer. */
167 char *pchBuf;
168 /** Buffer allocation size. */
169 size_t cbBufAlloc;
170 /** Offset of the first valid byte in the buffer. */
171 size_t offBufFirst;
172 /** Offset of the end of valid bytes in the buffer (exclusive). */
173 size_t offBufEnd;
174 /** The stream buffer direction. */
175 RTSTREAMBUFDIR enmBufDir;
[96089]176 /** The buffering style (unbuffered, line, full).
177 * @todo replace by RTSTRMBUFMODE. */
[95886]178 RTSTREAMBUFSTYLE enmBufStyle;
179# ifdef RTSTREAM_WITH_TEXT_MODE
[96089]180 /** Bitmap running parallel to each char pchBuf, indicating where a '\\r'
181 * character have been removed during buffer filling. This is used to implement
182 * RTStrmTell in non-binary mode. */
183 uint32_t *pbmBuf;
[95886]184 /** Indicates that we've got a CR ('\\r') beyond the end of official buffer
185 * and need to check if there is a LF following it. This member is ignored
186 * in binary mode. */
187 bool fPendingCr;
188# endif
189#endif
[38658]190 /** Stream is using the current process code set. */
191 bool fCurrentCodeSet;
192 /** Whether the stream was opened in binary mode. */
193 bool fBinary;
[57944]194 /** Whether to recheck the stream mode before writing. */
[38658]195 bool fRecheckMode;
[95886]196#if !defined(HAVE_FWRITE_UNLOCKED) || defined(RTSTREAM_STANDALONE)
[25510]197 /** Critical section for serializing access to the stream. */
198 PRTCRITSECT pCritSect;
199#endif
[95903]200#ifdef RTSTREAM_STANDALONE
201 /** Entry in g_StreamList (for automatic flushing and closing at
202 * exit/unload). */
203 RTLISTNODE ListEntry;
204#endif
[1]205} RTSTREAM;
206
207
[95586]208/**
209 * State for wrapped output (RTStrmWrappedPrintf, RTStrmWrappedPrintfV).
210 */
211typedef struct RTSTRMWRAPPEDSTATE
212{
213 PRTSTREAM pStream; /**< The output stream. */
214 uint32_t cchWidth; /**< The line width. */
215 uint32_t cchLine; /**< The current line length (valid chars in szLine). */
216 uint32_t cLines; /**< Number of lines written. */
217 uint32_t cchIndent; /**< The indent (determined from the first line). */
218 int rcStatus; /**< The output status. */
219 uint8_t cchHangingIndent; /**< Hanging indent (from fFlags). */
220 char szLine[0x1000+1]; /**< We must buffer output so we can do proper word splitting. */
221} RTSTRMWRAPPEDSTATE;
222
223
[57358]224/*********************************************************************************************************************************
225* Global Variables *
226*********************************************************************************************************************************/
[1]227/** The standard input stream. */
228static RTSTREAM g_StdIn =
229{
[95886]230 /* .u32Magic = */ RTSTREAM_MAGIC,
231 /* .i32Error = */ 0,
232#ifndef RTSTREAM_STANDALONE
233 /* .pFile = */ stdin,
234#else
235 /* .enmStdHandle = */ RTHANDLESTD_INPUT,
236 /* .hFile = */ NIL_RTFILE,
237 /* .pchBuf = */ NULL,
238 /* .cbBufAlloc = */ 0,
239 /* .offBufFirst = */ 0,
240 /* .offBufEnd = */ 0,
241 /* .enmBufDir = */ RTSTREAMBUFDIR_NONE,
242 /* .enmBufStyle = */ RTSTREAMBUFSTYLE_UNBUFFERED,
243# ifdef RTSTREAM_WITH_TEXT_MODE
[96089]244 /* .pbmBuf = */ NULL,
[95886]245 /* .fPendingCr = */ false,
246# endif
247#endif
248 /* .fCurrentCodeSet = */ true,
249 /* .fBinary = */ false,
250 /* .fRecheckMode = */ true,
[25510]251#ifndef HAVE_FWRITE_UNLOCKED
[95903]252 /* .pCritSect = */ NULL,
[25510]253#endif
[95903]254#ifdef RTSTREAM_STANDALONE
255 /* .ListEntry = */ { NULL, NULL },
256#endif
[1]257};
258
259/** The standard error stream. */
260static RTSTREAM g_StdErr =
261{
[95886]262 /* .u32Magic = */ RTSTREAM_MAGIC,
263 /* .i32Error = */ 0,
264#ifndef RTSTREAM_STANDALONE
265 /* .pFile = */ stderr,
266#else
267 /* .enmStdHandle = */ RTHANDLESTD_ERROR,
268 /* .hFile = */ NIL_RTFILE,
269 /* .pchBuf = */ NULL,
270 /* .cbBufAlloc = */ 0,
271 /* .offBufFirst = */ 0,
272 /* .offBufEnd = */ 0,
273 /* .enmBufDir = */ RTSTREAMBUFDIR_NONE,
274 /* .enmBufStyle = */ RTSTREAMBUFSTYLE_UNBUFFERED,
275# ifdef RTSTREAM_WITH_TEXT_MODE
[96089]276 /* .pbmBuf = */ NULL,
[95886]277 /* .fPendingCr = */ false,
278# endif
279#endif
280 /* .fCurrentCodeSet = */ true,
281 /* .fBinary = */ false,
282 /* .fRecheckMode = */ true,
[25510]283#ifndef HAVE_FWRITE_UNLOCKED
[95903]284 /* .pCritSect = */ NULL,
[25510]285#endif
[95903]286#ifdef RTSTREAM_STANDALONE
287 /* .ListEntry = */ { NULL, NULL },
288#endif
[1]289};
290
291/** The standard output stream. */
292static RTSTREAM g_StdOut =
293{
[95886]294 /* .u32Magic = */ RTSTREAM_MAGIC,
295 /* .i32Error = */ 0,
296#ifndef RTSTREAM_STANDALONE
[95943]297 /* .pFile = */ stdout,
[95886]298#else
299 /* .enmStdHandle = */ RTHANDLESTD_OUTPUT,
300 /* .hFile = */ NIL_RTFILE,
301 /* .pchBuf = */ NULL,
302 /* .cbBufAlloc = */ 0,
303 /* .offBufFirst = */ 0,
304 /* .offBufEnd = */ 0,
305 /* .enmBufDir = */ RTSTREAMBUFDIR_NONE,
306 /* .enmBufStyle = */ RTSTREAMBUFSTYLE_LINE,
307# ifdef RTSTREAM_WITH_TEXT_MODE
[96089]308 /* .pbmBuf = */ NULL,
[95886]309 /* .fPendingCr = */ false,
310# endif
311#endif
312 /* .fCurrentCodeSet = */ true,
313 /* .fBinary = */ false,
314 /* .fRecheckMode = */ true,
[25510]315#ifndef HAVE_FWRITE_UNLOCKED
[95903]316 /* .pCritSect = */ NULL,
[25510]317#endif
[95903]318#ifdef RTSTREAM_STANDALONE
319 /* .ListEntry = */ { NULL, NULL },
320#endif
[1]321};
322
323/** Pointer to the standard input stream. */
324RTDATADECL(PRTSTREAM) g_pStdIn = &g_StdIn;
325
326/** Pointer to the standard output stream. */
327RTDATADECL(PRTSTREAM) g_pStdErr = &g_StdErr;
328
329/** Pointer to the standard output stream. */
330RTDATADECL(PRTSTREAM) g_pStdOut = &g_StdOut;
331
[95903]332#ifdef RTSTREAM_STANDALONE
333/** Run-once initializer for the stream list (g_StreamList + g_StreamListCritSect). */
334static RTONCE g_StreamListOnce = RTONCE_INITIALIZER;
335/** List of user created streams (excludes the standard streams). */
336static RTLISTANCHOR g_StreamList;
337/** Critical section protecting the stream list. */
338static RTCRITSECT g_StreamListCritSect;
[1]339
[95903]340
341/** @callback_method_impl{FNRTONCE} */
342static DECLCALLBACK(int32_t) rtStrmListInitOnce(void *pvUser)
343{
344 RT_NOREF(pvUser);
345 RTListInit(&g_StreamList);
346 return RTCritSectInit(&g_StreamListCritSect);
347}
348
349#endif
350
351
[25510]352#ifndef HAVE_FWRITE_UNLOCKED
[1]353/**
[25510]354 * Allocates and acquires the lock for the stream.
355 *
[58171]356 * @returns IPRT status code.
[25510]357 * @param pStream The stream (valid).
358 */
359static int rtStrmAllocLock(PRTSTREAM pStream)
360{
361 Assert(pStream->pCritSect == NULL);
362
363 PRTCRITSECT pCritSect = (PRTCRITSECT)RTMemAlloc(sizeof(*pCritSect));
364 if (!pCritSect)
365 return VERR_NO_MEMORY;
[25685]366
367 /* The native stream lock are normally not recursive. */
[96442]368 uint32_t fFlags = RTCRITSECT_FLAGS_NO_NESTING;
[96511]369# if defined(IPRT_NO_CRT) || defined(IN_RT_STATIC)
[96442]370 /* IPRT is often used deliberatly without initialization in no-CRT
371 binaries (for instance VBoxAddInstallNt3x.exe), so in order to avoid
372 asserting in the lock validator we add the bootstrap hack that disable
[96511]373 lock validation for the section.
374 Update: Applying this to all builds involving static linking, as it's
375 now going to be used for tests running at compile-time too. */
[96442]376 if (!rtInitIsInitialized())
377 fFlags |= RTCRITSECT_FLAGS_BOOTSTRAP_HACK;
378# endif
379 int rc = RTCritSectInitEx(pCritSect, fFlags, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemSpinMutex");
[25510]380 if (RT_SUCCESS(rc))
381 {
382 rc = RTCritSectEnter(pCritSect);
383 if (RT_SUCCESS(rc))
384 {
[30111]385 if (RT_LIKELY(ASMAtomicCmpXchgPtr(&pStream->pCritSect, pCritSect, NULL)))
[25510]386 return VINF_SUCCESS;
387
388 RTCritSectLeave(pCritSect);
389 }
390 RTCritSectDelete(pCritSect);
391 }
392 RTMemFree(pCritSect);
393
394 /* Handle the lost race case... */
[30111]395 pCritSect = ASMAtomicReadPtrT(&pStream->pCritSect, PRTCRITSECT);
[25510]396 if (pCritSect)
397 return RTCritSectEnter(pCritSect);
398
399 return rc;
400}
401#endif /* !HAVE_FWRITE_UNLOCKED */
402
403
404/**
405 * Locks the stream. May have to allocate the lock as well.
406 *
407 * @param pStream The stream (valid).
408 */
409DECLINLINE(void) rtStrmLock(PRTSTREAM pStream)
410{
411#ifdef HAVE_FWRITE_UNLOCKED
412 flockfile(pStream->pFile);
413#else
414 if (RT_LIKELY(pStream->pCritSect))
415 RTCritSectEnter(pStream->pCritSect);
416 else
417 rtStrmAllocLock(pStream);
418#endif
419}
420
421
422/**
423 * Unlocks the stream.
424 *
425 * @param pStream The stream (valid).
426 */
427DECLINLINE(void) rtStrmUnlock(PRTSTREAM pStream)
428{
429#ifdef HAVE_FWRITE_UNLOCKED
430 funlockfile(pStream->pFile);
431#else
432 if (RT_LIKELY(pStream->pCritSect))
433 RTCritSectLeave(pStream->pCritSect);
434#endif
435}
436
437
438/**
[1]439 * Opens a file stream.
440 *
441 * @returns iprt status code.
[96077]442 * @param pszFilename Path to the file to open, hFile must be NIL_RTFILE.
443 * NULL if a hFile is to be used instead.
444 * @param hFile File handle to use when called from
445 * RTStrmOpenFileHandle. pszFilename must be NULL.
446 * @param pszMode See RTStrmOpen.
[1]447 * @param ppStream Where to store the opened stream.
448 */
[96077]449static int rtStrmOpenComon(const char *pszFilename, RTFILE hFile, const char *pszMode, PRTSTREAM *ppStream)
[1]450{
451 /*
[95886]452 * Validate input and look for things we care for in the pszMode string.
[1]453 */
[95886]454 AssertReturn(pszMode && *pszMode, VERR_INVALID_FLAGS);
455
[95977]456 /*
457 * Process the mode string.
458 */
459 char chMode = '\0'; /* a|r|w */
460 bool fPlus = false; /* + */
461 bool fBinary = false; /* b | !t */
462 bool fExclusive = false; /* x */
463 bool fNoInherit = false; /* e (linux, freebsd) | N (win) | E (our for reverse) */
464 const char *psz = pszMode;
465 char ch;
466 while ((ch = *psz++) != '\0')
[1]467 {
[95977]468 switch (ch)
469 {
470 case 'a':
471 case 'r':
472 case 'w':
473 chMode = ch;
474 break;
475 case '+':
476 fPlus = true;
477 break;
478 case 'b':
479 fBinary = true;
480 break;
481 case 't':
482 fBinary = false;
483 break;
484 case 'x':
485 fExclusive = true;
486 break;
487 case 'e':
488 case 'N':
489 fNoInherit = true;
490 break;
491 case 'E':
492 fNoInherit = false;
493 break;
494 default:
[95979]495 AssertMsgFailedReturn(("Invalid ch='%c' in pszMode='%s', '<a|r|w>[+][b|t][x][e|N|E]'\n", ch, pszMode),
[95977]496 VERR_INVALID_FLAGS);
497 }
498 }
[38658]499
[95977]500 /*
501 * Translate into to RTFILE_O_* flags:
502 */
503 uint64_t fOpen;
504 switch (chMode)
505 {
506 case 'a': fOpen = RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_APPEND; break;
507 case 'w': fOpen = !fExclusive
508 ? RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE
[95979]509 : RTFILE_O_CREATE | RTFILE_O_WRITE; break;
510 case 'r': fOpen = RTFILE_O_OPEN | RTFILE_O_READ; break;
[95980]511 default: AssertMsgFailedReturn(("No main mode (a|r|w) specified in '%s'!\n", pszMode), VERR_INVALID_FLAGS);
[1]512 }
[95977]513 AssertMsgReturn(!fExclusive || chMode == 'w', ("the 'x' flag is only allowed with 'w'! (%s)\n", pszMode),
514 VERR_INVALID_FLAGS);
515 if (fExclusive)
516 fOpen |= RTFILE_O_READ | RTFILE_O_WRITE;
517 if (fPlus)
518 fOpen |= RTFILE_O_READ | RTFILE_O_WRITE;
519 if (!fNoInherit)
520 fOpen |= RTFILE_O_INHERIT;
521 fOpen |= RTFILE_O_DENY_NONE;
522 fOpen |= 0666 << RTFILE_O_CREATE_MODE_SHIFT;
[1]523
[95977]524#ifndef RTSTREAM_STANDALONE
525 /*
526 * Normalize mode for fdopen.
527 */
528 char szNormalizedMode[8];
529 szNormalizedMode[0] = chMode;
530 size_t off = 1;
531 if (fPlus)
532 szNormalizedMode[off++] = '+';
533 if (fBinary)
534 szNormalizedMode[off++] = 'b';
535 szNormalizedMode[off] = '\0';
536#endif
537
[95903]538#ifdef RTSTREAM_STANDALONE
[1]539 /*
[95903]540 * Make the the stream list is initialized before we allocate anything.
541 */
542 int rc2 = RTOnce(&g_StreamListOnce, rtStrmListInitOnce, NULL);
543 AssertRCReturn(rc2, rc2);
544#endif
545
546 /*
[1]547 * Allocate the stream handle and try open it.
548 */
[95886]549 int rc = VERR_NO_MEMORY;
550 PRTSTREAM pStream = (PRTSTREAM)RTMemAllocZ(sizeof(*pStream));
[1]551 if (pStream)
552 {
[95886]553 pStream->u32Magic = RTSTREAM_MAGIC;
554#ifdef RTSTREAM_STANDALONE
555 pStream->enmStdHandle = RTHANDLESTD_INVALID;
556 pStream->hFile = NIL_RTFILE;
557 pStream->pchBuf = NULL;
558 pStream->cbBufAlloc = 0;
559 pStream->offBufFirst = 0;
560 pStream->offBufEnd = 0;
561 pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
562 pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL;
563# ifdef RTSTREAM_WITH_TEXT_MODE
[96089]564 pStream->pbmBuf = NULL;
[95886]565 pStream->fPendingCr = false,
566# endif
567#endif
568 pStream->i32Error = VINF_SUCCESS;
569 pStream->fCurrentCodeSet = false;
570 pStream->fBinary = fBinary;
571 pStream->fRecheckMode = false;
[25926]572#ifndef HAVE_FWRITE_UNLOCKED
[95886]573 pStream->pCritSect = NULL;
574#endif
[95977]575 RTFILEACTION enmActionTaken = RTFILEACTION_INVALID;
[96077]576 if (pszFilename)
577 rc = RTFileOpenEx(pszFilename, fOpen, &hFile, &enmActionTaken);
578 else
579 rc = VINF_SUCCESS;
[95886]580 if (RT_SUCCESS(rc))
[1]581 {
[95977]582#ifndef RTSTREAM_STANDALONE
583# ifndef _MSC_VER
584 int fd = (int)RTFileToNative(hFile);
585# else
586 int fd = _open_osfhandle(RTFileToNative(hFile),
587 (fPlus ? _O_RDWR : chMode == 'r' ? _O_RDONLY : _O_WRONLY)
588 | (chMode == 'a' ? _O_APPEND : 0)
589 | (fBinary ? _O_BINARY : _O_TEXT)
590 | (fNoInherit ? _O_NOINHERIT : 0));
591# endif
592 if (fd >= 0)
593 {
594 pStream->pFile = fdopen(fd, szNormalizedMode);
595 if (pStream->pFile)
596#endif
597 {
[95903]598#ifdef RTSTREAM_STANDALONE
[95977]599 pStream->hFile = hFile;
600
601 /* We keep a list of these for cleanup purposes. */
602 RTCritSectEnter(&g_StreamListCritSect);
603 RTListAppend(&g_StreamList, &pStream->ListEntry);
604 RTCritSectLeave(&g_StreamListCritSect);
[95903]605#endif
[95977]606 *ppStream = pStream;
607 return VINF_SUCCESS;
608 }
609
610 /*
611 * This better not happen too often as in 'w' mode we might've
612 * truncated a file, and in 'w' and 'a' modes there is a chance
613 * that we'll race other access to the file when deleting it.
614 */
615#ifndef RTSTREAM_STANDALONE
616 rc = RTErrConvertFromErrno(errno);
617# ifdef _MSC_VER
618 close(fd);
619 hFile = NIL_RTFILE;
[96077]620 /** @todo we're in trouble here when called from RTStrmOpenFileHandle! */
[95977]621# endif
622 }
623 else
624 {
625# ifdef _MSC_VER
626 rc = RTErrConvertFromErrno(errno);
627# else
628 AssertFailedStmt(rc = VERR_INVALID_HANDLE);
629# endif
630 }
[96077]631 if (pszFilename)
632 {
633 RTFileClose(hFile);
634 if (enmActionTaken == RTFILEACTION_CREATED)
635 RTFileDelete(pszFilename);
636 }
[95977]637#endif
[1]638 }
[38904]639 RTMemFree(pStream);
[1]640 }
[95886]641 return rc;
[1]642}
643
644
[96077]645RTR3DECL(int) RTStrmOpen(const char *pszFilename, const char *pszMode, PRTSTREAM *ppStream)
646{
647 *ppStream = NULL;
648 AssertReturn(pszFilename, VERR_INVALID_PARAMETER);
649 return rtStrmOpenComon(pszFilename, NIL_RTFILE, pszMode, ppStream);
650}
651
652
[17008]653RTR3DECL(int) RTStrmOpenFV(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, va_list args)
[1]654{
655 int rc;
656 char szFilename[RTPATH_MAX];
[17008]657 size_t cch = RTStrPrintfV(szFilename, sizeof(szFilename), pszFilenameFmt, args);
[14060]658 if (cch < sizeof(szFilename))
[1]659 rc = RTStrmOpen(szFilename, pszMode, ppStream);
660 else
661 {
662 AssertMsgFailed(("The filename is too long cch=%d\n", cch));
663 rc = VERR_FILENAME_TOO_LONG;
664 }
665 return rc;
666}
667
668
[17008]669RTR3DECL(int) RTStrmOpenF(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, ...)
[1]670{
671 va_list args;
672 va_start(args, pszFilenameFmt);
[17008]673 int rc = RTStrmOpenFV(pszMode, ppStream, pszFilenameFmt, args);
[1]674 va_end(args);
675 return rc;
676}
677
678
[96077]679RTR3DECL(int) RTStrmOpenFileHandle(RTFILE hFile, const char *pszMode, uint32_t fFlags, PRTSTREAM *ppStream)
680{
681 *ppStream = NULL;
682 AssertReturn(RTFileIsValid(hFile), VERR_INVALID_HANDLE);
683 AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
684 return rtStrmOpenComon(NULL, hFile, pszMode, ppStream);
685}
686
687
[1]688RTR3DECL(int) RTStrmClose(PRTSTREAM pStream)
689{
[95886]690 /*
691 * Validate input.
692 */
[38658]693 if (!pStream)
694 return VINF_SUCCESS;
[95886]695 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
696 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
[38658]697
[95886]698 /* We don't implement closing any of the standard handles at present. */
699 AssertReturn(pStream != &g_StdIn, VERR_NOT_SUPPORTED);
700 AssertReturn(pStream != &g_StdOut, VERR_NOT_SUPPORTED);
701 AssertReturn(pStream != &g_StdErr, VERR_NOT_SUPPORTED);
702
703 /*
704 * Invalidate the stream and destroy the critical section first.
705 */
[95903]706#ifdef RTSTREAM_STANDALONE
707 RTCritSectEnter(&g_StreamListCritSect);
708 RTListNodeRemove(&pStream->ListEntry);
709 RTCritSectLeave(&g_StreamListCritSect);
710#endif
[95886]711 pStream->u32Magic = 0xdeaddead;
712#ifndef HAVE_FWRITE_UNLOCKED
713 if (pStream->pCritSect)
[1]714 {
[95886]715 RTCritSectEnter(pStream->pCritSect);
716 RTCritSectLeave(pStream->pCritSect);
717 RTCritSectDelete(pStream->pCritSect);
718 RTMemFree(pStream->pCritSect);
719 pStream->pCritSect = NULL;
720 }
[25510]721#endif
722
[95886]723 /*
724 * Flush and close the underlying file.
725 */
726#ifdef RTSTREAM_STANDALONE
727 int const rc1 = RTStrmFlush(pStream);
728 AssertRC(rc1);
729 int const rc2 = RTFileClose(pStream->hFile);
730 AssertRC(rc2);
731 int const rc = RT_SUCCESS(rc1) ? rc2 : rc1;
732#else
733 int const rc = !fclose(pStream->pFile) ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
734#endif
735
736 /*
737 * Destroy the stream.
738 */
739#ifdef RTSTREAM_STANDALONE
740 pStream->hFile = NIL_RTFILE;
741 RTMemFree(pStream->pchBuf);
742 pStream->pchBuf = NULL;
743 pStream->cbBufAlloc = 0;
744 pStream->offBufFirst = 0;
745 pStream->offBufEnd = 0;
[96089]746# ifdef RTSTREAM_WITH_TEXT_MODE
747 RTMemFree(pStream->pbmBuf);
748 pStream->pbmBuf = NULL;
749# endif
[95886]750#else
751 pStream->pFile = NULL;
752#endif
753 RTMemFree(pStream);
754 return rc;
[1]755}
756
757
758RTR3DECL(int) RTStrmError(PRTSTREAM pStream)
759{
[95886]760 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
761 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
[38658]762 return pStream->i32Error;
[1]763}
764
765
766RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream)
767{
[95886]768 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
769 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
[38658]770
[95886]771#ifndef RTSTREAM_STANDALONE
[38658]772 clearerr(pStream->pFile);
[95886]773#endif
[38658]774 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
775 return VINF_SUCCESS;
[1]776}
777
778
[39377]779RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
780{
781 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
782 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
[39383]783 AssertReturn((unsigned)(fBinary + 1) <= 2, VERR_INVALID_PARAMETER);
784 AssertReturn((unsigned)(fCurrentCodeSet + 1) <= 2, VERR_INVALID_PARAMETER);
[39377]785
786 rtStrmLock(pStream);
787
788 if (fBinary != -1)
[39384]789 {
[39388]790 pStream->fBinary = RT_BOOL(fBinary);
[39384]791 pStream->fRecheckMode = true;
792 }
793
[39388]794 if (fCurrentCodeSet != -1)
795 pStream->fCurrentCodeSet = RT_BOOL(fCurrentCodeSet);
796
[39377]797 rtStrmUnlock(pStream);
798
799 return VINF_SUCCESS;
800}
801
[96089]802
803RTR3DECL(int) RTStrmSetBufferingMode(PRTSTREAM pStream, RTSTRMBUFMODE enmMode)
804{
805 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
806 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
807 AssertReturn(enmMode > RTSTRMBUFMODE_INVALID && enmMode < RTSTRMBUFMODE_END, VERR_INVALID_PARAMETER);
808
809#ifndef RTSTREAM_STANDALONE
810 int iCrtMode = enmMode == RTSTRMBUFMODE_FULL ? _IOFBF : enmMode == RTSTRMBUFMODE_LINE ? _IOLBF : _IONBF;
811 int rc = setvbuf(pStream->pFile, NULL, iCrtMode, 0);
812 if (rc >= 0)
813 return VINF_SUCCESS;
814 return RTErrConvertFromErrno(errno);
815
816#else
817 rtStrmLock(pStream);
818 pStream->enmBufStyle = enmMode == RTSTRMBUFMODE_FULL ? RTSTREAMBUFSTYLE_FULL
819 : enmMode == RTSTRMBUFMODE_LINE ? RTSTREAMBUFSTYLE_LINE : RTSTREAMBUFSTYLE_UNBUFFERED;
820 rtStrmUnlock(pStream);
821 return VINF_SUCCESS;
822#endif
823}
824
825
[95886]826#ifdef RTSTREAM_STANDALONE
[39377]827
[95886]828/**
829 * Deals with NIL_RTFILE in rtStrmGetFile.
830 */
831DECL_NO_INLINE(static, RTFILE) rtStrmGetFileNil(PRTSTREAM pStream)
832{
833# ifdef RT_OS_WINDOWS
834 DWORD dwStdHandle;
835 switch (pStream->enmStdHandle)
836 {
837 case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
838 case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
839 case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
840 default: return NIL_RTFILE;
841 }
842 HANDLE hHandle = GetStdHandle(dwStdHandle);
843 if (hHandle != INVALID_HANDLE_VALUE && hHandle != NULL)
844 {
845 int rc = RTFileFromNative(&pStream->hFile, (uintptr_t)hHandle);
846 if (RT_SUCCESS(rc))
847 {
848 /* Switch to full buffering if not a console handle. */
849 DWORD dwMode;
850 if (!GetConsoleMode(hHandle, &dwMode))
851 pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL;
852
853 return pStream->hFile;
854 }
855 }
856
857# else
858 uintptr_t uNative;
859 switch (pStream->enmStdHandle)
860 {
861 case RTHANDLESTD_INPUT: uNative = RTFILE_NATIVE_STDIN; break;
862 case RTHANDLESTD_OUTPUT: uNative = RTFILE_NATIVE_STDOUT; break;
863 case RTHANDLESTD_ERROR: uNative = RTFILE_NATIVE_STDERR; break;
864 default: return NIL_RTFILE;
865 }
866 int rc = RTFileFromNative(&pStream->hFile, uNative);
867 if (RT_SUCCESS(rc))
868 {
869 /* Switch to full buffering if not a console handle. */
870 if (!isatty((int)uNative))
871 pStream->enmBufStyle = RTSTREAMBUFDIR_FULL;
872
873 return pStream->hFile;
874 }
875
876# endif
877 return NIL_RTFILE;
878}
879
[96089]880
[95886]881/**
882 * For lazily resolving handles for the standard streams.
883 */
884DECLINLINE(RTFILE) rtStrmGetFile(PRTSTREAM pStream)
885{
886 RTFILE hFile = pStream->hFile;
887 if (hFile != NIL_RTFILE)
888 return hFile;
889 return rtStrmGetFileNil(pStream);
890}
891
[96089]892
893RTR3DECL(int) RTStrmQueryFileHandle(PRTSTREAM pStream, PRTFILE phFile)
894{
895 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
896 *phFile = NIL_RTFILE;
897 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
898 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
899
900 rtStrmLock(pStream);
901 RTFILE hFile = rtStrmGetFile(pStream);
902 rtStrmUnlock(pStream);
903 if (hFile != NIL_RTFILE)
904 {
905 *phFile = hFile;
906 return VINF_SUCCESS;
907 }
908 return VERR_NOT_AVAILABLE;
909}
910
[95886]911#endif /* RTSTREAM_STANDALONE */
912
913
914/**
915 * Wrapper around isatty, assumes caller takes care of stream locking/whatever
916 * is needed.
917 */
918DECLINLINE(bool) rtStrmIsTerminal(PRTSTREAM pStream)
919{
920#ifdef RTSTREAM_STANDALONE
921 RTFILE hFile = rtStrmGetFile(pStream);
922 if (hFile != NIL_RTFILE)
923 {
924 HANDLE hNative = (HANDLE)RTFileToNative(hFile);
925 DWORD dwType = GetFileType(hNative);
926 if (dwType == FILE_TYPE_CHAR)
927 {
928 DWORD dwMode;
929 if (GetConsoleMode(hNative, &dwMode))
930 return true;
931 }
932 }
933 return false;
934
935#else
936 if (pStream->pFile)
937 {
938 int fh = fileno(pStream->pFile);
939 if (isatty(fh) != 0)
940 {
941# ifdef RT_OS_WINDOWS
942 DWORD dwMode;
943 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
944 if (GetConsoleMode(hCon, &dwMode))
945 return true;
946# else
947 return true;
948# endif
949 }
950 }
951 return false;
952#endif
953}
954
955
956static int rtStrmInputGetEchoCharsNative(uintptr_t hNative, bool *pfEchoChars)
957{
958#ifdef RT_OS_WINDOWS
959 DWORD dwMode;
960 if (GetConsoleMode((HANDLE)hNative, &dwMode))
961 *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT);
962 else
963 {
964 DWORD dwErr = GetLastError();
965 if (dwErr == ERROR_INVALID_HANDLE)
966 return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE;
967 return RTErrConvertFromWin32(dwErr);
968 }
969#else
970 struct termios Termios;
[95888]971 int rcPosix = tcgetattr((int)hNative, &Termios);
[95886]972 if (!rcPosix)
973 *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);
974 else
975 return errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno);
976#endif
977 return VINF_SUCCESS;
978}
979
980
981
[54724]982RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars)
983{
984 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
985 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
986 AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER);
987
[95886]988#ifdef RTSTREAM_STANDALONE
989 return rtStrmInputGetEchoCharsNative(RTFileToNative(pStream->hFile), pfEchoChars);
990#else
991 int rc;
[54724]992 int fh = fileno(pStream->pFile);
993 if (isatty(fh))
994 {
[95886]995# ifdef RT_OS_WINDOWS
996 rc = rtStrmInputGetEchoCharsNative(_get_osfhandle(fh), pfEchoChars);
997# else
998 rc = rtStrmInputGetEchoCharsNative(fh, pfEchoChars);
999# endif
1000 }
1001 else
1002 rc = VERR_INVALID_FUNCTION;
1003 return rc;
1004#endif
1005}
1006
1007
1008static int rtStrmInputSetEchoCharsNative(uintptr_t hNative, bool fEchoChars)
1009{
1010 int rc;
[54724]1011#ifdef RT_OS_WINDOWS
[95886]1012 DWORD dwMode;
1013 if (GetConsoleMode((HANDLE)hNative, &dwMode))
1014 {
1015 if (fEchoChars)
1016 dwMode |= ENABLE_ECHO_INPUT;
[54724]1017 else
[95886]1018 dwMode &= ~ENABLE_ECHO_INPUT;
1019 if (SetConsoleMode((HANDLE)hNative, dwMode))
1020 rc = VINF_SUCCESS;
1021 else
[54724]1022 rc = RTErrConvertFromWin32(GetLastError());
[95886]1023 }
1024 else
1025 {
1026 DWORD dwErr = GetLastError();
1027 if (dwErr == ERROR_INVALID_HANDLE)
1028 return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE;
1029 return RTErrConvertFromWin32(dwErr);
1030 }
[54724]1031#else
[95886]1032 struct termios Termios;
[95888]1033 int rcPosix = tcgetattr((int)hNative, &Termios);
[95886]1034 if (!rcPosix)
1035 {
1036 if (fEchoChars)
1037 Termios.c_lflag |= ECHO;
1038 else
1039 Termios.c_lflag &= ~ECHO;
[54724]1040
[95888]1041 rcPosix = tcsetattr((int)hNative, TCSAFLUSH, &Termios);
[95886]1042 if (rcPosix == 0)
1043 rc = VINF_SUCCESS;
[54724]1044 else
1045 rc = RTErrConvertFromErrno(errno);
1046 }
1047 else
[95886]1048 rc = errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno);
1049#endif
[54724]1050 return rc;
1051}
1052
1053
1054RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)
1055{
1056 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
1057 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
1058
[95886]1059#ifdef RTSTREAM_STANDALONE
1060 return rtStrmInputSetEchoCharsNative(RTFileToNative(pStream->hFile), fEchoChars);
1061#else
1062 int rc;
[54724]1063 int fh = fileno(pStream->pFile);
1064 if (isatty(fh))
1065 {
[95886]1066# ifdef RT_OS_WINDOWS
1067 rc = rtStrmInputSetEchoCharsNative(_get_osfhandle(fh), fEchoChars);
1068# else
1069 rc = rtStrmInputSetEchoCharsNative(fh, fEchoChars);
1070# endif
1071 }
1072 else
1073 rc = VERR_INVALID_FUNCTION;
1074 return rc;
1075#endif
1076}
1077
1078
1079RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)
1080{
1081 AssertPtrReturn(pStream, false);
1082 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);
1083
1084 return rtStrmIsTerminal(pStream);
1085}
1086
1087
1088RTR3DECL(int) RTStrmQueryTerminalWidth(PRTSTREAM pStream, uint32_t *pcchWidth)
1089{
1090 AssertPtrReturn(pcchWidth, VERR_INVALID_HANDLE);
1091 *pcchWidth = 80;
1092
1093 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
1094 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
1095
1096 if (rtStrmIsTerminal(pStream))
1097 {
[54724]1098#ifdef RT_OS_WINDOWS
[95886]1099# ifdef RTSTREAM_STANDALONE
1100 HANDLE hCon = (HANDLE)RTFileToNative(pStream->hFile);
1101# else
1102 HANDLE hCon = (HANDLE)_get_osfhandle(fileno(pStream->pFile));
1103# endif
1104 CONSOLE_SCREEN_BUFFER_INFO Info;
1105 RT_ZERO(Info);
1106 if (GetConsoleScreenBufferInfo(hCon, &Info))
[54724]1107 {
[95886]1108 *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;
1109 return VINF_SUCCESS;
[54724]1110 }
[95886]1111 return RTErrConvertFromWin32(GetLastError());
1112
1113#elif defined(RT_OS_OS2) && !defined(TIOCGWINSZ) /* only OS/2 should currently miss this */
1114 return VINF_SUCCESS; /* just pretend for now. */
1115
[54724]1116#else
[95886]1117 struct winsize Info;
1118 RT_ZERO(Info);
1119 int rc = ioctl(fileno(pStream->pFile), TIOCGWINSZ, &Info);
1120 if (rc >= 0)
[54724]1121 {
[95886]1122 *pcchWidth = Info.ws_col ? Info.ws_col : 80;
1123 return VINF_SUCCESS;
[54724]1124 }
[95886]1125 return RTErrConvertFromErrno(errno);
[54724]1126#endif
1127 }
[95886]1128 return VERR_INVALID_FUNCTION;
1129}
[54724]1130
[95886]1131
1132#ifdef RTSTREAM_STANDALONE
1133
1134DECLINLINE(void) rtStrmBufInvalidate(PRTSTREAM pStream)
1135{
1136 pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
1137 pStream->offBufEnd = 0;
1138 pStream->offBufFirst = 0;
[54724]1139}
1140
1141
[95886]1142static int rtStrmBufFlushWrite(PRTSTREAM pStream, size_t cbToFlush)
[68836]1143{
[95886]1144 Assert(cbToFlush <= pStream->offBufEnd - pStream->offBufFirst);
[68836]1145
[96710]1146 RTFILE const hFile = rtStrmGetFile(pStream);
1147 if (hFile != NIL_RTFILE)
[68836]1148 {
[96710]1149 /** @todo do nonblocking & incomplete writes? */
1150 size_t offBufFirst = pStream->offBufFirst;
1151 int rc = RTFileWrite(hFile, &pStream->pchBuf[offBufFirst], cbToFlush, NULL);
1152 if (RT_SUCCESS(rc))
[68836]1153 {
[96710]1154 offBufFirst += cbToFlush;
1155 if (offBufFirst >= pStream->offBufEnd)
1156 pStream->offBufEnd = 0;
1157 else
1158 {
1159 /* Shift up the remaining content so the next write can take full
1160 advantage of the buffer size. */
1161 size_t cbLeft = pStream->offBufEnd - offBufFirst;
1162 memmove(pStream->pchBuf, &pStream->pchBuf[offBufFirst], cbLeft);
1163 pStream->offBufEnd = cbLeft;
1164 }
1165 pStream->offBufFirst = 0;
1166 return VINF_SUCCESS;
[68836]1167 }
[96710]1168 return rc;
[68836]1169 }
[96710]1170 return VERR_INVALID_HANDLE;
[68836]1171}
1172
1173
[95886]1174static int rtStrmBufFlushWriteMaybe(PRTSTREAM pStream, bool fInvalidate)
[68836]1175{
[95886]1176 if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE)
1177 {
1178 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
1179 if (cbInBuffer > 0)
1180 {
1181 int rc = rtStrmBufFlushWrite(pStream, cbInBuffer);
1182 if (fInvalidate)
1183 pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
1184 return rc;
1185 }
1186 }
1187 if (fInvalidate)
1188 rtStrmBufInvalidate(pStream);
1189 return VINF_SUCCESS;
1190}
[68836]1191
1192
[95886]1193/**
1194 * Worker for rtStrmBufCheckErrorAndSwitchToReadMode and
1195 * rtStrmBufCheckErrorAndSwitchToWriteMode that allocates a buffer.
1196 *
1197 * Only updates cbBufAlloc and pchBuf, callers deals with error fallout.
1198 */
1199static int rtStrmBufAlloc(PRTSTREAM pStream)
1200{
1201 size_t cbBuf = pStream->enmBufStyle == RTSTREAMBUFSTYLE_FULL ? _64K : _16K;
1202 do
[68836]1203 {
[95886]1204 pStream->pchBuf = (char *)RTMemAllocZ(cbBuf);
1205 if (RT_LIKELY(pStream->pchBuf))
[68836]1206 {
[96089]1207# ifdef RTSTREAM_WITH_TEXT_MODE
1208 Assert(RT_ALIGN_Z(cbBuf, 64 / 8) == cbBuf);
1209 pStream->pbmBuf = (uint32_t *)RTMemAllocZ(cbBuf / 8);
1210 if (RT_LIKELY(pStream->pbmBuf))
1211# endif
1212 {
1213 pStream->cbBufAlloc = cbBuf;
1214 return VINF_SUCCESS;
1215 }
1216# ifdef RTSTREAM_WITH_TEXT_MODE
1217 RTMemFree(pStream->pchBuf);
1218 pStream->pchBuf = NULL;
1219# endif
[95886]1220 }
1221 cbBuf /= 2;
1222 } while (cbBuf >= 256);
1223 return VERR_NO_MEMORY;
1224}
1225
1226
1227/**
1228 * Checks the stream error status, flushed any pending writes, ensures there is
1229 * a buffer allocated and switches the stream to the read direction.
1230 *
1231 * @returns IPRT status code (same as i32Error).
1232 * @param pStream The stream.
1233 */
1234static int rtStrmBufCheckErrorAndSwitchToReadMode(PRTSTREAM pStream)
1235{
1236 int rc = pStream->i32Error;
1237 if (RT_SUCCESS(rc))
1238 {
1239 /*
1240 * We're very likely already in read mode and can return without doing
1241 * anything here.
1242 */
1243 if (pStream->enmBufDir == RTSTREAMBUFDIR_READ)
1244 return VINF_SUCCESS;
1245
1246 /*
1247 * Flush any pending writes before switching the buffer to read:
1248 */
1249 rc = rtStrmBufFlushWriteMaybe(pStream, false /*fInvalidate*/);
1250 if (RT_SUCCESS(rc))
1251 {
1252 pStream->enmBufDir = RTSTREAMBUFDIR_READ;
1253 pStream->offBufEnd = 0;
1254 pStream->offBufFirst = 0;
1255 pStream->fPendingCr = false;
1256
1257 /*
1258 * Read direction implies a buffer, so make sure we've got one and
1259 * change to NONE direction if allocating one fails.
1260 */
1261 if (pStream->pchBuf)
[68836]1262 {
[95886]1263 Assert(pStream->cbBufAlloc >= 256);
[68836]1264 return VINF_SUCCESS;
1265 }
[68839]1266
[95886]1267 rc = rtStrmBufAlloc(pStream);
1268 if (RT_SUCCESS(rc))
1269 return VINF_SUCCESS;
1270
1271 pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
1272 }
1273 ASMAtomicWriteS32(&pStream->i32Error, rc);
1274 }
1275 return rc;
1276}
1277
1278
1279/**
1280 * Checks the stream error status, ensures there is a buffer allocated and
1281 * switches the stream to the write direction.
1282 *
1283 * @returns IPRT status code (same as i32Error).
1284 * @param pStream The stream.
1285 */
1286static int rtStrmBufCheckErrorAndSwitchToWriteMode(PRTSTREAM pStream)
1287{
1288 int rc = pStream->i32Error;
1289 if (RT_SUCCESS(rc))
1290 {
1291 /*
1292 * We're very likely already in write mode and can return without doing
1293 * anything here.
1294 */
1295 if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE)
1296 return VINF_SUCCESS;
1297
1298 /*
1299 * A read buffer does not need any flushing, so we just have to make
1300 * sure there is a buffer present before switching to the write direction.
1301 */
1302 pStream->enmBufDir = RTSTREAMBUFDIR_WRITE;
1303 pStream->offBufEnd = 0;
1304 pStream->offBufFirst = 0;
1305 if (pStream->pchBuf)
1306 {
1307 Assert(pStream->cbBufAlloc >= 256);
1308 return VINF_SUCCESS;
1309 }
1310
1311 rc = rtStrmBufAlloc(pStream);
1312 if (RT_SUCCESS(rc))
1313 return VINF_SUCCESS;
1314
1315 pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
1316 ASMAtomicWriteS32(&pStream->i32Error, rc);
1317 }
1318 return rc;
1319}
1320
1321
1322/**
1323 * Reads more bytes into the buffer.
1324 *
1325 * @returns IPRT status code (same as i32Error).
1326 * @param pStream The stream.
1327 */
1328static int rtStrmBufFill(PRTSTREAM pStream)
1329{
1330 /*
1331 * Check preconditions
1332 */
1333 Assert(pStream->i32Error == VINF_SUCCESS);
1334 Assert(pStream->enmBufDir == RTSTREAMBUFDIR_READ);
1335 AssertPtr(pStream->pchBuf);
1336 Assert(pStream->cbBufAlloc >= 256);
[96089]1337 Assert(RT_ALIGN_Z(pStream->cbBufAlloc, 64) == pStream->cbBufAlloc);
[95886]1338 Assert(pStream->offBufFirst <= pStream->cbBufAlloc);
1339 Assert(pStream->offBufEnd <= pStream->cbBufAlloc);
1340 Assert(pStream->offBufFirst <= pStream->offBufEnd);
[96089]1341# ifdef RTSTREAM_WITH_TEXT_MODE
1342 AssertPtr(pStream->pbmBuf);
1343# endif
[95886]1344 /*
1345 * If there is data in the buffer, move it up to the start.
1346 */
1347 size_t cbInBuffer;
1348 if (!pStream->offBufFirst)
1349 cbInBuffer = pStream->offBufEnd;
1350 else
1351 {
1352 cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
1353 if (cbInBuffer)
[96089]1354 {
[95886]1355 memmove(pStream->pchBuf, &pStream->pchBuf[pStream->offBufFirst], cbInBuffer);
[96089]1356# ifdef RTSTREAM_WITH_TEXT_MODE
1357 if (!pStream->fBinary) /** @todo this isn't very efficient, must be a better way of shifting a bitmap. */
1358 for (size_t off = 0; off < pStream->offBufFirst; off++)
1359 if (ASMBitTest(pStream->pbmBuf, (int32_t)off))
1360 ASMBitSet(pStream->pbmBuf, (int32_t)off);
1361 else
1362 ASMBitClear(pStream->pbmBuf, (int32_t)off);
1363# endif
1364 }
[95886]1365 pStream->offBufFirst = 0;
1366 pStream->offBufEnd = cbInBuffer;
1367 }
1368
1369 /*
1370 * Add pending CR to the buffer.
1371 */
1372 size_t const offCrLfConvStart = cbInBuffer;
1373 Assert(cbInBuffer + 2 <= pStream->cbBufAlloc);
1374 if (!pStream->fPendingCr || pStream->fBinary)
1375 { /* likely */ }
1376 else
1377 {
1378 pStream->pchBuf[cbInBuffer] = '\r';
1379 pStream->fPendingCr = false;
1380 pStream->offBufEnd = ++cbInBuffer;
1381 }
1382
1383 /*
1384 * Read data till the buffer is full.
1385 */
[96710]1386 int rc = VERR_INVALID_HANDLE;
1387 RTFILE const hFile = rtStrmGetFile(pStream);
1388 if (hFile != NIL_RTFILE)
[95886]1389 {
[96710]1390 size_t cbRead = 0;
1391 rc = RTFileRead(hFile, &pStream->pchBuf[cbInBuffer], pStream->cbBufAlloc - cbInBuffer, &cbRead);
1392 if (RT_SUCCESS(rc))
1393 {
1394 cbInBuffer += cbRead;
1395 pStream->offBufEnd = cbInBuffer;
[95886]1396
[96710]1397 if (cbInBuffer != 0)
1398 {
[96089]1399# ifdef RTSTREAM_WITH_TEXT_MODE
[95886]1400 if (pStream->fBinary)
[96089]1401# endif
[95886]1402 return VINF_SUCCESS;
[96710]1403 }
1404 else
1405 {
1406 /** @todo this shouldn't be sticky, should it? */
1407 ASMAtomicWriteS32(&pStream->i32Error, VERR_EOF);
1408 return VERR_EOF;
1409 }
[95886]1410
[96089]1411# ifdef RTSTREAM_WITH_TEXT_MODE
[96710]1412 /*
1413 * Do CRLF -> LF conversion in the buffer.
1414 */
1415 ASMBitClearRange(pStream->pbmBuf, offCrLfConvStart, RT_ALIGN_Z(cbInBuffer, 64));
1416 char *pchCur = &pStream->pchBuf[offCrLfConvStart];
1417 size_t cbLeft = cbInBuffer - offCrLfConvStart;
1418 while (cbLeft > 0)
[68836]1419 {
[96710]1420 Assert(&pchCur[cbLeft] == &pStream->pchBuf[pStream->offBufEnd]);
1421 char *pchCr = (char *)memchr(pchCur, '\r', cbLeft);
1422 if (pchCr)
[95886]1423 {
[96710]1424 size_t offCur = (size_t)(pchCr - pchCur);
1425 if (offCur + 1 < cbLeft)
[95886]1426 {
[96710]1427 if (pchCr[1] == '\n')
1428 {
1429 /* Found one '\r\n' sequence. Look for more before shifting the buffer content. */
1430 cbLeft -= offCur;
1431 pchCur = pchCr;
[95886]1432
[96710]1433 do
1434 {
1435 ASMBitSet(pStream->pbmBuf, (int32_t)(pchCur - pStream->pchBuf));
1436 *pchCur++ = '\n'; /* dst */
1437 cbLeft -= 2;
1438 pchCr += 2; /* src */
1439 } while (cbLeft >= 2 && pchCr[0] == '\r' && pchCr[1] == '\n');
1440
1441 memmove(&pchCur, pchCr, cbLeft);
1442 }
1443 else
[95886]1444 {
[96710]1445 cbLeft -= offCur + 1;
1446 pchCur = pchCr + 1;
1447 }
[95886]1448 }
1449 else
1450 {
[96710]1451 Assert(pchCr == &pStream->pchBuf[pStream->offBufEnd - 1]);
1452 pStream->fPendingCr = true;
1453 pStream->offBufEnd = --cbInBuffer;
1454 break;
[95886]1455 }
1456 }
1457 else
1458 break;
[68836]1459 }
[95886]1460
[96710]1461 return VINF_SUCCESS;
[96089]1462# endif
[96710]1463 }
[68836]1464 }
[95886]1465
1466 /*
1467 * If there is data in the buffer, don't raise the error till it has all
1468 * been consumed, ASSUMING that another fill call will follow and that the
1469 * error condition will reoccur then.
1470 *
1471 * Note! We may currently end up not converting a CRLF pair, if it's
1472 * split over a temporary EOF condition, since we forces the caller
1473 * to read the CR before requesting more data. However, it's not a
1474 * very likely scenario, so we'll just leave it like that for now.
1475 */
1476 if (cbInBuffer)
1477 return VINF_SUCCESS;
1478 ASMAtomicWriteS32(&pStream->i32Error, rc);
1479 return rc;
[68836]1480}
1481
1482
[95886]1483/**
1484 * Copies @a cbSrc bytes from @a pvSrc and into the buffer, flushing as needed
1485 * to make space available.
1486 *
1487 *
1488 * @returns IPRT status code (errors not assigned to i32Error).
1489 * @param pStream The stream.
1490 * @param pvSrc The source buffer.
1491 * @param cbSrc Number of bytes to copy from @a pvSrc.
1492 * @param pcbTotal A total counter to update with what was copied.
1493 */
1494static int rtStrmBufCopyTo(PRTSTREAM pStream, const void *pvSrc, size_t cbSrc, size_t *pcbTotal)
1495{
1496 Assert(cbSrc > 0);
1497 for (;;)
1498 {
1499 size_t cbToCopy = RT_MIN(pStream->cbBufAlloc - pStream->offBufEnd, cbSrc);
1500 if (cbToCopy)
1501 {
1502 memcpy(&pStream->pchBuf[pStream->offBufEnd], pvSrc, cbToCopy);
1503 pStream->offBufEnd += cbToCopy;
1504 pvSrc = (const char *)pvSrc + cbToCopy;
1505 *pcbTotal += cbToCopy;
1506 cbSrc -= cbToCopy;
1507 if (!cbSrc)
1508 break;
1509 }
[68836]1510
[95886]1511 int rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
1512 if (RT_FAILURE(rc))
1513 return rc;
1514 }
1515 return VINF_SUCCESS;
1516}
1517
[95894]1518
1519/**
[95903]1520 * Worker for rtStrmFlushAndCloseAll and rtStrmFlushAndClose.
[95894]1521 */
[95903]1522static RTFILE rtStrmFlushAndCleanup(PRTSTREAM pStream)
[95894]1523{
1524 if (pStream->pchBuf)
1525 {
1526 if ( pStream->enmBufDir == RTSTREAMBUFDIR_WRITE
1527 && pStream->offBufFirst < pStream->offBufEnd
1528 && RT_SUCCESS(pStream->i32Error) )
1529 rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
1530 RTMemFree(pStream->pchBuf);
1531 pStream->pchBuf = NULL;
1532 pStream->offBufFirst = 0;
1533 pStream->offBufEnd = 0;
[96089]1534# ifdef RTSTREAM_WITH_TEXT_MODE
1535 RTMemFree(pStream->pbmBuf);
1536 pStream->pbmBuf = NULL;
1537# endif
[95894]1538 }
1539
1540 PRTCRITSECT pCritSect = pStream->pCritSect;
1541 if (pCritSect)
1542 {
[95903]1543 pStream->pCritSect = NULL;
[95894]1544 RTCritSectDelete(pCritSect);
1545 RTMemFree(pCritSect);
1546 }
1547
[95903]1548 RTFILE hFile = pStream->hFile;
1549 pStream->hFile = NIL_RTFILE;
1550 return hFile;
1551}
[95894]1552
[95903]1553
1554/**
1555 * Worker for rtStrmFlushAndCloseAll.
1556 */
1557static void rtStrmFlushAndClose(PRTSTREAM pStream)
1558{
1559 pStream->u32Magic = ~RTSTREAM_MAGIC;
1560 RTFILE hFile = rtStrmFlushAndCleanup(pStream);
1561 if (hFile != NIL_RTFILE)
1562 RTFileClose(hFile);
1563 RTMemFree(pStream);
[95894]1564}
1565
1566
1567/**
1568 * Flushes and cleans up the standard streams, should flush and close all others
1569 * too but doesn't yet...
1570 */
1571DECLCALLBACK(void) rtStrmFlushAndCloseAll(void)
1572{
1573 /*
1574 * Flush the standard handles.
1575 */
[95903]1576 rtStrmFlushAndCleanup(&g_StdOut);
1577 rtStrmFlushAndCleanup(&g_StdErr);
1578 rtStrmFlushAndCleanup(&g_StdIn);
[95894]1579
1580 /*
1581 * Make a list of the rest and flush+close those too.
1582 */
[95903]1583 if (RTOnceWasInitialized(&g_StreamListOnce))
1584 {
1585 RTCritSectDelete(&g_StreamListCritSect);
1586
1587 PRTSTREAM pStream;
1588 while ((pStream = RTListRemoveFirst(&g_StreamList, RTSTREAM, ListEntry)) != NULL)
1589 rtStrmFlushAndClose(pStream);
1590
1591 RTOnceReset(&g_StreamListOnce);
1592 }
[95894]1593}
1594
1595# ifdef IPRT_COMPILER_TERM_CALLBACK
1596IPRT_COMPILER_TERM_CALLBACK(rtStrmFlushAndCloseAll);
1597# endif
1598
[95886]1599#endif /* RTSTREAM_STANDALONE */
1600
1601
[21045]1602RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
1603{
1604 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
1605 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
1606
[95886]1607#ifdef RTSTREAM_STANDALONE
1608 rtStrmLock(pStream);
1609 int const rc1 = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
1610 int const rc2 = RTFileSeek(rtStrmGetFile(pStream), 0, RTFILE_SEEK_BEGIN, NULL);
1611 int rc = RT_SUCCESS(rc1) ? rc2 : rc1;
1612 ASMAtomicWriteS32(&pStream->i32Error, rc);
1613 rtStrmUnlock(pStream);
1614#else
[21045]1615 clearerr(pStream->pFile);
1616 errno = 0;
[95886]1617 int rc;
[21045]1618 if (!fseek(pStream->pFile, 0, SEEK_SET))
1619 rc = VINF_SUCCESS;
1620 else
1621 rc = RTErrConvertFromErrno(errno);
[95886]1622 ASMAtomicWriteS32(&pStream->i32Error, rc);
1623#endif
[21045]1624 return rc;
1625}
1626
1627
[96089]1628RTR3DECL(int) RTStrmSeek(PRTSTREAM pStream, RTFOFF off, uint32_t uMethod)
1629{
1630 AssertReturn(uMethod <= RTFILE_SEEK_END, VERR_INVALID_PARAMETER);
1631#ifdef RTSTREAM_STANDALONE
1632 rtStrmLock(pStream);
1633 int rc = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
1634 if (RT_SUCCESS(rc))
1635 rc = RTFileSeek(rtStrmGetFile(pStream), off, uMethod, NULL);
1636 if (RT_FAILURE(rc))
1637 ASMAtomicWriteS32(&pStream->i32Error, rc);
1638 rtStrmUnlock(pStream);
1639#else
1640 int const iCrtMethod = uMethod == RTFILE_SEEK_BEGIN ? SEEK_SET : uMethod == RTFILE_SEEK_CURRENT ? SEEK_CUR : SEEK_END;
1641 errno = 0;
1642 int rc;
1643# ifdef _MSC_VER
1644 if (!_fseeki64(pStream->pFile, off, iCrtMethod))
1645# else
1646 if (!fseeko(pStream->pFile, off, iCrtMethod))
1647# endif
1648 rc = VINF_SUCCESS;
1649 else
1650 rc = RTErrConvertFromErrno(errno);
1651 ASMAtomicWriteS32(&pStream->i32Error, rc);
1652#endif
1653 return rc;
1654}
1655
1656
1657RTR3DECL(RTFOFF) RTStrmTell(PRTSTREAM pStream)
1658{
1659#ifdef RTSTREAM_STANDALONE
1660 uint64_t off = 0;
1661 rtStrmLock(pStream);
1662 int rc = pStream->i32Error;
1663 if (RT_SUCCESS(rc))
1664 {
[96710]1665 RTFILE const hFile = rtStrmGetFile(pStream);
1666 if (hFile != NIL_RTFILE)
[96089]1667 {
[96710]1668 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &off);
1669 if (RT_SUCCESS(rc))
[96089]1670 {
[96710]1671 switch (pStream->enmBufDir)
1672 {
1673 case RTSTREAMBUFDIR_READ:
1674 /* Subtract unconsumed chars and removed '\r' characters. */
1675 off -= pStream->offBufEnd - pStream->offBufFirst;
1676 if (!pStream->fBinary)
1677 for (size_t offBuf = pStream->offBufFirst; offBuf < pStream->offBufEnd; offBuf++)
1678 off -= ASMBitTest(pStream->pbmBuf, (int32_t)offBuf);
1679 break;
1680 case RTSTREAMBUFDIR_WRITE:
1681 /* Add unwrittend chars in the buffer. */
1682 off += pStream->offBufEnd - pStream->offBufFirst;
1683 break;
1684 default:
1685 AssertFailed();
1686 case RTSTREAMBUFDIR_NONE:
1687 break;
1688 }
[96089]1689 }
1690 }
[96710]1691 else
1692 rc = VERR_INVALID_HANDLE;
[96089]1693 }
1694 if (RT_FAILURE(rc))
1695 {
1696 ASMAtomicWriteS32(&pStream->i32Error, rc);
1697 off = rc;
1698 }
1699 rtStrmUnlock(pStream);
1700#else
1701# ifdef _MSC_VER
1702 RTFOFF off = _ftelli64(pStream->pFile);
1703# else
1704 RTFOFF off = ftello(pStream->pFile);
1705# endif
1706 if (off < 0)
1707 {
1708 int rc = RTErrConvertFromErrno(errno);
1709 ASMAtomicWriteS32(&pStream->i32Error, rc);
1710 off = rc;
1711 }
1712#endif
1713 return off;
1714}
1715
1716
1717/**
[39327]1718 * Recheck the stream mode.
1719 *
[38658]1720 * @param pStream The stream (locked).
1721 */
1722static void rtStreamRecheckMode(PRTSTREAM pStream)
1723{
[95886]1724#if (defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)) && !defined(RTSTREAM_STANDALONE)
[38658]1725 int fh = fileno(pStream->pFile);
1726 if (fh >= 0)
1727 {
1728 int fExpected = pStream->fBinary ? _O_BINARY : _O_TEXT;
1729 int fActual = _setmode(fh, fExpected);
1730 if (fActual != -1 && fExpected != (fActual & (_O_BINARY | _O_TEXT)))
1731 {
[39384]1732 fActual = _setmode(fh, fActual & (_O_BINARY | _O_TEXT));
[38658]1733 pStream->fBinary = !(fActual & _O_TEXT);
1734 }
1735 }
1736#else
1737 NOREF(pStream);
1738#endif
1739 pStream->fRecheckMode = false;
1740}
1741
1742
[95886]1743RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
[1]1744{
[95886]1745 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
1746 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
[38658]1747
[95886]1748#ifdef RTSTREAM_STANDALONE
1749 rtStrmLock(pStream);
1750 int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream);
1751#else
[38658]1752 int rc = pStream->i32Error;
[95886]1753#endif
[38658]1754 if (RT_SUCCESS(rc))
[1]1755 {
[38658]1756 if (pStream->fRecheckMode)
1757 rtStreamRecheckMode(pStream);
1758
[95886]1759#ifdef RTSTREAM_STANDALONE
1760
1761 /*
1762 * Copy data thru the read buffer for now as that'll handle both binary
1763 * and text modes seamlessly. We could optimize larger reads here when
1764 * in binary mode, that can wait till the basics work, I think.
1765 */
1766 size_t cbTotal = 0;
1767 if (cbToRead > 0)
1768 for (;;)
1769 {
1770 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
1771 if (cbInBuffer > 0)
1772 {
1773 size_t cbToCopy = RT_MIN(cbInBuffer, cbToRead);
1774 memcpy(pvBuf, &pStream->pchBuf[pStream->offBufFirst], cbToCopy);
1775 cbTotal += cbToRead;
1776 cbToRead -= cbToCopy;
1777 pvBuf = (char *)pvBuf + cbToCopy;
1778 if (!cbToRead)
1779 break;
1780 }
1781 rc = rtStrmBufFill(pStream);
1782 if (RT_SUCCESS(rc))
1783 { /* likely */ }
1784 else
1785 {
1786 if (rc == VERR_EOF && pcbRead && cbTotal > 0)
1787 rc = VINF_EOF;
1788 break;
1789 }
1790 }
[38658]1791 if (pcbRead)
[95886]1792 *pcbRead = cbTotal;
1793
1794#else /* !RTSTREAM_STANDALONE */
1795 if (pcbRead)
[1]1796 {
[38658]1797 /*
1798 * Can do with a partial read.
1799 */
[95886]1800 *pcbRead = fread(pvBuf, 1, cbToRead, pStream->pFile);
1801 if ( *pcbRead == cbToRead
[38658]1802 || !ferror(pStream->pFile))
[95886]1803 rc = VINF_SUCCESS;
1804 else if (feof(pStream->pFile))
1805 rc = *pcbRead ? VINF_EOF : VERR_EOF;
[38658]1806 else if (ferror(pStream->pFile))
1807 rc = VERR_READ_ERROR;
[1]1808 else
1809 {
[38658]1810 AssertMsgFailed(("This shouldn't happen\n"));
1811 rc = VERR_INTERNAL_ERROR;
1812 }
1813 }
1814 else
1815 {
1816 /*
1817 * Must read it all!
1818 */
[95886]1819 if (fread(pvBuf, cbToRead, 1, pStream->pFile) == 1)
1820 rc = VINF_SUCCESS;
[38658]1821 /* possible error/eof. */
[95886]1822 else if (feof(pStream->pFile))
[38658]1823 rc = VERR_EOF;
1824 else if (ferror(pStream->pFile))
1825 rc = VERR_READ_ERROR;
1826 else
1827 {
1828 AssertMsgFailed(("This shouldn't happen\n"));
1829 rc = VERR_INTERNAL_ERROR;
[1]1830 }
1831 }
[95886]1832#endif /* !RTSTREAM_STANDALONE */
1833 if (RT_FAILURE(rc))
1834 ASMAtomicWriteS32(&pStream->i32Error, rc);
[1]1835 }
[95886]1836#ifdef RTSTREAM_STANDALONE
1837 rtStrmUnlock(pStream);
1838#endif
[38658]1839 return rc;
1840}
1841
1842
1843/**
[39327]1844 * Check if the input text is valid UTF-8.
1845 *
[38658]1846 * @returns true/false.
1847 * @param pvBuf Pointer to the buffer.
1848 * @param cbBuf Size of the buffer.
1849 */
1850static bool rtStrmIsUtf8Text(const void *pvBuf, size_t cbBuf)
1851{
1852 NOREF(pvBuf);
1853 NOREF(cbBuf);
1854 /** @todo not sure this is a good idea... Better redefine RTStrmWrite. */
1855 return false;
1856}
1857
1858
[95886]1859#if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE)
1860
[38658]1861/**
[39327]1862 * Check if the stream is for a Window console.
1863 *
[38658]1864 * @returns true / false.
1865 * @param pStream The stream.
1866 * @param phCon Where to return the console handle.
1867 */
1868static bool rtStrmIsConsoleUnlocked(PRTSTREAM pStream, HANDLE *phCon)
1869{
1870 int fh = fileno(pStream->pFile);
1871 if (isatty(fh))
[1]1872 {
[38658]1873 DWORD dwMode;
1874 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
1875 if (GetConsoleMode(hCon, &dwMode))
1876 {
1877 *phCon = hCon;
1878 return true;
1879 }
[1]1880 }
[38658]1881 return false;
[1]1882}
[95886]1883
1884
1885static int rtStrmWriteWinConsoleLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, HANDLE hCon)
1886{
1887 int rc;
1888# ifdef HAVE_FWRITE_UNLOCKED
1889 if (!fflush_unlocked(pStream->pFile))
1890# else
1891 if (!fflush(pStream->pFile))
1892# endif
1893 {
1894 /** @todo Consider buffering later. For now, we'd rather correct output than
1895 * fast output. */
1896 DWORD cwcWritten = 0;
1897 PRTUTF16 pwszSrc = NULL;
1898 size_t cwcSrc = 0;
1899 rc = RTStrToUtf16Ex((const char *)pvBuf, cbToWrite, &pwszSrc, 0, &cwcSrc);
[96234]1900 AssertRC(rc);
[95886]1901 if (RT_SUCCESS(rc))
1902 {
1903 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
1904 {
1905 /* try write char-by-char to avoid heap problem. */
1906 cwcWritten = 0;
1907 while (cwcWritten != cwcSrc)
1908 {
1909 DWORD cwcThis;
1910 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
1911 {
1912 if (!pcbWritten || cwcWritten == 0)
1913 rc = RTErrConvertFromErrno(GetLastError());
1914 break;
1915 }
1916 if (cwcThis != 1) /* Unable to write current char (amount)? */
1917 break;
1918 cwcWritten++;
1919 }
1920 }
1921 if (RT_SUCCESS(rc))
1922 {
1923 if (cwcWritten == cwcSrc)
1924 {
1925 if (pcbWritten)
1926 *pcbWritten = cbToWrite;
1927 }
1928 else if (pcbWritten)
1929 {
1930 PCRTUTF16 pwszCur = pwszSrc;
1931 const char *pszCur = (const char *)pvBuf;
1932 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
1933 {
1934 RTUNICP CpIgnored;
1935 RTUtf16GetCpEx(&pwszCur, &CpIgnored);
1936 RTStrGetCpEx(&pszCur, &CpIgnored);
1937 }
1938 *pcbWritten = pszCur - (const char *)pvBuf;
1939 }
1940 else
1941 rc = VERR_WRITE_ERROR;
1942 }
1943 RTUtf16Free(pwszSrc);
1944 }
1945 }
1946 else
1947 rc = RTErrConvertFromErrno(errno);
1948 return rc;
1949}
1950
[38658]1951#endif /* RT_OS_WINDOWS */
[1]1952
[95886]1953static int rtStrmWriteWorkerLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fMustWriteAll)
1954{
1955#ifdef RTSTREAM_STANDALONE
1956 /*
1957 * Check preconditions.
1958 */
1959 Assert(pStream->enmBufDir == RTSTREAMBUFDIR_WRITE);
1960 Assert(pStream->cbBufAlloc >= 256);
1961 Assert(pStream->offBufFirst <= pStream->cbBufAlloc);
1962 Assert(pStream->offBufEnd <= pStream->cbBufAlloc);
1963 Assert(pStream->offBufFirst <= pStream->offBufEnd);
[1]1964
[95886]1965 /*
1966 * We write everything via the buffer, letting the buffer flushing take
1967 * care of console output hacks and similar.
1968 */
1969 RT_NOREF(fMustWriteAll);
1970 int rc = VINF_SUCCESS;
1971 size_t cbTotal = 0;
1972 if (cbToWrite > 0)
1973 {
1974# ifdef RTSTREAM_WITH_TEXT_MODE
1975 const char *pchLf;
1976 if ( !pStream->fBinary
1977 && (pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite)) != NULL)
1978 for (;;)
1979 {
1980 /* Deal with everything up to the newline. */
1981 size_t const cbToLf = (size_t)(pchLf - (const char *)pvBuf);
1982 if (cbToLf > 0)
1983 {
1984 rc = rtStrmBufCopyTo(pStream, pvBuf, cbToLf, &cbTotal);
1985 if (RT_FAILURE(rc))
1986 break;
1987 }
1988
1989 /* Copy the CRLF sequence into the buffer in one go to avoid complications. */
1990 if (pStream->cbBufAlloc - pStream->offBufEnd < 2)
1991 {
1992 rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
1993 if (RT_FAILURE(rc))
1994 break;
1995 Assert(pStream->cbBufAlloc - pStream->offBufEnd >= 2);
1996 }
1997 pStream->pchBuf[pStream->offBufEnd++] = '\r';
1998 pStream->pchBuf[pStream->offBufEnd++] = '\n';
1999
2000 /* Advance past the newline. */
2001 pvBuf = (const char *)pvBuf + 1 + cbToLf;
2002 cbTotal += 1 + cbToLf;
2003 cbToWrite -= 1 + cbToLf;
2004 if (!cbToWrite)
2005 break;
2006
2007 /* More newlines? */
2008 pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite);
2009 if (!pchLf)
2010 {
2011 rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal);
2012 break;
2013 }
2014 }
2015 else
2016# endif
2017 rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal);
2018
2019 /*
2020 * If line buffered or unbuffered, we probably have to do some flushing now.
2021 */
2022 if (RT_SUCCESS(rc) && pStream->enmBufStyle != RTSTREAMBUFSTYLE_FULL)
2023 {
2024 Assert(pStream->enmBufStyle == RTSTREAMBUFSTYLE_LINE || pStream->enmBufStyle == RTSTREAMBUFSTYLE_UNBUFFERED);
2025 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
2026 if (cbInBuffer > 0)
2027 {
2028 if ( pStream->enmBufStyle != RTSTREAMBUFSTYLE_LINE
2029 || pStream->pchBuf[pStream->offBufEnd - 1] == '\n')
2030 rc = rtStrmBufFlushWrite(pStream, cbInBuffer);
2031 else
2032 {
2033 const char *pchToFlush = &pStream->pchBuf[pStream->offBufFirst];
2034 const char *pchLastLf = (const char *)memrchr(pchToFlush, '\n', cbInBuffer);
2035 if (pchLastLf)
2036 rc = rtStrmBufFlushWrite(pStream, (size_t)(&pchLastLf[1] - pchToFlush));
2037 }
2038 }
2039 }
2040 }
2041 if (pcbWritten)
2042 *pcbWritten = cbTotal;
2043 return rc;
2044
2045
2046#else
2047 if (!fMustWriteAll)
2048 {
2049 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
2050# ifdef HAVE_FWRITE_UNLOCKED
2051 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbToWrite, pStream->pFile);
2052# else
2053 *pcbWritten = fwrite(pvBuf, 1, cbToWrite, pStream->pFile);
2054# endif
2055 IPRT_ALIGNMENT_CHECKS_ENABLE();
2056 if ( *pcbWritten == cbToWrite
2057# ifdef HAVE_FWRITE_UNLOCKED
2058 || !ferror_unlocked(pStream->pFile))
2059# else
2060 || !ferror(pStream->pFile))
2061# endif
2062 return VINF_SUCCESS;
2063 }
2064 else
2065 {
2066 /* Must write it all! */
2067 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
2068# ifdef HAVE_FWRITE_UNLOCKED
2069 size_t cbWritten = fwrite_unlocked(pvBuf, cbToWrite, 1, pStream->pFile);
2070# else
2071 size_t cbWritten = fwrite(pvBuf, cbToWrite, 1, pStream->pFile);
2072# endif
2073 if (pcbWritten)
2074 *pcbWritten = cbWritten;
2075 IPRT_ALIGNMENT_CHECKS_ENABLE();
2076 if (cbWritten == 1)
2077 return VINF_SUCCESS;
2078# ifdef HAVE_FWRITE_UNLOCKED
2079 if (!ferror_unlocked(pStream->pFile))
2080# else
2081 if (!ferror(pStream->pFile))
2082# endif
2083 return VINF_SUCCESS; /* WEIRD! But anyway... */
2084 }
2085 return VERR_WRITE_ERROR;
2086#endif
2087}
2088
2089
[1]2090/**
[39327]2091 * Internal write API, stream lock already held.
2092 *
[38658]2093 * @returns IPRT status code.
2094 * @param pStream The stream.
2095 * @param pvBuf What to write.
[95886]2096 * @param cbToWrite How much to write.
[39327]2097 * @param pcbWritten Where to optionally return the number of bytes
[38658]2098 * written.
2099 * @param fSureIsText Set if we're sure this is UTF-8 text already.
[1]2100 */
[95886]2101static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText)
[1]2102{
[95886]2103#ifdef RTSTREAM_STANDALONE
2104 int rc = rtStrmBufCheckErrorAndSwitchToWriteMode(pStream);
2105#else
[38658]2106 int rc = pStream->i32Error;
[95886]2107#endif
[38658]2108 if (RT_FAILURE(rc))
2109 return rc;
2110 if (pStream->fRecheckMode)
2111 rtStreamRecheckMode(pStream);
2112
[95886]2113#if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE)
[38658]2114 /*
[39327]2115 * Use the unicode console API when possible in order to avoid stuff
[38658]2116 * getting lost in unnecessary code page translations.
2117 */
2118 HANDLE hCon;
2119 if (rtStrmIsConsoleUnlocked(pStream, &hCon))
[95886]2120 rc = rtStrmWriteWinConsoleLocked(pStream, pvBuf, cbToWrite, pcbWritten, hCon);
[95942]2121#else
2122 if (0) { }
[95886]2123#endif /* RT_OS_WINDOWS && !RTSTREAM_STANDALONE */
[38658]2124
2125 /*
[39327]2126 * If we're sure it's text output, convert it from UTF-8 to the current
2127 * code page before printing it.
2128 *
2129 * Note! Partial writes are not supported in this scenario because we
[38658]2130 * cannot easily report back a written length matching the input.
2131 */
2132 /** @todo Skip this if the current code set is UTF-8. */
[95886]2133 else if ( pStream->fCurrentCodeSet
2134 && !pStream->fBinary
2135 && ( fSureIsText
2136 || rtStrmIsUtf8Text(pvBuf, cbToWrite))
2137 )
[38658]2138 {
2139 char *pszSrcFree = NULL;
2140 const char *pszSrc = (const char *)pvBuf;
[95886]2141 if (pszSrc[cbToWrite - 1])
[38658]2142 {
[95886]2143 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbToWrite);
[38658]2144 if (pszSrc == NULL)
2145 rc = VERR_NO_STR_MEMORY;
2146 }
2147 if (RT_SUCCESS(rc))
2148 {
2149 char *pszSrcCurCP;
2150 rc = RTStrUtf8ToCurrentCP(&pszSrcCurCP, pszSrc);
[96234]2151 AssertRC(rc);
[38658]2152 if (RT_SUCCESS(rc))
[1]2153 {
[38658]2154 size_t cchSrcCurCP = strlen(pszSrcCurCP);
[95886]2155 size_t cbWritten = 0;
2156 rc = rtStrmWriteWorkerLocked(pStream, pszSrcCurCP, cchSrcCurCP, &cbWritten, true /*fMustWriteAll*/);
2157 if (pcbWritten)
2158 *pcbWritten = cbWritten == cchSrcCurCP ? cbToWrite : 0;
[38658]2159 RTStrFree(pszSrcCurCP);
[1]2160 }
[38658]2161 RTStrFree(pszSrcFree);
[1]2162 }
2163 }
[38658]2164 /*
[39327]2165 * Otherwise, just write it as-is.
[38658]2166 */
[1]2167 else
[95886]2168 rc = rtStrmWriteWorkerLocked(pStream, pvBuf, cbToWrite, pcbWritten, pcbWritten == NULL);
[38658]2169
[95886]2170 /*
2171 * Update error status on failure and return.
[96235]2172 *
2173 * We ignore failures from RTStrUtf8ToCurrentCP and RTStrToUtf16Ex regarding
2174 * invalid UTF-8 encoding, as that's an input issue and shouldn't affect the
2175 * stream state.
[95886]2176 */
[96235]2177 if (RT_FAILURE(rc) && rc != VERR_INVALID_UTF8_ENCODING)
[95886]2178 ASMAtomicWriteS32(&pStream->i32Error, rc);
[1]2179 return rc;
2180}
2181
2182
2183/**
[39327]2184 * Internal write API.
2185 *
[38658]2186 * @returns IPRT status code.
2187 * @param pStream The stream.
2188 * @param pvBuf What to write.
[95886]2189 * @param cbToWrite How much to write.
[39327]2190 * @param pcbWritten Where to optionally return the number of bytes
[38658]2191 * written.
2192 * @param fSureIsText Set if we're sure this is UTF-8 text already.
2193 */
[95886]2194DECLINLINE(int) rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText)
[38658]2195{
2196 rtStrmLock(pStream);
[95886]2197 int rc = rtStrmWriteLocked(pStream, pvBuf, cbToWrite, pcbWritten, fSureIsText);
[38658]2198 rtStrmUnlock(pStream);
2199 return rc;
2200}
2201
2202
[95886]2203RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
[38658]2204{
2205 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
[95886]2206 return rtStrmWrite(pStream, pvBuf, cbToWrite, pcbWritten, false);
[38658]2207}
2208
2209
[1]2210RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
2211{
2212 unsigned char ch;
2213 int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
2214 if (RT_SUCCESS(rc))
2215 return ch;
2216 return -1;
2217}
2218
2219
2220RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
2221{
[38658]2222 return rtStrmWrite(pStream, &ch, 1, NULL, true /*fSureIsText*/);
[1]2223}
2224
2225
2226RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
2227{
2228 size_t cch = strlen(pszString);
[38658]2229 return rtStrmWrite(pStream, pszString, cch, NULL, true /*fSureIsText*/);
[1]2230}
2231
2232
[41504]2233RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString)
[1]2234{
[95886]2235 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
2236 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
2237 AssertReturn(pszString, VERR_INVALID_POINTER);
2238 AssertReturn(cbString >= 2, VERR_INVALID_PARAMETER);
[41504]2239
[95886]2240 rtStrmLock(pStream);
2241
2242#ifdef RTSTREAM_STANDALONE
2243 int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream);
[41504]2244#else
[95886]2245 int rc = pStream->i32Error;
[41504]2246#endif
[95886]2247 if (RT_SUCCESS(rc))
2248 {
2249 cbString--; /* Reserve space for the terminator. */
[41504]2250
[95886]2251#ifdef RTSTREAM_STANDALONE
2252 char * const pszStringStart = pszString;
[20823]2253#endif
[95886]2254 for (;;)
2255 {
2256#ifdef RTSTREAM_STANDALONE
2257 /* Make sure there is at least one character in the buffer: */
2258 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
2259 if (cbInBuffer == 0)
2260 {
2261 rc = rtStrmBufFill(pStream);
2262 if (RT_SUCCESS(rc))
2263 cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
2264 else
[38658]2265 break;
[95886]2266 }
[41504]2267
[95886]2268 /* Scan the buffer content terminating on a '\n', '\r\n' and '\0' sequence. */
2269 const char *pchSrc = &pStream->pchBuf[pStream->offBufFirst];
2270 const char *pchNewline = (const char *)memchr(pchSrc, '\n', cbInBuffer);
2271 const char *pchTerm = (const char *)memchr(pchSrc, '\0', cbInBuffer);
2272 size_t cbCopy;
2273 size_t cbAdvance;
2274 bool fStop = pchNewline || pchTerm;
2275 if (!fStop)
2276 cbAdvance = cbCopy = cbInBuffer;
2277 else if (!pchTerm || (pchNewline && pchTerm && (uintptr_t)pchNewline < (uintptr_t)pchTerm))
2278 {
2279 cbCopy = (size_t)(pchNewline - pchSrc);
2280 cbAdvance = cbCopy + 1;
2281 if (cbCopy && pchNewline[-1] == '\r')
2282 cbCopy--;
2283 else if (cbCopy == 0 && (uintptr_t)pszString > (uintptr_t)pszStringStart && pszString[-1] == '\r')
2284 pszString--, cbString++; /* drop trailing '\r' that it turns out was followed by '\n' */
2285 }
2286 else
2287 {
2288 cbCopy = (size_t)(pchTerm - pchSrc);
2289 cbAdvance = cbCopy + 1;
2290 }
2291
2292 /* Adjust for available space in the destination buffer, copy over the string
2293 characters and advance the buffer position (even on overflow). */
2294 if (cbCopy <= cbString)
2295 pStream->offBufFirst += cbAdvance;
2296 else
2297 {
2298 rc = VERR_BUFFER_OVERFLOW;
2299 fStop = true;
2300 cbCopy = cbString;
2301 pStream->offBufFirst += cbString;
2302 }
2303
2304 memcpy(pszString, pchSrc, cbCopy);
2305 pszString += cbCopy;
2306 cbString -= cbCopy;
2307
2308 if (fStop)
2309 break;
2310
2311#else /* !RTSTREAM_STANDALONE */
2312# ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
2313 int ch = fgetc_unlocked(pStream->pFile);
2314# else
2315 int ch = fgetc(pStream->pFile);
2316# endif
2317
2318 /* Deal with \r\n sequences here. We'll return lone CR, but
2319 treat CRLF as LF. */
2320 if (ch == '\r')
2321 {
2322# ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
2323 ch = fgetc_unlocked(pStream->pFile);
2324# else
2325 ch = fgetc(pStream->pFile);
2326# endif
2327 if (ch == '\n')
[38658]2328 break;
[41504]2329
[95886]2330 *pszString++ = '\r';
[41504]2331 if (--cbString <= 0)
[38658]2332 {
[95886]2333 /* yeah, this is an error, we dropped a character. */
2334 rc = VERR_BUFFER_OVERFLOW;
[38658]2335 break;
2336 }
2337 }
[1]2338
[95886]2339 /* Deal with end of file. */
2340 if (ch == EOF)
2341 {
2342# ifdef HAVE_FWRITE_UNLOCKED
2343 if (feof_unlocked(pStream->pFile))
2344# else
2345 if (feof(pStream->pFile))
2346# endif
2347 {
2348 rc = VERR_EOF;
2349 break;
2350 }
2351# ifdef HAVE_FWRITE_UNLOCKED
2352 if (ferror_unlocked(pStream->pFile))
2353# else
2354 if (ferror(pStream->pFile))
2355# endif
2356 rc = VERR_READ_ERROR;
2357 else
2358 {
2359 AssertMsgFailed(("This shouldn't happen\n"));
2360 rc = VERR_INTERNAL_ERROR;
2361 }
2362 break;
2363 }
2364
2365 /* Deal with null terminator and (lone) new line. */
2366 if (ch == '\0' || ch == '\n')
2367 break;
2368
2369 /* No special character, append it to the return string. */
2370 *pszString++ = ch;
2371 if (--cbString <= 0)
2372 {
2373 rc = VINF_BUFFER_OVERFLOW;
2374 break;
2375 }
2376#endif /* !RTSTREAM_STANDALONE */
[1]2377 }
[95886]2378
2379 *pszString = '\0';
2380 if (RT_FAILURE(rc))
2381 ASMAtomicWriteS32(&pStream->i32Error, rc);
[1]2382 }
[95886]2383
2384 rtStrmUnlock(pStream);
[1]2385 return rc;
2386}
2387
2388
2389RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
2390{
[95886]2391 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
2392 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
2393
2394#ifdef RTSTREAM_STANDALONE
2395 rtStrmLock(pStream);
2396 int rc = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
2397 rtStrmUnlock(pStream);
2398 return rc;
2399
2400#else
[1]2401 if (!fflush(pStream->pFile))
2402 return VINF_SUCCESS;
2403 return RTErrConvertFromErrno(errno);
[95886]2404#endif
[1]2405}
2406
2407
2408/**
2409 * Output callback.
2410 *
2411 * @returns number of bytes written.
2412 * @param pvArg User argument.
2413 * @param pachChars Pointer to an array of utf-8 characters.
2414 * @param cchChars Number of bytes in the character array pointed to by pachChars.
2415 */
2416static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
2417{
2418 if (cchChars)
[38658]2419 rtStrmWriteLocked((PRTSTREAM)pvArg, pachChars, cchChars, NULL, true /*fSureIsText*/);
[1]2420 /* else: ignore termination call. */
2421 return cchChars;
2422}
2423
2424
2425RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
2426{
[38658]2427 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
2428 int rc = pStream->i32Error;
2429 if (RT_SUCCESS(rc))
[1]2430 {
[38658]2431 rtStrmLock(pStream);
2432// pStream->fShouldFlush = true;
2433 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
2434 rtStrmUnlock(pStream);
2435 Assert(rc >= 0);
[1]2436 }
2437 else
2438 rc = -1;
2439 return rc;
2440}
2441
2442
2443RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
2444{
2445 va_list args;
2446 va_start(args, pszFormat);
2447 int rc = RTStrmPrintfV(pStream, pszFormat, args);
2448 va_end(args);
2449 return rc;
2450}
2451
2452
[51770]2453RTDECL(void) RTStrmDumpPrintfV(void *pvUser, const char *pszFormat, va_list va)
2454{
2455 RTStrmPrintfV(pvUser ? (PRTSTREAM)pvUser : g_pStdOut, pszFormat, va);
2456}
2457
2458
[1]2459RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
2460{
2461 return RTStrmPrintfV(g_pStdOut, pszFormat, args);
2462}
2463
2464
2465RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
2466{
2467 va_list args;
2468 va_start(args, pszFormat);
2469 int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
2470 va_end(args);
2471 return rc;
2472}
2473
[95586]2474
2475/**
2476 * Outputs @a cchIndent spaces.
2477 */
2478static void rtStrmWrapppedIndent(RTSTRMWRAPPEDSTATE *pState, uint32_t cchIndent)
2479{
2480 static const char s_szSpaces[] = " ";
2481 while (cchIndent)
2482 {
2483 uint32_t cchToWrite = RT_MIN(cchIndent, sizeof(s_szSpaces) - 1);
2484 int rc = RTStrmWrite(pState->pStream, s_szSpaces, cchToWrite);
2485 if (RT_SUCCESS(rc))
2486 cchIndent -= cchToWrite;
2487 else
2488 {
2489 pState->rcStatus = rc;
2490 break;
2491 }
2492 }
2493}
2494
2495
2496/**
2497 * Flushes the current line.
2498 *
2499 * @param pState The wrapped output state.
2500 * @param fPartial Set if partial flush due to buffer overflow, clear when
2501 * flushing due to '\n'.
2502 */
2503static void rtStrmWrappedFlushLine(RTSTRMWRAPPEDSTATE *pState, bool fPartial)
2504{
2505 /*
2506 * Check indentation in case we need to split the line later.
2507 */
2508 uint32_t cchIndent = pState->cchIndent;
2509 if (cchIndent == UINT32_MAX)
2510 {
2511 pState->cchIndent = 0;
2512 cchIndent = pState->cchHangingIndent;
2513 while (RT_C_IS_BLANK(pState->szLine[cchIndent]))
2514 cchIndent++;
2515 }
2516
2517 /*
2518 * Do the flushing.
2519 */
2520 uint32_t cchLine = pState->cchLine;
2521 Assert(cchLine < sizeof(pState->szLine));
2522 while (cchLine >= pState->cchWidth || !fPartial)
2523 {
2524 /*
2525 * Hopefully we don't need to do any wrapping ...
2526 */
2527 uint32_t offSplit;
2528 if (pState->cchIndent + cchLine <= pState->cchWidth)
2529 {
2530 if (!fPartial)
2531 {
2532 rtStrmWrapppedIndent(pState, pState->cchIndent);
2533 pState->szLine[cchLine] = '\n';
2534 int rc = RTStrmWrite(pState->pStream, pState->szLine, cchLine + 1);
2535 if (RT_FAILURE(rc))
2536 pState->rcStatus = rc;
2537 pState->cLines += 1;
2538 pState->cchLine = 0;
2539 pState->cchIndent = UINT32_MAX;
2540 return;
2541 }
2542
2543 /*
2544 * ... no such luck.
2545 */
2546 offSplit = cchLine;
2547 }
2548 else
2549 offSplit = pState->cchWidth - pState->cchIndent;
2550
2551 /* Find the start of the current word: */
2552 while (offSplit > 0 && !RT_C_IS_BLANK(pState->szLine[offSplit - 1]))
2553 offSplit--;
2554
2555 /* Skip spaces. */
2556 while (offSplit > 0 && RT_C_IS_BLANK(pState->szLine[offSplit - 1]))
2557 offSplit--;
2558 uint32_t offNextLine = offSplit;
2559
2560 /* If the first word + indent is wider than the screen width, so just output it in full. */
2561 if (offSplit == 0) /** @todo Split words, look for hyphen... This code is currently a bit crude. */
2562 {
2563 while (offSplit < cchLine && !RT_C_IS_BLANK(pState->szLine[offSplit]))
2564 offSplit++;
2565 offNextLine = offSplit;
2566 }
2567
2568 while (offNextLine < cchLine && RT_C_IS_BLANK(pState->szLine[offNextLine]))
2569 offNextLine++;
2570
2571 /*
2572 * Output and advance.
2573 */
2574 rtStrmWrapppedIndent(pState, pState->cchIndent);
2575 int rc = RTStrmWrite(pState->pStream, pState->szLine, offSplit);
2576 if (RT_SUCCESS(rc))
2577 rc = RTStrmPutCh(pState->pStream, '\n');
2578 if (RT_FAILURE(rc))
2579 pState->rcStatus = rc;
2580
2581 cchLine -= offNextLine;
2582 pState->cchLine = cchLine;
2583 pState->cLines += 1;
2584 pState->cchIndent = cchIndent;
2585 memmove(&pState->szLine[0], &pState->szLine[offNextLine], cchLine);
2586 }
2587
2588 /* The indentation level is reset for each '\n' we process, so only save cchIndent if partial. */
2589 pState->cchIndent = fPartial ? cchIndent : UINT32_MAX;
2590}
2591
2592
2593/**
2594 * @callback_method_impl{FNRTSTROUTPUT}
2595 */
2596static DECLCALLBACK(size_t) rtStrmWrappedOutput(void *pvArg, const char *pachChars, size_t cbChars)
2597{
2598 RTSTRMWRAPPEDSTATE *pState = (RTSTRMWRAPPEDSTATE *)pvArg;
2599 size_t const cchRet = cbChars;
2600 while (cbChars > 0)
2601 {
2602 if (*pachChars == '\n')
2603 {
2604 rtStrmWrappedFlushLine(pState, false /*fPartial*/);
2605 pachChars++;
2606 cbChars--;
2607 }
2608 else
2609 {
2610 const char *pszEol = (const char *)memchr(pachChars, '\n', cbChars);
2611 size_t cchToCopy = pszEol ? (size_t)(pszEol - pachChars) : cbChars;
2612 uint32_t cchLine = pState->cchLine;
2613 Assert(cchLine < sizeof(pState->szLine));
2614 bool const fFlush = cchLine + cchToCopy >= sizeof(pState->szLine);
2615 if (fFlush)
2616 cchToCopy = cchToCopy - sizeof(pState->szLine) - 1;
2617
2618 pState->cchLine = cchLine + (uint32_t)cchToCopy;
2619 memcpy(&pState->szLine[cchLine], pachChars, cchToCopy);
2620
2621 pachChars += cchToCopy;
2622 cbChars -= cchToCopy;
2623
2624 if (fFlush)
2625 rtStrmWrappedFlushLine(pState, true /*fPartial*/);
2626 }
2627 }
2628 return cchRet;
2629}
2630
2631
2632RTDECL(int32_t) RTStrmWrappedPrintfV(PRTSTREAM pStream, uint32_t fFlags, const char *pszFormat, va_list va)
2633{
2634 /*
2635 * Figure the output width and set up the rest of the output state.
2636 */
2637 RTSTRMWRAPPEDSTATE State;
2638 State.pStream = pStream;
2639 State.cchLine = fFlags & RTSTRMWRAPPED_F_LINE_OFFSET_MASK;
2640 State.cLines = 0;
2641 State.rcStatus = VINF_SUCCESS;
2642 State.cchIndent = UINT32_MAX;
2643 State.cchHangingIndent = 0;
2644 if (fFlags & RTSTRMWRAPPED_F_HANGING_INDENT)
2645 {
2646 State.cchHangingIndent = (fFlags & RTSTRMWRAPPED_F_HANGING_INDENT_MASK) >> RTSTRMWRAPPED_F_HANGING_INDENT_SHIFT;
2647 if (!State.cchHangingIndent)
2648 State.cchHangingIndent = 4;
2649 }
2650
2651 int rc = RTStrmQueryTerminalWidth(pStream, &State.cchWidth);
2652 if (RT_SUCCESS(rc))
2653 State.cchWidth = RT_MIN(State.cchWidth, RTSTRMWRAPPED_F_LINE_OFFSET_MASK + 1);
2654 else
2655 {
2656 State.cchWidth = (uint32_t)fFlags & RTSTRMWRAPPED_F_NON_TERMINAL_WIDTH_MASK;
2657 if (!State.cchWidth)
2658 State.cchWidth = 80;
2659 }
2660 if (State.cchWidth < 32)
2661 State.cchWidth = 32;
2662 //State.cchWidth -= 1; /* necessary here? */
2663
2664 /*
2665 * Do the formatting.
2666 */
2667 RTStrFormatV(rtStrmWrappedOutput, &State, NULL, NULL, pszFormat, va);
2668
2669 /*
2670 * Returning is simple if the buffer is empty. Otherwise we'll have to
2671 * perform a partial flush and write out whatever is left ourselves.
2672 */
2673 if (RT_SUCCESS(State.rcStatus))
2674 {
2675 if (State.cchLine == 0)
2676 return State.cLines << 16;
2677
2678 rtStrmWrappedFlushLine(&State, true /*fPartial*/);
2679 if (RT_SUCCESS(State.rcStatus) && State.cchLine > 0)
2680 {
2681 rtStrmWrapppedIndent(&State, State.cchIndent);
2682 State.rcStatus = RTStrmWrite(State.pStream, State.szLine, State.cchLine);
2683 }
2684 if (RT_SUCCESS(State.rcStatus))
2685 return RT_MIN(State.cchIndent + State.cchLine, RTSTRMWRAPPED_F_LINE_OFFSET_MASK) | (State.cLines << 16);
2686 }
2687 return State.rcStatus;
2688}
2689
2690
2691RTDECL(int32_t) RTStrmWrappedPrintf(PRTSTREAM pStream, uint32_t fFlags, const char *pszFormat, ...)
2692{
2693 va_list va;
2694 va_start(va, pszFormat);
2695 int32_t rcRet = RTStrmWrappedPrintfV(pStream, fFlags, pszFormat, va);
2696 va_end(va);
2697 return rcRet;
2698}
2699
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use