VirtualBox

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

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

iprt/stream.h: Added RTStrSetMode. (untested)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.7 KB
Line 
1/* $Id: stream.cpp 39377 2011-11-21 11:25:39Z 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 /*.fCurrentCodeSet = */ 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 /*.fCurrentCodeSet = */ 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 /*.fCurrentCodeSet = */ 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
435RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
436{
437 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
438 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
439 AssertReturn(fBinary == true || fBinary == false || fBinary == -1, VERR_INVALID_PARAMETER);
440 AssertReturn(fCurrentCodeSet == true || fCurrentCodeSet == false || fCurrentCodeSet == -1, VERR_INVALID_PARAMETER);
441
442 rtStrmLock(pStream);
443
444 if (fBinary != -1)
445 pStream->fBinary = fBinary != false;
446 if (fCurrentCodeSet != -1)
447 pStream->fCurrentCodeSet = fCurrentCodeSet != false;
448
449 rtStrmUnlock(pStream);
450
451 return VINF_SUCCESS;
452}
453
454
455/**
456 * Rewinds the stream.
457 *
458 * Stream errors will be reset on success.
459 *
460 * @returns IPRT status code.
461 *
462 * @param pStream The stream.
463 *
464 * @remarks Not all streams are rewindable and that behavior is currently
465 * undefined for those.
466 */
467RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
468{
469 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
470 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
471
472 int rc;
473 clearerr(pStream->pFile);
474 errno = 0;
475 if (!fseek(pStream->pFile, 0, SEEK_SET))
476 {
477 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
478 rc = VINF_SUCCESS;
479 }
480 else
481 {
482 rc = RTErrConvertFromErrno(errno);
483 ASMAtomicWriteS32(&pStream->i32Error, rc);
484 }
485
486 return rc;
487}
488
489
490/**
491 * Recheck the stream mode.
492 *
493 * @param pStream The stream (locked).
494 */
495static void rtStreamRecheckMode(PRTSTREAM pStream)
496{
497#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
498 int fh = fileno(pStream->pFile);
499 if (fh >= 0)
500 {
501 int fExpected = pStream->fBinary ? _O_BINARY : _O_TEXT;
502 int fActual = _setmode(fh, fExpected);
503 if (fActual != -1 && fExpected != (fActual & (_O_BINARY | _O_TEXT)))
504 {
505 _setmode(fh, fActual & (_O_BINARY | _O_TEXT));
506 pStream->fBinary = !(fActual & _O_TEXT);
507 }
508 }
509#else
510 NOREF(pStream);
511#endif
512 pStream->fRecheckMode = false;
513}
514
515
516/**
517 * Reads from a file stream.
518 *
519 * @returns iprt status code.
520 * @param pStream The stream.
521 * @param pvBuf Where to put the read bits.
522 * Must be cbRead bytes or more.
523 * @param cbRead Number of bytes to read.
524 * @param pcbRead Where to store the number of bytes actually read.
525 * If NULL cbRead bytes are read or an error is returned.
526 */
527RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead)
528{
529 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
530
531 int rc = pStream->i32Error;
532 if (RT_SUCCESS(rc))
533 {
534 if (pStream->fRecheckMode)
535 rtStreamRecheckMode(pStream);
536
537 if (pcbRead)
538 {
539 /*
540 * Can do with a partial read.
541 */
542 *pcbRead = fread(pvBuf, 1, cbRead, pStream->pFile);
543 if ( *pcbRead == cbRead
544 || !ferror(pStream->pFile))
545 return VINF_SUCCESS;
546 if (feof(pStream->pFile))
547 {
548 if (*pcbRead)
549 return VINF_EOF;
550 rc = VERR_EOF;
551 }
552 else if (ferror(pStream->pFile))
553 rc = VERR_READ_ERROR;
554 else
555 {
556 AssertMsgFailed(("This shouldn't happen\n"));
557 rc = VERR_INTERNAL_ERROR;
558 }
559 }
560 else
561 {
562 /*
563 * Must read it all!
564 */
565 if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1)
566 return VINF_SUCCESS;
567
568 /* possible error/eof. */
569 if (feof(pStream->pFile))
570 rc = VERR_EOF;
571 else if (ferror(pStream->pFile))
572 rc = VERR_READ_ERROR;
573 else
574 {
575 AssertMsgFailed(("This shouldn't happen\n"));
576 rc = VERR_INTERNAL_ERROR;
577 }
578 }
579 ASMAtomicWriteS32(&pStream->i32Error, rc);
580 }
581 return rc;
582}
583
584
585/**
586 * Check if the input text is valid UTF-8.
587 *
588 * @returns true/false.
589 * @param pvBuf Pointer to the buffer.
590 * @param cbBuf Size of the buffer.
591 */
592static bool rtStrmIsUtf8Text(const void *pvBuf, size_t cbBuf)
593{
594 NOREF(pvBuf);
595 NOREF(cbBuf);
596 /** @todo not sure this is a good idea... Better redefine RTStrmWrite. */
597 return false;
598}
599
600
601#ifdef RT_OS_WINDOWS
602/**
603 * Check if the stream is for a Window console.
604 *
605 * @returns true / false.
606 * @param pStream The stream.
607 * @param phCon Where to return the console handle.
608 */
609static bool rtStrmIsConsoleUnlocked(PRTSTREAM pStream, HANDLE *phCon)
610{
611 int fh = fileno(pStream->pFile);
612 if (isatty(fh))
613 {
614 DWORD dwMode;
615 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
616 if (GetConsoleMode(hCon, &dwMode))
617 {
618 *phCon = hCon;
619 return true;
620 }
621 }
622 return false;
623}
624#endif /* RT_OS_WINDOWS */
625
626
627/**
628 * Internal write API, stream lock already held.
629 *
630 * @returns IPRT status code.
631 * @param pStream The stream.
632 * @param pvBuf What to write.
633 * @param cbWrite How much to write.
634 * @param pcbWritten Where to optionally return the number of bytes
635 * written.
636 * @param fSureIsText Set if we're sure this is UTF-8 text already.
637 */
638static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten,
639 bool fSureIsText)
640{
641 int rc = pStream->i32Error;
642 if (RT_FAILURE(rc))
643 return rc;
644 if (pStream->fRecheckMode)
645 rtStreamRecheckMode(pStream);
646
647#ifdef RT_OS_WINDOWS
648 /*
649 * Use the unicode console API when possible in order to avoid stuff
650 * getting lost in unnecessary code page translations.
651 */
652 HANDLE hCon;
653 if (rtStrmIsConsoleUnlocked(pStream, &hCon))
654 {
655# ifdef HAVE_FWRITE_UNLOCKED
656 if (!fflush_unlocked(pStream->pFile))
657# else
658 if (!fflush(pStream->pFile))
659# endif
660 {
661 /** @todo Consider buffering later. For now, we'd rather correct output than
662 * fast output. */
663 DWORD cwcWritten = 0;
664 PRTUTF16 pwszSrc = NULL;
665 size_t cwcSrc = 0;
666 rc = RTStrToUtf16Ex((const char *)pvBuf, cbWrite, &pwszSrc, 0, &cwcSrc);
667 if (RT_SUCCESS(rc))
668 {
669 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
670 {
671 /* try write char-by-char to avoid heap problem. */
672 cwcWritten = 0;
673 while (cwcWritten != cwcSrc)
674 {
675 DWORD cwcThis;
676 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
677 {
678 if (!pcbWritten || cwcWritten == 0)
679 rc = RTErrConvertFromErrno(GetLastError());
680 break;
681 }
682 if (cwcThis != 0)
683 break;
684 cwcWritten++;
685 }
686 }
687 if (RT_SUCCESS(rc))
688 {
689 if (cwcWritten == cwcSrc)
690 {
691 if (pcbWritten)
692 *pcbWritten = cbWrite;
693 }
694 else if (pcbWritten)
695 {
696 PCRTUTF16 pwszCur = pwszSrc;
697 const char *pszCur = (const char *)pvBuf;
698 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
699 {
700 RTUNICP CpIgnored;
701 RTUtf16GetCpEx(&pwszCur, &CpIgnored);
702 RTStrGetCpEx(&pszCur, &CpIgnored);
703 }
704 *pcbWritten = pszCur - (const char *)pvBuf;
705 }
706 else
707 rc = VERR_WRITE_ERROR;
708 }
709 RTUtf16Free(pwszSrc);
710 }
711 }
712 else
713 rc = RTErrConvertFromErrno(errno);
714 if (RT_FAILURE(rc))
715 ASMAtomicWriteS32(&pStream->i32Error, rc);
716 return rc;
717 }
718#endif /* RT_OS_WINDOWS */
719
720 /*
721 * If we're sure it's text output, convert it from UTF-8 to the current
722 * code page before printing it.
723 *
724 * Note! Partial writes are not supported in this scenario because we
725 * cannot easily report back a written length matching the input.
726 */
727 /** @todo Skip this if the current code set is UTF-8. */
728 if ( pStream->fCurrentCodeSet
729 && !pStream->fBinary
730 && ( fSureIsText
731 || rtStrmIsUtf8Text(pvBuf, cbWrite))
732 )
733 {
734 char *pszSrcFree = NULL;
735 const char *pszSrc = (const char *)pvBuf;
736 if (pszSrc[cbWrite])
737 {
738 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbWrite);
739 if (pszSrc == NULL)
740 rc = VERR_NO_STR_MEMORY;
741 }
742 if (RT_SUCCESS(rc))
743 {
744 char *pszSrcCurCP;
745 rc = RTStrUtf8ToCurrentCP(&pszSrcCurCP, pszSrc);
746 if (RT_SUCCESS(rc))
747 {
748 size_t cchSrcCurCP = strlen(pszSrcCurCP);
749 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
750#ifdef HAVE_FWRITE_UNLOCKED
751 ssize_t cbWritten = fwrite_unlocked(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
752#else
753 ssize_t cbWritten = fwrite(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
754#endif
755 IPRT_ALIGNMENT_CHECKS_ENABLE();
756 if (cbWritten == 1)
757 {
758 if (pcbWritten)
759 *pcbWritten = cbWrite;
760 }
761#ifdef HAVE_FWRITE_UNLOCKED
762 else if (!ferror_unlocked(pStream->pFile))
763#else
764 else if (!ferror(pStream->pFile))
765#endif
766 {
767 if (pcbWritten)
768 *pcbWritten = 0;
769 }
770 else
771 rc = VERR_WRITE_ERROR;
772 RTStrFree(pszSrcCurCP);
773 }
774 RTStrFree(pszSrcFree);
775 }
776
777 if (RT_FAILURE(rc))
778 ASMAtomicWriteS32(&pStream->i32Error, rc);
779 return rc;
780 }
781
782 /*
783 * Otherwise, just write it as-is.
784 */
785 if (pcbWritten)
786 {
787 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
788#ifdef HAVE_FWRITE_UNLOCKED
789 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbWrite, pStream->pFile);
790#else
791 *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);
792#endif
793 IPRT_ALIGNMENT_CHECKS_ENABLE();
794 if ( *pcbWritten == cbWrite
795#ifdef HAVE_FWRITE_UNLOCKED
796 || !ferror_unlocked(pStream->pFile))
797#else
798 || !ferror(pStream->pFile))
799#endif
800 return VINF_SUCCESS;
801 rc = VERR_WRITE_ERROR;
802 }
803 else
804 {
805 /* Must write it all! */
806 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
807#ifdef HAVE_FWRITE_UNLOCKED
808 size_t cbWritten = fwrite_unlocked(pvBuf, cbWrite, 1, pStream->pFile);
809#else
810 size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile);
811#endif
812 IPRT_ALIGNMENT_CHECKS_ENABLE();
813 if (cbWritten == 1)
814 return VINF_SUCCESS;
815#ifdef HAVE_FWRITE_UNLOCKED
816 if (!ferror_unlocked(pStream->pFile))
817#else
818 if (!ferror(pStream->pFile))
819#endif
820 return VINF_SUCCESS; /* WEIRD! But anyway... */
821
822 rc = VERR_WRITE_ERROR;
823 }
824 ASMAtomicWriteS32(&pStream->i32Error, rc);
825
826 return rc;
827}
828
829
830/**
831 * Internal write API.
832 *
833 * @returns IPRT status code.
834 * @param pStream The stream.
835 * @param pvBuf What to write.
836 * @param cbWrite How much to write.
837 * @param pcbWritten Where to optionally return the number of bytes
838 * written.
839 * @param fSureIsText Set if we're sure this is UTF-8 text already.
840 */
841static int rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, bool fSureIsText)
842{
843 rtStrmLock(pStream);
844 int rc = rtStrmWriteLocked(pStream, pvBuf, cbWrite, pcbWritten, fSureIsText);
845 rtStrmUnlock(pStream);
846 return rc;
847}
848
849
850/**
851 * Writes to a file stream.
852 *
853 * @returns iprt status code.
854 * @param pStream The stream.
855 * @param pvBuf Where to get the bits to write from.
856 * @param cbWrite Number of bytes to write.
857 * @param pcbWritten Where to store the number of bytes actually written.
858 * If NULL cbWrite bytes are written or an error is returned.
859 */
860RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
861{
862 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
863 return rtStrmWrite(pStream, pvBuf, cbWrite, pcbWritten, false);
864}
865
866
867/**
868 * Reads a character from a file stream.
869 *
870 * @returns The char as an unsigned char cast to int.
871 * @returns -1 on failure.
872 * @param pStream The stream.
873 */
874RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
875{
876 unsigned char ch;
877 int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
878 if (RT_SUCCESS(rc))
879 return ch;
880 return -1;
881}
882
883
884/**
885 * Writes a character to a file stream.
886 *
887 * @returns iprt status code.
888 * @param pStream The stream.
889 * @param ch The char to write.
890 */
891RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
892{
893 return rtStrmWrite(pStream, &ch, 1, NULL, true /*fSureIsText*/);
894}
895
896
897/**
898 * Writes a string to a file stream.
899 *
900 * @returns iprt status code.
901 * @param pStream The stream.
902 * @param pszString The string to write.
903 * No newlines or anything is appended or prepended.
904 * The terminating '\\0' is not written, of course.
905 */
906RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
907{
908 size_t cch = strlen(pszString);
909 return rtStrmWrite(pStream, pszString, cch, NULL, true /*fSureIsText*/);
910}
911
912
913/**
914 * Reads a line from a file stream.
915 * A line ends with a '\\n', '\\0' or the end of the file.
916 *
917 * @returns iprt status code.
918 * @returns VINF_BUFFER_OVERFLOW if the buffer wasn't big enough to read an entire line.
919 * @param pStream The stream.
920 * @param pszString Where to store the line.
921 * The line will *NOT* contain any '\\n'.
922 * @param cchString The size of the string buffer.
923 */
924RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cchString)
925{
926 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
927 int rc;
928 if (pszString && cchString > 1)
929 {
930 rc = pStream->i32Error;
931 if (RT_SUCCESS(rc))
932 {
933 cchString--; /* save space for the terminator. */
934 rtStrmLock(pStream);
935 for (;;)
936 {
937#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
938 int ch = fgetc_unlocked(pStream->pFile);
939#else
940 int ch = fgetc(pStream->pFile);
941#endif
942 if (ch == EOF)
943 {
944#ifdef HAVE_FWRITE_UNLOCKED
945 if (feof_unlocked(pStream->pFile))
946#else
947 if (feof(pStream->pFile))
948#endif
949 {
950 rc = VERR_EOF;
951 break;
952 }
953#ifdef HAVE_FWRITE_UNLOCKED
954 if (ferror_unlocked(pStream->pFile))
955#else
956 if (ferror(pStream->pFile))
957#endif
958 rc = VERR_READ_ERROR;
959 else
960 {
961 AssertMsgFailed(("This shouldn't happen\n"));
962 rc = VERR_INTERNAL_ERROR;
963 }
964 break;
965 }
966 if (ch == '\0' || ch == '\n' || ch == '\r')
967 break;
968 *pszString++ = ch;
969 if (--cchString <= 0)
970 {
971 rc = VINF_BUFFER_OVERFLOW;
972 break;
973 }
974 }
975 rtStrmUnlock(pStream);
976
977 *pszString = '\0';
978 if (RT_FAILURE(rc))
979 ASMAtomicWriteS32(&pStream->i32Error, rc);
980 }
981 }
982 else
983 {
984 AssertMsgFailed(("no buffer or too small buffer!\n"));
985 rc = VERR_INVALID_PARAMETER;
986 }
987 return rc;
988}
989
990
991/**
992 * Flushes a stream.
993 *
994 * @returns iprt status code.
995 * @param pStream The stream to flush.
996 */
997RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
998{
999 if (!fflush(pStream->pFile))
1000 return VINF_SUCCESS;
1001 return RTErrConvertFromErrno(errno);
1002}
1003
1004
1005/**
1006 * Output callback.
1007 *
1008 * @returns number of bytes written.
1009 * @param pvArg User argument.
1010 * @param pachChars Pointer to an array of utf-8 characters.
1011 * @param cchChars Number of bytes in the character array pointed to by pachChars.
1012 */
1013static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
1014{
1015 if (cchChars)
1016 rtStrmWriteLocked((PRTSTREAM)pvArg, pachChars, cchChars, NULL, true /*fSureIsText*/);
1017 /* else: ignore termination call. */
1018 return cchChars;
1019}
1020
1021
1022/**
1023 * Prints a formatted string to the specified stream.
1024 *
1025 * @returns Number of bytes printed.
1026 * @param pStream The stream to print to.
1027 * @param pszFormat IPRT format string.
1028 * @param args Arguments specified by pszFormat.
1029 */
1030RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
1031{
1032 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1033 int rc = pStream->i32Error;
1034 if (RT_SUCCESS(rc))
1035 {
1036 rtStrmLock(pStream);
1037// pStream->fShouldFlush = true;
1038 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
1039 rtStrmUnlock(pStream);
1040 Assert(rc >= 0);
1041 }
1042 else
1043 rc = -1;
1044 return rc;
1045}
1046
1047
1048/**
1049 * Prints a formatted string to the specified stream.
1050 *
1051 * @returns Number of bytes printed.
1052 * @param pStream The stream to print to.
1053 * @param pszFormat IPRT format string.
1054 * @param ... Arguments specified by pszFormat.
1055 */
1056RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
1057{
1058 va_list args;
1059 va_start(args, pszFormat);
1060 int rc = RTStrmPrintfV(pStream, pszFormat, args);
1061 va_end(args);
1062 return rc;
1063}
1064
1065
1066/**
1067 * Prints a formatted string to the standard output stream (g_pStdOut).
1068 *
1069 * @returns Number of bytes printed.
1070 * @param pszFormat IPRT format string.
1071 * @param args Arguments specified by pszFormat.
1072 */
1073RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
1074{
1075 return RTStrmPrintfV(g_pStdOut, pszFormat, args);
1076}
1077
1078
1079/**
1080 * Prints a formatted string to the standard output stream (g_pStdOut).
1081 *
1082 * @returns Number of bytes printed.
1083 * @param pszFormat IPRT format string.
1084 * @param ... Arguments specified by pszFormat.
1085 */
1086RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
1087{
1088 va_list args;
1089 va_start(args, pszFormat);
1090 int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
1091 va_end(args);
1092 return rc;
1093}
1094
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