VirtualBox

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

Last change on this file since 48935 was 48935, checked in by vboxsync, 11 years ago

Runtime: Whitespace and svn:keyword cleanups by scm.

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