VirtualBox

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

最後變更 在這個檔案從96442是 96442,由 vboxsync 提交於 3 年 前

IPRT: Split out the public init data into separate files and made it possible to check if IPRT is initialized w/o dragging in the init code. Added tweak to stream.cpp so it'll work w/o IPRT init in debug builds. bugref:10261

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

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