VirtualBox

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

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

IPRT/stream.cpp: Assert if UTF-8 to UTF-16/current-cp fails during console output, just silently failing isn't good and it's questionable whether we should actually block the stream here as it's an input failure we weed out before it hits the actual output.

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

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