VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 36.3 KB
Line 
1/* $Id: stream.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.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#if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */
30#define HAVE_FWRITE_UNLOCKED
31#endif
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#include <iprt/stream.h>
38#include "internal/iprt.h"
39
40#include <iprt/asm.h>
41#ifndef HAVE_FWRITE_UNLOCKED
42# include <iprt/critsect.h>
43#endif
44#include <iprt/string.h>
45#include <iprt/assert.h>
46#include <iprt/alloc.h>
47#include <iprt/err.h>
48#include <iprt/param.h>
49#include <iprt/string.h>
50
51#include "internal/alignmentchecks.h"
52#include "internal/magics.h"
53
54#include <stdio.h>
55#include <errno.h>
56#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
57# include <io.h>
58# include <fcntl.h>
59#endif
60#ifdef RT_OS_WINDOWS
61# include <iprt/utf16.h>
62# include <iprt/win/windows.h>
63#else
64# include <termios.h>
65# include <unistd.h>
66# include <sys/ioctl.h>
67#endif
68
69#ifdef RT_OS_OS2
70# define _O_TEXT O_TEXT
71# define _O_BINARY O_BINARY
72#endif
73
74
75/*********************************************************************************************************************************
76* Structures and Typedefs *
77*********************************************************************************************************************************/
78/**
79 * File stream.
80 */
81typedef struct RTSTREAM
82{
83 /** Magic value used to validate the stream. (RTSTREAM_MAGIC) */
84 uint32_t u32Magic;
85 /** File stream error. */
86 int32_t volatile i32Error;
87 /** Pointer to the LIBC file stream. */
88 FILE *pFile;
89 /** Stream is using the current process code set. */
90 bool fCurrentCodeSet;
91 /** Whether the stream was opened in binary mode. */
92 bool fBinary;
93 /** Whether to recheck the stream mode before writing. */
94 bool fRecheckMode;
95#ifndef HAVE_FWRITE_UNLOCKED
96 /** Critical section for serializing access to the stream. */
97 PRTCRITSECT pCritSect;
98#endif
99} RTSTREAM;
100
101
102/*********************************************************************************************************************************
103* Global Variables *
104*********************************************************************************************************************************/
105/** The standard input stream. */
106static RTSTREAM g_StdIn =
107{
108 RTSTREAM_MAGIC,
109 0,
110 stdin,
111 /*.fCurrentCodeSet = */ true,
112 /*.fBinary = */ false,
113 /*.fRecheckMode = */ true
114#ifndef HAVE_FWRITE_UNLOCKED
115 , NULL
116#endif
117};
118
119/** The standard error stream. */
120static RTSTREAM g_StdErr =
121{
122 RTSTREAM_MAGIC,
123 0,
124 stderr,
125 /*.fCurrentCodeSet = */ true,
126 /*.fBinary = */ false,
127 /*.fRecheckMode = */ true
128#ifndef HAVE_FWRITE_UNLOCKED
129 , NULL
130#endif
131};
132
133/** The standard output stream. */
134static RTSTREAM g_StdOut =
135{
136 RTSTREAM_MAGIC,
137 0,
138 stdout,
139 /*.fCurrentCodeSet = */ true,
140 /*.fBinary = */ false,
141 /*.fRecheckMode = */ true
142#ifndef HAVE_FWRITE_UNLOCKED
143 , NULL
144#endif
145};
146
147/** Pointer to the standard input stream. */
148RTDATADECL(PRTSTREAM) g_pStdIn = &g_StdIn;
149
150/** Pointer to the standard output stream. */
151RTDATADECL(PRTSTREAM) g_pStdErr = &g_StdErr;
152
153/** Pointer to the standard output stream. */
154RTDATADECL(PRTSTREAM) g_pStdOut = &g_StdOut;
155
156
157#ifndef HAVE_FWRITE_UNLOCKED
158/**
159 * Allocates and acquires the lock for the stream.
160 *
161 * @returns IPRT status code.
162 * @param pStream The stream (valid).
163 */
164static int rtStrmAllocLock(PRTSTREAM pStream)
165{
166 Assert(pStream->pCritSect == NULL);
167
168 PRTCRITSECT pCritSect = (PRTCRITSECT)RTMemAlloc(sizeof(*pCritSect));
169 if (!pCritSect)
170 return VERR_NO_MEMORY;
171
172 /* The native stream lock are normally not recursive. */
173 int rc = RTCritSectInitEx(pCritSect, RTCRITSECT_FLAGS_NO_NESTING,
174 NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemSpinMutex");
175 if (RT_SUCCESS(rc))
176 {
177 rc = RTCritSectEnter(pCritSect);
178 if (RT_SUCCESS(rc))
179 {
180 if (RT_LIKELY(ASMAtomicCmpXchgPtr(&pStream->pCritSect, pCritSect, NULL)))
181 return VINF_SUCCESS;
182
183 RTCritSectLeave(pCritSect);
184 }
185 RTCritSectDelete(pCritSect);
186 }
187 RTMemFree(pCritSect);
188
189 /* Handle the lost race case... */
190 pCritSect = ASMAtomicReadPtrT(&pStream->pCritSect, PRTCRITSECT);
191 if (pCritSect)
192 return RTCritSectEnter(pCritSect);
193
194 return rc;
195}
196#endif /* !HAVE_FWRITE_UNLOCKED */
197
198
199/**
200 * Locks the stream. May have to allocate the lock as well.
201 *
202 * @param pStream The stream (valid).
203 */
204DECLINLINE(void) rtStrmLock(PRTSTREAM pStream)
205{
206#ifdef HAVE_FWRITE_UNLOCKED
207 flockfile(pStream->pFile);
208#else
209 if (RT_LIKELY(pStream->pCritSect))
210 RTCritSectEnter(pStream->pCritSect);
211 else
212 rtStrmAllocLock(pStream);
213#endif
214}
215
216
217/**
218 * Unlocks the stream.
219 *
220 * @param pStream The stream (valid).
221 */
222DECLINLINE(void) rtStrmUnlock(PRTSTREAM pStream)
223{
224#ifdef HAVE_FWRITE_UNLOCKED
225 funlockfile(pStream->pFile);
226#else
227 if (RT_LIKELY(pStream->pCritSect))
228 RTCritSectLeave(pStream->pCritSect);
229#endif
230}
231
232
233/**
234 * Opens a file stream.
235 *
236 * @returns iprt status code.
237 * @param pszFilename Path to the file to open.
238 * @param pszMode The open mode. See fopen() standard.
239 * Format: <a|r|w>[+][b|t]
240 * @param ppStream Where to store the opened stream.
241 */
242RTR3DECL(int) RTStrmOpen(const char *pszFilename, const char *pszMode, PRTSTREAM *ppStream)
243{
244 /*
245 * Validate input.
246 */
247 if (!pszMode || !*pszMode)
248 {
249 AssertMsgFailed(("No pszMode!\n"));
250 return VERR_INVALID_PARAMETER;
251 }
252 if (!pszFilename)
253 {
254 AssertMsgFailed(("No pszFilename!\n"));
255 return VERR_INVALID_PARAMETER;
256 }
257 bool fOk = true;
258 bool fBinary = false;
259 switch (*pszMode)
260 {
261 case 'a':
262 case 'w':
263 case 'r':
264 switch (pszMode[1])
265 {
266 case '\0':
267 break;
268
269 case '+':
270 switch (pszMode[2])
271 {
272 case '\0':
273 break;
274
275 //case 't':
276 // break;
277
278 case 'b':
279 fBinary = true;
280 break;
281
282 default:
283 fOk = false;
284 break;
285 }
286 break;
287
288 //case 't':
289 // break;
290
291 case 'b':
292 fBinary = true;
293 break;
294
295 default:
296 fOk = false;
297 break;
298 }
299 break;
300 default:
301 fOk = false;
302 break;
303 }
304 if (!fOk)
305 {
306 AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b|t]'\n", pszMode));
307 return VINF_SUCCESS;
308 }
309
310 /*
311 * Allocate the stream handle and try open it.
312 */
313 PRTSTREAM pStream = (PRTSTREAM)RTMemAlloc(sizeof(*pStream));
314 if (pStream)
315 {
316 pStream->u32Magic = RTSTREAM_MAGIC;
317 pStream->i32Error = VINF_SUCCESS;
318 pStream->fCurrentCodeSet = false;
319 pStream->fBinary = fBinary;
320 pStream->fRecheckMode = false;
321#ifndef HAVE_FWRITE_UNLOCKED
322 pStream->pCritSect = NULL;
323#endif /* HAVE_FWRITE_UNLOCKED */
324 pStream->pFile = fopen(pszFilename, pszMode);
325 if (pStream->pFile)
326 {
327 *ppStream = pStream;
328 return VINF_SUCCESS;
329 }
330 RTMemFree(pStream);
331 return RTErrConvertFromErrno(errno);
332 }
333 return VERR_NO_MEMORY;
334}
335
336
337/**
338 * Opens a file stream.
339 *
340 * @returns iprt status code.
341 * @param pszMode The open mode. See fopen() standard.
342 * Format: <a|r|w>[+][b|t]
343 * @param ppStream Where to store the opened stream.
344 * @param pszFilenameFmt Filename path format string.
345 * @param args Arguments to the format string.
346 */
347RTR3DECL(int) RTStrmOpenFV(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, va_list args)
348{
349 int rc;
350 char szFilename[RTPATH_MAX];
351 size_t cch = RTStrPrintfV(szFilename, sizeof(szFilename), pszFilenameFmt, args);
352 if (cch < sizeof(szFilename))
353 rc = RTStrmOpen(szFilename, pszMode, ppStream);
354 else
355 {
356 AssertMsgFailed(("The filename is too long cch=%d\n", cch));
357 rc = VERR_FILENAME_TOO_LONG;
358 }
359 return rc;
360}
361
362
363/**
364 * Opens a file stream.
365 *
366 * @returns iprt status code.
367 * @param pszMode The open mode. See fopen() standard.
368 * Format: <a|r|w>[+][b|t]
369 * @param ppStream Where to store the opened stream.
370 * @param pszFilenameFmt Filename path format string.
371 * @param ... Arguments to the format string.
372 */
373RTR3DECL(int) RTStrmOpenF(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, ...)
374{
375 va_list args;
376 va_start(args, pszFilenameFmt);
377 int rc = RTStrmOpenFV(pszMode, ppStream, pszFilenameFmt, args);
378 va_end(args);
379 return rc;
380}
381
382
383/**
384 * Closes the specified stream.
385 *
386 * @returns iprt status code.
387 * @param pStream The stream to close.
388 */
389RTR3DECL(int) RTStrmClose(PRTSTREAM pStream)
390{
391 if (!pStream)
392 return VINF_SUCCESS;
393 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
394
395 if (!fclose(pStream->pFile))
396 {
397 pStream->u32Magic = 0xdeaddead;
398 pStream->pFile = NULL;
399#ifndef HAVE_FWRITE_UNLOCKED
400 if (pStream->pCritSect)
401 {
402 RTCritSectEnter(pStream->pCritSect);
403 RTCritSectLeave(pStream->pCritSect);
404 RTCritSectDelete(pStream->pCritSect);
405 RTMemFree(pStream->pCritSect);
406 pStream->pCritSect = NULL;
407 }
408#endif
409 RTMemFree(pStream);
410 return VINF_SUCCESS;
411 }
412
413 return RTErrConvertFromErrno(errno);
414}
415
416
417/**
418 * Get the pending error of the stream.
419 *
420 * @returns iprt status code. of the stream.
421 * @param pStream The stream.
422 */
423RTR3DECL(int) RTStrmError(PRTSTREAM pStream)
424{
425 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
426 return pStream->i32Error;
427}
428
429
430/**
431 * Clears stream error condition.
432 *
433 * All stream operations save RTStrmClose and this will fail
434 * while an error is asserted on the stream
435 *
436 * @returns iprt status code.
437 * @param pStream The stream.
438 */
439RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream)
440{
441 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
442
443 clearerr(pStream->pFile);
444 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
445 return VINF_SUCCESS;
446}
447
448
449RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
450{
451 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
452 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
453 AssertReturn((unsigned)(fBinary + 1) <= 2, VERR_INVALID_PARAMETER);
454 AssertReturn((unsigned)(fCurrentCodeSet + 1) <= 2, VERR_INVALID_PARAMETER);
455
456 rtStrmLock(pStream);
457
458 if (fBinary != -1)
459 {
460 pStream->fBinary = RT_BOOL(fBinary);
461 pStream->fRecheckMode = true;
462 }
463
464 if (fCurrentCodeSet != -1)
465 pStream->fCurrentCodeSet = RT_BOOL(fCurrentCodeSet);
466
467 rtStrmUnlock(pStream);
468
469 return VINF_SUCCESS;
470}
471
472
473RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars)
474{
475 int rc = VINF_SUCCESS;
476
477 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
478 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
479 AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER);
480
481 int fh = fileno(pStream->pFile);
482 if (isatty(fh))
483 {
484#ifdef RT_OS_WINDOWS
485 DWORD dwMode;
486 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
487 if (GetConsoleMode(hCon, &dwMode))
488 *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT);
489 else
490 rc = RTErrConvertFromWin32(GetLastError());
491#else
492 struct termios Termios;
493
494 int rcPosix = tcgetattr(fh, &Termios);
495 if (!rcPosix)
496 *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);
497 else
498 rc = RTErrConvertFromErrno(errno);
499#endif
500 }
501 else
502 rc = VERR_INVALID_HANDLE;
503
504 return rc;
505}
506
507
508RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)
509{
510 int rc = VINF_SUCCESS;
511
512 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
513 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
514
515 int fh = fileno(pStream->pFile);
516 if (isatty(fh))
517 {
518#ifdef RT_OS_WINDOWS
519 DWORD dwMode;
520 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
521 if (GetConsoleMode(hCon, &dwMode))
522 {
523 if (fEchoChars)
524 dwMode |= ENABLE_ECHO_INPUT;
525 else
526 dwMode &= ~ENABLE_ECHO_INPUT;
527 if (!SetConsoleMode(hCon, dwMode))
528 rc = RTErrConvertFromWin32(GetLastError());
529 }
530 else
531 rc = RTErrConvertFromWin32(GetLastError());
532#else
533 struct termios Termios;
534
535 int rcPosix = tcgetattr(fh, &Termios);
536 if (!rcPosix)
537 {
538 if (fEchoChars)
539 Termios.c_lflag |= ECHO;
540 else
541 Termios.c_lflag &= ~ECHO;
542
543 rcPosix = tcsetattr(fh, TCSAFLUSH, &Termios);
544 if (rcPosix != 0)
545 rc = RTErrConvertFromErrno(errno);
546 }
547 else
548 rc = RTErrConvertFromErrno(errno);
549#endif
550 }
551 else
552 rc = VERR_INVALID_HANDLE;
553
554 return rc;
555}
556
557
558RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)
559{
560 AssertPtrReturn(pStream, false);
561 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);
562
563 if (pStream->pFile)
564 {
565 int fh = fileno(pStream->pFile);
566 if (isatty(fh))
567 {
568#ifdef RT_OS_WINDOWS
569 DWORD dwMode;
570 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
571 if (GetConsoleMode(hCon, &dwMode))
572 return true;
573#else
574 return true;
575#endif
576 }
577 }
578 return false;
579}
580
581
582RTR3DECL(int) RTStrmQueryTerminalWidth(PRTSTREAM pStream, uint32_t *pcchWidth)
583{
584 AssertPtrReturn(pcchWidth, VERR_INVALID_HANDLE);
585 *pcchWidth = 80;
586
587 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
588 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
589
590 if (pStream->pFile)
591 {
592 int fh = fileno(pStream->pFile);
593 if (isatty(fh))
594 {
595#ifdef RT_OS_WINDOWS
596 CONSOLE_SCREEN_BUFFER_INFO Info;
597 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
598 RT_ZERO(Info);
599 if (GetConsoleScreenBufferInfo(hCon, &Info))
600 {
601 *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;
602 return VINF_SUCCESS;
603 }
604 return RTErrConvertFromWin32(GetLastError());
605
606#elif defined(TIOCGWINSZ) || !defined(RT_OS_OS2) /* only OS/2 should currently miss this */
607 struct winsize Info;
608 RT_ZERO(Info);
609 int rc = ioctl(fh, TIOCGWINSZ, &Info);
610 if (rc >= 0)
611 {
612 *pcchWidth = Info.ws_col ? Info.ws_col : 80;
613 return VINF_SUCCESS;
614 }
615 return RTErrConvertFromErrno(errno);
616#endif
617 }
618 }
619 return VERR_INVALID_FUNCTION;
620}
621
622
623
624/**
625 * Rewinds the stream.
626 *
627 * Stream errors will be reset on success.
628 *
629 * @returns IPRT status code.
630 *
631 * @param pStream The stream.
632 *
633 * @remarks Not all streams are rewindable and that behavior is currently
634 * undefined for those.
635 */
636RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
637{
638 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
639 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
640
641 int rc;
642 clearerr(pStream->pFile);
643 errno = 0;
644 if (!fseek(pStream->pFile, 0, SEEK_SET))
645 {
646 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
647 rc = VINF_SUCCESS;
648 }
649 else
650 {
651 rc = RTErrConvertFromErrno(errno);
652 ASMAtomicWriteS32(&pStream->i32Error, rc);
653 }
654
655 return rc;
656}
657
658
659/**
660 * Recheck the stream mode.
661 *
662 * @param pStream The stream (locked).
663 */
664static void rtStreamRecheckMode(PRTSTREAM pStream)
665{
666#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
667 int fh = fileno(pStream->pFile);
668 if (fh >= 0)
669 {
670 int fExpected = pStream->fBinary ? _O_BINARY : _O_TEXT;
671 int fActual = _setmode(fh, fExpected);
672 if (fActual != -1 && fExpected != (fActual & (_O_BINARY | _O_TEXT)))
673 {
674 fActual = _setmode(fh, fActual & (_O_BINARY | _O_TEXT));
675 pStream->fBinary = !(fActual & _O_TEXT);
676 }
677 }
678#else
679 NOREF(pStream);
680#endif
681 pStream->fRecheckMode = false;
682}
683
684
685/**
686 * Reads from a file stream.
687 *
688 * @returns iprt status code.
689 * @param pStream The stream.
690 * @param pvBuf Where to put the read bits.
691 * Must be cbRead bytes or more.
692 * @param cbRead Number of bytes to read.
693 * @param pcbRead Where to store the number of bytes actually read.
694 * If NULL cbRead bytes are read or an error is returned.
695 */
696RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead)
697{
698 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
699
700 int rc = pStream->i32Error;
701 if (RT_SUCCESS(rc))
702 {
703 if (pStream->fRecheckMode)
704 rtStreamRecheckMode(pStream);
705
706 if (pcbRead)
707 {
708 /*
709 * Can do with a partial read.
710 */
711 *pcbRead = fread(pvBuf, 1, cbRead, pStream->pFile);
712 if ( *pcbRead == cbRead
713 || !ferror(pStream->pFile))
714 return VINF_SUCCESS;
715 if (feof(pStream->pFile))
716 {
717 if (*pcbRead)
718 return VINF_EOF;
719 rc = VERR_EOF;
720 }
721 else if (ferror(pStream->pFile))
722 rc = VERR_READ_ERROR;
723 else
724 {
725 AssertMsgFailed(("This shouldn't happen\n"));
726 rc = VERR_INTERNAL_ERROR;
727 }
728 }
729 else
730 {
731 /*
732 * Must read it all!
733 */
734 if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1)
735 return VINF_SUCCESS;
736
737 /* possible error/eof. */
738 if (feof(pStream->pFile))
739 rc = VERR_EOF;
740 else if (ferror(pStream->pFile))
741 rc = VERR_READ_ERROR;
742 else
743 {
744 AssertMsgFailed(("This shouldn't happen\n"));
745 rc = VERR_INTERNAL_ERROR;
746 }
747 }
748 ASMAtomicWriteS32(&pStream->i32Error, rc);
749 }
750 return rc;
751}
752
753
754/**
755 * Check if the input text is valid UTF-8.
756 *
757 * @returns true/false.
758 * @param pvBuf Pointer to the buffer.
759 * @param cbBuf Size of the buffer.
760 */
761static bool rtStrmIsUtf8Text(const void *pvBuf, size_t cbBuf)
762{
763 NOREF(pvBuf);
764 NOREF(cbBuf);
765 /** @todo not sure this is a good idea... Better redefine RTStrmWrite. */
766 return false;
767}
768
769
770#ifdef RT_OS_WINDOWS
771/**
772 * Check if the stream is for a Window console.
773 *
774 * @returns true / false.
775 * @param pStream The stream.
776 * @param phCon Where to return the console handle.
777 */
778static bool rtStrmIsConsoleUnlocked(PRTSTREAM pStream, HANDLE *phCon)
779{
780 int fh = fileno(pStream->pFile);
781 if (isatty(fh))
782 {
783 DWORD dwMode;
784 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
785 if (GetConsoleMode(hCon, &dwMode))
786 {
787 *phCon = hCon;
788 return true;
789 }
790 }
791 return false;
792}
793#endif /* RT_OS_WINDOWS */
794
795
796/**
797 * Internal write API, stream lock already held.
798 *
799 * @returns IPRT status code.
800 * @param pStream The stream.
801 * @param pvBuf What to write.
802 * @param cbWrite How much to write.
803 * @param pcbWritten Where to optionally return the number of bytes
804 * written.
805 * @param fSureIsText Set if we're sure this is UTF-8 text already.
806 */
807static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten,
808 bool fSureIsText)
809{
810 int rc = pStream->i32Error;
811 if (RT_FAILURE(rc))
812 return rc;
813 if (pStream->fRecheckMode)
814 rtStreamRecheckMode(pStream);
815
816#ifdef RT_OS_WINDOWS
817 /*
818 * Use the unicode console API when possible in order to avoid stuff
819 * getting lost in unnecessary code page translations.
820 */
821 HANDLE hCon;
822 if (rtStrmIsConsoleUnlocked(pStream, &hCon))
823 {
824# ifdef HAVE_FWRITE_UNLOCKED
825 if (!fflush_unlocked(pStream->pFile))
826# else
827 if (!fflush(pStream->pFile))
828# endif
829 {
830 /** @todo Consider buffering later. For now, we'd rather correct output than
831 * fast output. */
832 DWORD cwcWritten = 0;
833 PRTUTF16 pwszSrc = NULL;
834 size_t cwcSrc = 0;
835 rc = RTStrToUtf16Ex((const char *)pvBuf, cbWrite, &pwszSrc, 0, &cwcSrc);
836 if (RT_SUCCESS(rc))
837 {
838 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
839 {
840 /* try write char-by-char to avoid heap problem. */
841 cwcWritten = 0;
842 while (cwcWritten != cwcSrc)
843 {
844 DWORD cwcThis;
845 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
846 {
847 if (!pcbWritten || cwcWritten == 0)
848 rc = RTErrConvertFromErrno(GetLastError());
849 break;
850 }
851 if (cwcThis != 1) /* Unable to write current char (amount)? */
852 break;
853 cwcWritten++;
854 }
855 }
856 if (RT_SUCCESS(rc))
857 {
858 if (cwcWritten == cwcSrc)
859 {
860 if (pcbWritten)
861 *pcbWritten = cbWrite;
862 }
863 else if (pcbWritten)
864 {
865 PCRTUTF16 pwszCur = pwszSrc;
866 const char *pszCur = (const char *)pvBuf;
867 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
868 {
869 RTUNICP CpIgnored;
870 RTUtf16GetCpEx(&pwszCur, &CpIgnored);
871 RTStrGetCpEx(&pszCur, &CpIgnored);
872 }
873 *pcbWritten = pszCur - (const char *)pvBuf;
874 }
875 else
876 rc = VERR_WRITE_ERROR;
877 }
878 RTUtf16Free(pwszSrc);
879 }
880 }
881 else
882 rc = RTErrConvertFromErrno(errno);
883 if (RT_FAILURE(rc))
884 ASMAtomicWriteS32(&pStream->i32Error, rc);
885 return rc;
886 }
887#endif /* RT_OS_WINDOWS */
888
889 /*
890 * If we're sure it's text output, convert it from UTF-8 to the current
891 * code page before printing it.
892 *
893 * Note! Partial writes are not supported in this scenario because we
894 * cannot easily report back a written length matching the input.
895 */
896 /** @todo Skip this if the current code set is UTF-8. */
897 if ( pStream->fCurrentCodeSet
898 && !pStream->fBinary
899 && ( fSureIsText
900 || rtStrmIsUtf8Text(pvBuf, cbWrite))
901 )
902 {
903 char *pszSrcFree = NULL;
904 const char *pszSrc = (const char *)pvBuf;
905 if (pszSrc[cbWrite])
906 {
907 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbWrite);
908 if (pszSrc == NULL)
909 rc = VERR_NO_STR_MEMORY;
910 }
911 if (RT_SUCCESS(rc))
912 {
913 char *pszSrcCurCP;
914 rc = RTStrUtf8ToCurrentCP(&pszSrcCurCP, pszSrc);
915 if (RT_SUCCESS(rc))
916 {
917 size_t cchSrcCurCP = strlen(pszSrcCurCP);
918 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
919#ifdef HAVE_FWRITE_UNLOCKED
920 ssize_t cbWritten = fwrite_unlocked(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
921#else
922 ssize_t cbWritten = fwrite(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
923#endif
924 IPRT_ALIGNMENT_CHECKS_ENABLE();
925 if (cbWritten == 1)
926 {
927 if (pcbWritten)
928 *pcbWritten = cbWrite;
929 }
930#ifdef HAVE_FWRITE_UNLOCKED
931 else if (!ferror_unlocked(pStream->pFile))
932#else
933 else if (!ferror(pStream->pFile))
934#endif
935 {
936 if (pcbWritten)
937 *pcbWritten = 0;
938 }
939 else
940 rc = VERR_WRITE_ERROR;
941 RTStrFree(pszSrcCurCP);
942 }
943 RTStrFree(pszSrcFree);
944 }
945
946 if (RT_FAILURE(rc))
947 ASMAtomicWriteS32(&pStream->i32Error, rc);
948 return rc;
949 }
950
951 /*
952 * Otherwise, just write it as-is.
953 */
954 if (pcbWritten)
955 {
956 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
957#ifdef HAVE_FWRITE_UNLOCKED
958 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbWrite, pStream->pFile);
959#else
960 *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);
961#endif
962 IPRT_ALIGNMENT_CHECKS_ENABLE();
963 if ( *pcbWritten == cbWrite
964#ifdef HAVE_FWRITE_UNLOCKED
965 || !ferror_unlocked(pStream->pFile))
966#else
967 || !ferror(pStream->pFile))
968#endif
969 return VINF_SUCCESS;
970 rc = VERR_WRITE_ERROR;
971 }
972 else
973 {
974 /* Must write it all! */
975 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
976#ifdef HAVE_FWRITE_UNLOCKED
977 size_t cbWritten = fwrite_unlocked(pvBuf, cbWrite, 1, pStream->pFile);
978#else
979 size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile);
980#endif
981 IPRT_ALIGNMENT_CHECKS_ENABLE();
982 if (cbWritten == 1)
983 return VINF_SUCCESS;
984#ifdef HAVE_FWRITE_UNLOCKED
985 if (!ferror_unlocked(pStream->pFile))
986#else
987 if (!ferror(pStream->pFile))
988#endif
989 return VINF_SUCCESS; /* WEIRD! But anyway... */
990
991 rc = VERR_WRITE_ERROR;
992 }
993 ASMAtomicWriteS32(&pStream->i32Error, rc);
994
995 return rc;
996}
997
998
999/**
1000 * Internal write API.
1001 *
1002 * @returns IPRT status code.
1003 * @param pStream The stream.
1004 * @param pvBuf What to write.
1005 * @param cbWrite How much to write.
1006 * @param pcbWritten Where to optionally return the number of bytes
1007 * written.
1008 * @param fSureIsText Set if we're sure this is UTF-8 text already.
1009 */
1010static int rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, bool fSureIsText)
1011{
1012 rtStrmLock(pStream);
1013 int rc = rtStrmWriteLocked(pStream, pvBuf, cbWrite, pcbWritten, fSureIsText);
1014 rtStrmUnlock(pStream);
1015 return rc;
1016}
1017
1018
1019/**
1020 * Writes to a file stream.
1021 *
1022 * @returns iprt status code.
1023 * @param pStream The stream.
1024 * @param pvBuf Where to get the bits to write from.
1025 * @param cbWrite Number of bytes to write.
1026 * @param pcbWritten Where to store the number of bytes actually written.
1027 * If NULL cbWrite bytes are written or an error is returned.
1028 */
1029RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
1030{
1031 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1032 return rtStrmWrite(pStream, pvBuf, cbWrite, pcbWritten, false);
1033}
1034
1035
1036/**
1037 * Reads a character from a file stream.
1038 *
1039 * @returns The char as an unsigned char cast to int.
1040 * @returns -1 on failure.
1041 * @param pStream The stream.
1042 */
1043RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
1044{
1045 unsigned char ch;
1046 int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
1047 if (RT_SUCCESS(rc))
1048 return ch;
1049 return -1;
1050}
1051
1052
1053/**
1054 * Writes a character to a file stream.
1055 *
1056 * @returns iprt status code.
1057 * @param pStream The stream.
1058 * @param ch The char to write.
1059 */
1060RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
1061{
1062 return rtStrmWrite(pStream, &ch, 1, NULL, true /*fSureIsText*/);
1063}
1064
1065
1066/**
1067 * Writes a string to a file stream.
1068 *
1069 * @returns iprt status code.
1070 * @param pStream The stream.
1071 * @param pszString The string to write.
1072 * No newlines or anything is appended or prepended.
1073 * The terminating '\\0' is not written, of course.
1074 */
1075RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
1076{
1077 size_t cch = strlen(pszString);
1078 return rtStrmWrite(pStream, pszString, cch, NULL, true /*fSureIsText*/);
1079}
1080
1081
1082RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString)
1083{
1084 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1085 int rc;
1086 if (pszString && cbString > 1)
1087 {
1088 rc = pStream->i32Error;
1089 if (RT_SUCCESS(rc))
1090 {
1091 cbString--; /* save space for the terminator. */
1092 rtStrmLock(pStream);
1093 for (;;)
1094 {
1095#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
1096 int ch = fgetc_unlocked(pStream->pFile);
1097#else
1098 int ch = fgetc(pStream->pFile);
1099#endif
1100
1101 /* Deal with \r\n sequences here. We'll return lone CR, but
1102 treat CRLF as LF. */
1103 if (ch == '\r')
1104 {
1105#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
1106 ch = fgetc_unlocked(pStream->pFile);
1107#else
1108 ch = fgetc(pStream->pFile);
1109#endif
1110 if (ch == '\n')
1111 break;
1112
1113 *pszString++ = '\r';
1114 if (--cbString <= 0)
1115 {
1116 /* yeah, this is an error, we dropped a character. */
1117 rc = VERR_BUFFER_OVERFLOW;
1118 break;
1119 }
1120 }
1121
1122 /* Deal with end of file. */
1123 if (ch == EOF)
1124 {
1125#ifdef HAVE_FWRITE_UNLOCKED
1126 if (feof_unlocked(pStream->pFile))
1127#else
1128 if (feof(pStream->pFile))
1129#endif
1130 {
1131 rc = VERR_EOF;
1132 break;
1133 }
1134#ifdef HAVE_FWRITE_UNLOCKED
1135 if (ferror_unlocked(pStream->pFile))
1136#else
1137 if (ferror(pStream->pFile))
1138#endif
1139 rc = VERR_READ_ERROR;
1140 else
1141 {
1142 AssertMsgFailed(("This shouldn't happen\n"));
1143 rc = VERR_INTERNAL_ERROR;
1144 }
1145 break;
1146 }
1147
1148 /* Deal with null terminator and (lone) new line. */
1149 if (ch == '\0' || ch == '\n')
1150 break;
1151
1152 /* No special character, append it to the return string. */
1153 *pszString++ = ch;
1154 if (--cbString <= 0)
1155 {
1156 rc = VINF_BUFFER_OVERFLOW;
1157 break;
1158 }
1159 }
1160 rtStrmUnlock(pStream);
1161
1162 *pszString = '\0';
1163 if (RT_FAILURE(rc))
1164 ASMAtomicWriteS32(&pStream->i32Error, rc);
1165 }
1166 }
1167 else
1168 {
1169 AssertMsgFailed(("no buffer or too small buffer!\n"));
1170 rc = VERR_INVALID_PARAMETER;
1171 }
1172 return rc;
1173}
1174
1175
1176/**
1177 * Flushes a stream.
1178 *
1179 * @returns iprt status code.
1180 * @param pStream The stream to flush.
1181 */
1182RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
1183{
1184 if (!fflush(pStream->pFile))
1185 return VINF_SUCCESS;
1186 return RTErrConvertFromErrno(errno);
1187}
1188
1189
1190/**
1191 * Output callback.
1192 *
1193 * @returns number of bytes written.
1194 * @param pvArg User argument.
1195 * @param pachChars Pointer to an array of utf-8 characters.
1196 * @param cchChars Number of bytes in the character array pointed to by pachChars.
1197 */
1198static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
1199{
1200 if (cchChars)
1201 rtStrmWriteLocked((PRTSTREAM)pvArg, pachChars, cchChars, NULL, true /*fSureIsText*/);
1202 /* else: ignore termination call. */
1203 return cchChars;
1204}
1205
1206
1207/**
1208 * Prints a formatted string to the specified stream.
1209 *
1210 * @returns Number of bytes printed.
1211 * @param pStream The stream to print to.
1212 * @param pszFormat IPRT format string.
1213 * @param args Arguments specified by pszFormat.
1214 */
1215RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
1216{
1217 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1218 int rc = pStream->i32Error;
1219 if (RT_SUCCESS(rc))
1220 {
1221 rtStrmLock(pStream);
1222// pStream->fShouldFlush = true;
1223 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
1224 rtStrmUnlock(pStream);
1225 Assert(rc >= 0);
1226 }
1227 else
1228 rc = -1;
1229 return rc;
1230}
1231
1232
1233/**
1234 * Prints a formatted string to the specified stream.
1235 *
1236 * @returns Number of bytes printed.
1237 * @param pStream The stream to print to.
1238 * @param pszFormat IPRT format string.
1239 * @param ... Arguments specified by pszFormat.
1240 */
1241RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
1242{
1243 va_list args;
1244 va_start(args, pszFormat);
1245 int rc = RTStrmPrintfV(pStream, pszFormat, args);
1246 va_end(args);
1247 return rc;
1248}
1249
1250
1251/**
1252 * Dumper vprintf-like function outputting to a stream.
1253 *
1254 * @param pvUser The stream to print to. NULL means standard output.
1255 * @param pszFormat Runtime format string.
1256 * @param va Arguments specified by pszFormat.
1257 */
1258RTDECL(void) RTStrmDumpPrintfV(void *pvUser, const char *pszFormat, va_list va)
1259{
1260 RTStrmPrintfV(pvUser ? (PRTSTREAM)pvUser : g_pStdOut, pszFormat, va);
1261}
1262
1263
1264/**
1265 * Prints a formatted string to the standard output stream (g_pStdOut).
1266 *
1267 * @returns Number of bytes printed.
1268 * @param pszFormat IPRT format string.
1269 * @param args Arguments specified by pszFormat.
1270 */
1271RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
1272{
1273 return RTStrmPrintfV(g_pStdOut, pszFormat, args);
1274}
1275
1276
1277/**
1278 * Prints a formatted string to the standard output stream (g_pStdOut).
1279 *
1280 * @returns Number of bytes printed.
1281 * @param pszFormat IPRT format string.
1282 * @param ... Arguments specified by pszFormat.
1283 */
1284RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
1285{
1286 va_list args;
1287 va_start(args, pszFormat);
1288 int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
1289 va_end(args);
1290 return rc;
1291}
1292
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use