VirtualBox

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

Last change on this file since 62571 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 34.5 KB
Line 
1/* $Id: stream.cpp 62477 2016-07-22 18:27:37Z vboxsync $ */
2/** @file
3 * IPRT - I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 <Windows.h>
62#endif
63#ifndef RT_OS_WINDOWS
64# include <termios.h>
65# include <unistd.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
557/**
558 * Rewinds the stream.
559 *
560 * Stream errors will be reset on success.
561 *
562 * @returns IPRT status code.
563 *
564 * @param pStream The stream.
565 *
566 * @remarks Not all streams are rewindable and that behavior is currently
567 * undefined for those.
568 */
569RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
570{
571 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
572 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
573
574 int rc;
575 clearerr(pStream->pFile);
576 errno = 0;
577 if (!fseek(pStream->pFile, 0, SEEK_SET))
578 {
579 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
580 rc = VINF_SUCCESS;
581 }
582 else
583 {
584 rc = RTErrConvertFromErrno(errno);
585 ASMAtomicWriteS32(&pStream->i32Error, rc);
586 }
587
588 return rc;
589}
590
591
592/**
593 * Recheck the stream mode.
594 *
595 * @param pStream The stream (locked).
596 */
597static void rtStreamRecheckMode(PRTSTREAM pStream)
598{
599#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
600 int fh = fileno(pStream->pFile);
601 if (fh >= 0)
602 {
603 int fExpected = pStream->fBinary ? _O_BINARY : _O_TEXT;
604 int fActual = _setmode(fh, fExpected);
605 if (fActual != -1 && fExpected != (fActual & (_O_BINARY | _O_TEXT)))
606 {
607 fActual = _setmode(fh, fActual & (_O_BINARY | _O_TEXT));
608 pStream->fBinary = !(fActual & _O_TEXT);
609 }
610 }
611#else
612 NOREF(pStream);
613#endif
614 pStream->fRecheckMode = false;
615}
616
617
618/**
619 * Reads from a file stream.
620 *
621 * @returns iprt status code.
622 * @param pStream The stream.
623 * @param pvBuf Where to put the read bits.
624 * Must be cbRead bytes or more.
625 * @param cbRead Number of bytes to read.
626 * @param pcbRead Where to store the number of bytes actually read.
627 * If NULL cbRead bytes are read or an error is returned.
628 */
629RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead)
630{
631 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
632
633 int rc = pStream->i32Error;
634 if (RT_SUCCESS(rc))
635 {
636 if (pStream->fRecheckMode)
637 rtStreamRecheckMode(pStream);
638
639 if (pcbRead)
640 {
641 /*
642 * Can do with a partial read.
643 */
644 *pcbRead = fread(pvBuf, 1, cbRead, pStream->pFile);
645 if ( *pcbRead == cbRead
646 || !ferror(pStream->pFile))
647 return VINF_SUCCESS;
648 if (feof(pStream->pFile))
649 {
650 if (*pcbRead)
651 return VINF_EOF;
652 rc = VERR_EOF;
653 }
654 else if (ferror(pStream->pFile))
655 rc = VERR_READ_ERROR;
656 else
657 {
658 AssertMsgFailed(("This shouldn't happen\n"));
659 rc = VERR_INTERNAL_ERROR;
660 }
661 }
662 else
663 {
664 /*
665 * Must read it all!
666 */
667 if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1)
668 return VINF_SUCCESS;
669
670 /* possible error/eof. */
671 if (feof(pStream->pFile))
672 rc = VERR_EOF;
673 else if (ferror(pStream->pFile))
674 rc = VERR_READ_ERROR;
675 else
676 {
677 AssertMsgFailed(("This shouldn't happen\n"));
678 rc = VERR_INTERNAL_ERROR;
679 }
680 }
681 ASMAtomicWriteS32(&pStream->i32Error, rc);
682 }
683 return rc;
684}
685
686
687/**
688 * Check if the input text is valid UTF-8.
689 *
690 * @returns true/false.
691 * @param pvBuf Pointer to the buffer.
692 * @param cbBuf Size of the buffer.
693 */
694static bool rtStrmIsUtf8Text(const void *pvBuf, size_t cbBuf)
695{
696 NOREF(pvBuf);
697 NOREF(cbBuf);
698 /** @todo not sure this is a good idea... Better redefine RTStrmWrite. */
699 return false;
700}
701
702
703#ifdef RT_OS_WINDOWS
704/**
705 * Check if the stream is for a Window console.
706 *
707 * @returns true / false.
708 * @param pStream The stream.
709 * @param phCon Where to return the console handle.
710 */
711static bool rtStrmIsConsoleUnlocked(PRTSTREAM pStream, HANDLE *phCon)
712{
713 int fh = fileno(pStream->pFile);
714 if (isatty(fh))
715 {
716 DWORD dwMode;
717 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
718 if (GetConsoleMode(hCon, &dwMode))
719 {
720 *phCon = hCon;
721 return true;
722 }
723 }
724 return false;
725}
726#endif /* RT_OS_WINDOWS */
727
728
729/**
730 * Internal write API, stream lock already held.
731 *
732 * @returns IPRT status code.
733 * @param pStream The stream.
734 * @param pvBuf What to write.
735 * @param cbWrite How much to write.
736 * @param pcbWritten Where to optionally return the number of bytes
737 * written.
738 * @param fSureIsText Set if we're sure this is UTF-8 text already.
739 */
740static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten,
741 bool fSureIsText)
742{
743 int rc = pStream->i32Error;
744 if (RT_FAILURE(rc))
745 return rc;
746 if (pStream->fRecheckMode)
747 rtStreamRecheckMode(pStream);
748
749#ifdef RT_OS_WINDOWS
750 /*
751 * Use the unicode console API when possible in order to avoid stuff
752 * getting lost in unnecessary code page translations.
753 */
754 HANDLE hCon;
755 if (rtStrmIsConsoleUnlocked(pStream, &hCon))
756 {
757# ifdef HAVE_FWRITE_UNLOCKED
758 if (!fflush_unlocked(pStream->pFile))
759# else
760 if (!fflush(pStream->pFile))
761# endif
762 {
763 /** @todo Consider buffering later. For now, we'd rather correct output than
764 * fast output. */
765 DWORD cwcWritten = 0;
766 PRTUTF16 pwszSrc = NULL;
767 size_t cwcSrc = 0;
768 rc = RTStrToUtf16Ex((const char *)pvBuf, cbWrite, &pwszSrc, 0, &cwcSrc);
769 if (RT_SUCCESS(rc))
770 {
771 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
772 {
773 /* try write char-by-char to avoid heap problem. */
774 cwcWritten = 0;
775 while (cwcWritten != cwcSrc)
776 {
777 DWORD cwcThis;
778 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
779 {
780 if (!pcbWritten || cwcWritten == 0)
781 rc = RTErrConvertFromErrno(GetLastError());
782 break;
783 }
784 if (cwcThis != 1) /* Unable to write current char (amount)? */
785 break;
786 cwcWritten++;
787 }
788 }
789 if (RT_SUCCESS(rc))
790 {
791 if (cwcWritten == cwcSrc)
792 {
793 if (pcbWritten)
794 *pcbWritten = cbWrite;
795 }
796 else if (pcbWritten)
797 {
798 PCRTUTF16 pwszCur = pwszSrc;
799 const char *pszCur = (const char *)pvBuf;
800 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
801 {
802 RTUNICP CpIgnored;
803 RTUtf16GetCpEx(&pwszCur, &CpIgnored);
804 RTStrGetCpEx(&pszCur, &CpIgnored);
805 }
806 *pcbWritten = pszCur - (const char *)pvBuf;
807 }
808 else
809 rc = VERR_WRITE_ERROR;
810 }
811 RTUtf16Free(pwszSrc);
812 }
813 }
814 else
815 rc = RTErrConvertFromErrno(errno);
816 if (RT_FAILURE(rc))
817 ASMAtomicWriteS32(&pStream->i32Error, rc);
818 return rc;
819 }
820#endif /* RT_OS_WINDOWS */
821
822 /*
823 * If we're sure it's text output, convert it from UTF-8 to the current
824 * code page before printing it.
825 *
826 * Note! Partial writes are not supported in this scenario because we
827 * cannot easily report back a written length matching the input.
828 */
829 /** @todo Skip this if the current code set is UTF-8. */
830 if ( pStream->fCurrentCodeSet
831 && !pStream->fBinary
832 && ( fSureIsText
833 || rtStrmIsUtf8Text(pvBuf, cbWrite))
834 )
835 {
836 char *pszSrcFree = NULL;
837 const char *pszSrc = (const char *)pvBuf;
838 if (pszSrc[cbWrite])
839 {
840 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbWrite);
841 if (pszSrc == NULL)
842 rc = VERR_NO_STR_MEMORY;
843 }
844 if (RT_SUCCESS(rc))
845 {
846 char *pszSrcCurCP;
847 rc = RTStrUtf8ToCurrentCP(&pszSrcCurCP, pszSrc);
848 if (RT_SUCCESS(rc))
849 {
850 size_t cchSrcCurCP = strlen(pszSrcCurCP);
851 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
852#ifdef HAVE_FWRITE_UNLOCKED
853 ssize_t cbWritten = fwrite_unlocked(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
854#else
855 ssize_t cbWritten = fwrite(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
856#endif
857 IPRT_ALIGNMENT_CHECKS_ENABLE();
858 if (cbWritten == 1)
859 {
860 if (pcbWritten)
861 *pcbWritten = cbWrite;
862 }
863#ifdef HAVE_FWRITE_UNLOCKED
864 else if (!ferror_unlocked(pStream->pFile))
865#else
866 else if (!ferror(pStream->pFile))
867#endif
868 {
869 if (pcbWritten)
870 *pcbWritten = 0;
871 }
872 else
873 rc = VERR_WRITE_ERROR;
874 RTStrFree(pszSrcCurCP);
875 }
876 RTStrFree(pszSrcFree);
877 }
878
879 if (RT_FAILURE(rc))
880 ASMAtomicWriteS32(&pStream->i32Error, rc);
881 return rc;
882 }
883
884 /*
885 * Otherwise, just write it as-is.
886 */
887 if (pcbWritten)
888 {
889 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
890#ifdef HAVE_FWRITE_UNLOCKED
891 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbWrite, pStream->pFile);
892#else
893 *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);
894#endif
895 IPRT_ALIGNMENT_CHECKS_ENABLE();
896 if ( *pcbWritten == cbWrite
897#ifdef HAVE_FWRITE_UNLOCKED
898 || !ferror_unlocked(pStream->pFile))
899#else
900 || !ferror(pStream->pFile))
901#endif
902 return VINF_SUCCESS;
903 rc = VERR_WRITE_ERROR;
904 }
905 else
906 {
907 /* Must write it all! */
908 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
909#ifdef HAVE_FWRITE_UNLOCKED
910 size_t cbWritten = fwrite_unlocked(pvBuf, cbWrite, 1, pStream->pFile);
911#else
912 size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile);
913#endif
914 IPRT_ALIGNMENT_CHECKS_ENABLE();
915 if (cbWritten == 1)
916 return VINF_SUCCESS;
917#ifdef HAVE_FWRITE_UNLOCKED
918 if (!ferror_unlocked(pStream->pFile))
919#else
920 if (!ferror(pStream->pFile))
921#endif
922 return VINF_SUCCESS; /* WEIRD! But anyway... */
923
924 rc = VERR_WRITE_ERROR;
925 }
926 ASMAtomicWriteS32(&pStream->i32Error, rc);
927
928 return rc;
929}
930
931
932/**
933 * Internal write API.
934 *
935 * @returns IPRT status code.
936 * @param pStream The stream.
937 * @param pvBuf What to write.
938 * @param cbWrite How much to write.
939 * @param pcbWritten Where to optionally return the number of bytes
940 * written.
941 * @param fSureIsText Set if we're sure this is UTF-8 text already.
942 */
943static int rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, bool fSureIsText)
944{
945 rtStrmLock(pStream);
946 int rc = rtStrmWriteLocked(pStream, pvBuf, cbWrite, pcbWritten, fSureIsText);
947 rtStrmUnlock(pStream);
948 return rc;
949}
950
951
952/**
953 * Writes to a file stream.
954 *
955 * @returns iprt status code.
956 * @param pStream The stream.
957 * @param pvBuf Where to get the bits to write from.
958 * @param cbWrite Number of bytes to write.
959 * @param pcbWritten Where to store the number of bytes actually written.
960 * If NULL cbWrite bytes are written or an error is returned.
961 */
962RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
963{
964 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
965 return rtStrmWrite(pStream, pvBuf, cbWrite, pcbWritten, false);
966}
967
968
969/**
970 * Reads a character from a file stream.
971 *
972 * @returns The char as an unsigned char cast to int.
973 * @returns -1 on failure.
974 * @param pStream The stream.
975 */
976RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
977{
978 unsigned char ch;
979 int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
980 if (RT_SUCCESS(rc))
981 return ch;
982 return -1;
983}
984
985
986/**
987 * Writes a character to a file stream.
988 *
989 * @returns iprt status code.
990 * @param pStream The stream.
991 * @param ch The char to write.
992 */
993RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
994{
995 return rtStrmWrite(pStream, &ch, 1, NULL, true /*fSureIsText*/);
996}
997
998
999/**
1000 * Writes a string to a file stream.
1001 *
1002 * @returns iprt status code.
1003 * @param pStream The stream.
1004 * @param pszString The string to write.
1005 * No newlines or anything is appended or prepended.
1006 * The terminating '\\0' is not written, of course.
1007 */
1008RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
1009{
1010 size_t cch = strlen(pszString);
1011 return rtStrmWrite(pStream, pszString, cch, NULL, true /*fSureIsText*/);
1012}
1013
1014
1015RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString)
1016{
1017 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1018 int rc;
1019 if (pszString && cbString > 1)
1020 {
1021 rc = pStream->i32Error;
1022 if (RT_SUCCESS(rc))
1023 {
1024 cbString--; /* save space for the terminator. */
1025 rtStrmLock(pStream);
1026 for (;;)
1027 {
1028#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
1029 int ch = fgetc_unlocked(pStream->pFile);
1030#else
1031 int ch = fgetc(pStream->pFile);
1032#endif
1033
1034 /* Deal with \r\n sequences here. We'll return lone CR, but
1035 treat CRLF as LF. */
1036 if (ch == '\r')
1037 {
1038#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
1039 ch = fgetc_unlocked(pStream->pFile);
1040#else
1041 ch = fgetc(pStream->pFile);
1042#endif
1043 if (ch == '\n')
1044 break;
1045
1046 *pszString++ = '\r';
1047 if (--cbString <= 0)
1048 {
1049 /* yeah, this is an error, we dropped a character. */
1050 rc = VERR_BUFFER_OVERFLOW;
1051 break;
1052 }
1053 }
1054
1055 /* Deal with end of file. */
1056 if (ch == EOF)
1057 {
1058#ifdef HAVE_FWRITE_UNLOCKED
1059 if (feof_unlocked(pStream->pFile))
1060#else
1061 if (feof(pStream->pFile))
1062#endif
1063 {
1064 rc = VERR_EOF;
1065 break;
1066 }
1067#ifdef HAVE_FWRITE_UNLOCKED
1068 if (ferror_unlocked(pStream->pFile))
1069#else
1070 if (ferror(pStream->pFile))
1071#endif
1072 rc = VERR_READ_ERROR;
1073 else
1074 {
1075 AssertMsgFailed(("This shouldn't happen\n"));
1076 rc = VERR_INTERNAL_ERROR;
1077 }
1078 break;
1079 }
1080
1081 /* Deal with null terminator and (lone) new line. */
1082 if (ch == '\0' || ch == '\n')
1083 break;
1084
1085 /* No special character, append it to the return string. */
1086 *pszString++ = ch;
1087 if (--cbString <= 0)
1088 {
1089 rc = VINF_BUFFER_OVERFLOW;
1090 break;
1091 }
1092 }
1093 rtStrmUnlock(pStream);
1094
1095 *pszString = '\0';
1096 if (RT_FAILURE(rc))
1097 ASMAtomicWriteS32(&pStream->i32Error, rc);
1098 }
1099 }
1100 else
1101 {
1102 AssertMsgFailed(("no buffer or too small buffer!\n"));
1103 rc = VERR_INVALID_PARAMETER;
1104 }
1105 return rc;
1106}
1107
1108
1109/**
1110 * Flushes a stream.
1111 *
1112 * @returns iprt status code.
1113 * @param pStream The stream to flush.
1114 */
1115RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
1116{
1117 if (!fflush(pStream->pFile))
1118 return VINF_SUCCESS;
1119 return RTErrConvertFromErrno(errno);
1120}
1121
1122
1123/**
1124 * Output callback.
1125 *
1126 * @returns number of bytes written.
1127 * @param pvArg User argument.
1128 * @param pachChars Pointer to an array of utf-8 characters.
1129 * @param cchChars Number of bytes in the character array pointed to by pachChars.
1130 */
1131static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
1132{
1133 if (cchChars)
1134 rtStrmWriteLocked((PRTSTREAM)pvArg, pachChars, cchChars, NULL, true /*fSureIsText*/);
1135 /* else: ignore termination call. */
1136 return cchChars;
1137}
1138
1139
1140/**
1141 * Prints a formatted string to the specified stream.
1142 *
1143 * @returns Number of bytes printed.
1144 * @param pStream The stream to print to.
1145 * @param pszFormat IPRT format string.
1146 * @param args Arguments specified by pszFormat.
1147 */
1148RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
1149{
1150 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1151 int rc = pStream->i32Error;
1152 if (RT_SUCCESS(rc))
1153 {
1154 rtStrmLock(pStream);
1155// pStream->fShouldFlush = true;
1156 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
1157 rtStrmUnlock(pStream);
1158 Assert(rc >= 0);
1159 }
1160 else
1161 rc = -1;
1162 return rc;
1163}
1164
1165
1166/**
1167 * Prints a formatted string to the specified stream.
1168 *
1169 * @returns Number of bytes printed.
1170 * @param pStream The stream to print to.
1171 * @param pszFormat IPRT format string.
1172 * @param ... Arguments specified by pszFormat.
1173 */
1174RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
1175{
1176 va_list args;
1177 va_start(args, pszFormat);
1178 int rc = RTStrmPrintfV(pStream, pszFormat, args);
1179 va_end(args);
1180 return rc;
1181}
1182
1183
1184/**
1185 * Dumper vprintf-like function outputting to a stream.
1186 *
1187 * @param pvUser The stream to print to. NULL means standard output.
1188 * @param pszFormat Runtime format string.
1189 * @param va Arguments specified by pszFormat.
1190 */
1191RTDECL(void) RTStrmDumpPrintfV(void *pvUser, const char *pszFormat, va_list va)
1192{
1193 RTStrmPrintfV(pvUser ? (PRTSTREAM)pvUser : g_pStdOut, pszFormat, va);
1194}
1195
1196
1197/**
1198 * Prints a formatted string to the standard output stream (g_pStdOut).
1199 *
1200 * @returns Number of bytes printed.
1201 * @param pszFormat IPRT format string.
1202 * @param args Arguments specified by pszFormat.
1203 */
1204RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
1205{
1206 return RTStrmPrintfV(g_pStdOut, pszFormat, args);
1207}
1208
1209
1210/**
1211 * Prints a formatted string to the standard output stream (g_pStdOut).
1212 *
1213 * @returns Number of bytes printed.
1214 * @param pszFormat IPRT format string.
1215 * @param ... Arguments specified by pszFormat.
1216 */
1217RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
1218{
1219 va_list args;
1220 va_start(args, pszFormat);
1221 int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
1222 va_end(args);
1223 return rc;
1224}
1225
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