VirtualBox

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

Last change on this file since 39091 was 38904, checked in by vboxsync, 13 years ago

some small memory leaks in error cases and additional NULL checks

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