VirtualBox

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

Last change on this file since 57016 was 56290, checked in by vboxsync, 10 years ago

IPRT: Updated (C) year.

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