VirtualBox

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

Last change on this file since 43129 was 41504, checked in by vboxsync, 12 years ago

RTStrmGetLine: Deal with correctly with \r\n, current handling is stupid as it returns two lines.

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