VirtualBox

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

Last change on this file since 74882 was 69111, checked in by vboxsync, 7 years ago

(C) year

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

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