VirtualBox

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

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

scm copyright and license note update

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

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