VirtualBox

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

Last change on this file since 25292 was 21045, checked in by vboxsync, 15 years ago

IPRT: Fixed RTStrmClearError so that it clears the error on the stream as well. Added RTStrmRewind. (Needed for #3897.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 20.3 KB
Line 
1/* $Id: stream.cpp 21045 2009-06-30 01:09:19Z vboxsync $ */
2/** @file
3 * IPRT - I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32
33/*******************************************************************************
34* Header Files *
35*******************************************************************************/
36#include <iprt/stream.h>
37#include <iprt/string.h>
38#include <iprt/asm.h>
39#include <iprt/assert.h>
40#include <iprt/alloc.h>
41#include <iprt/err.h>
42#include <iprt/param.h>
43#include <iprt/string.h>
44
45#include "internal/alignmentchecks.h"
46#include "internal/magics.h"
47
48#include <stdio.h>
49#include <errno.h>
50
51#if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */
52#define HAVE_FWRITE_UNLOCKED
53#endif
54
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59/**
60 * File stream.
61 */
62typedef struct RTSTREAM
63{
64 /** Magic value used to validate the stream. (RTSTREAM_MAGIC) */
65 uint32_t u32Magic;
66 /** File stream error. */
67 int32_t volatile i32Error;
68 /** Pointer to the LIBC file stream. */
69 FILE *pFile;
70} RTSTREAM;
71
72
73/*******************************************************************************
74* Global Variables *
75*******************************************************************************/
76/** The standard input stream. */
77static RTSTREAM g_StdIn =
78{
79 RTSTREAM_MAGIC,
80 0,
81 stdin
82};
83
84/** The standard error stream. */
85static RTSTREAM g_StdErr =
86{
87 RTSTREAM_MAGIC,
88 0,
89 stderr
90};
91
92/** The standard output stream. */
93static RTSTREAM g_StdOut =
94{
95 RTSTREAM_MAGIC,
96 0,
97 stdout
98};
99
100/** Pointer to the standard input stream. */
101RTDATADECL(PRTSTREAM) g_pStdIn = &g_StdIn;
102
103/** Pointer to the standard output stream. */
104RTDATADECL(PRTSTREAM) g_pStdErr = &g_StdErr;
105
106/** Pointer to the standard output stream. */
107RTDATADECL(PRTSTREAM) g_pStdOut = &g_StdOut;
108
109
110/**
111 * Opens a file stream.
112 *
113 * @returns iprt status code.
114 * @param pszFilename Path to the file to open.
115 * @param pszMode The open mode. See fopen() standard.
116 * Format: <a|r|w>[+][b|t]
117 * @param ppStream Where to store the opened stream.
118 */
119RTR3DECL(int) RTStrmOpen(const char *pszFilename, const char *pszMode, PRTSTREAM *ppStream)
120{
121 /*
122 * Validate input.
123 */
124 if (!pszMode || !*pszMode)
125 {
126 AssertMsgFailed(("No pszMode!\n"));
127 return VERR_INVALID_PARAMETER;
128 }
129 if (!pszFilename)
130 {
131 AssertMsgFailed(("No pszFilename!\n"));
132 return VERR_INVALID_PARAMETER;
133 }
134 bool fOk = true;
135 switch (*pszMode)
136 {
137 case 'a':
138 case 'w':
139 case 'r':
140 switch (pszMode[1])
141 {
142 case '\0':
143 break;
144 case '+':
145 switch (pszMode[2])
146 {
147 case '\0':
148 //case 't':
149 case 'b':
150 break;
151 default:
152 fOk = false;
153 break;
154 }
155 break;
156 //case 't':
157 case 'b':
158 break;
159 default:
160 fOk = false;
161 break;
162 }
163 break;
164 default:
165 fOk = false;
166 break;
167 }
168 if (!fOk)
169 {
170 AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b|t]'\n", pszMode));
171 return VINF_SUCCESS;
172 }
173
174 /*
175 * Allocate the stream handle and try open it.
176 */
177 PRTSTREAM pStream = (PRTSTREAM)RTMemAlloc(sizeof(*pStream));
178 if (pStream)
179 {
180 pStream->u32Magic = RTSTREAM_MAGIC;
181 pStream->i32Error = VINF_SUCCESS;
182 pStream->pFile = fopen(pszFilename, pszMode);
183 if (pStream->pFile)
184 {
185 *ppStream = pStream;
186 return VINF_SUCCESS;
187 }
188 return RTErrConvertFromErrno(errno);
189 }
190 return VERR_NO_MEMORY;
191}
192
193
194/**
195 * Opens a file stream.
196 *
197 * @returns iprt status code.
198 * @param pszMode The open mode. See fopen() standard.
199 * Format: <a|r|w>[+][b|t]
200 * @param ppStream Where to store the opened stream.
201 * @param pszFilenameFmt Filename path format string.
202 * @param args Arguments to the format string.
203 */
204RTR3DECL(int) RTStrmOpenFV(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, va_list args)
205{
206 int rc;
207 char szFilename[RTPATH_MAX];
208 size_t cch = RTStrPrintfV(szFilename, sizeof(szFilename), pszFilenameFmt, args);
209 if (cch < sizeof(szFilename))
210 rc = RTStrmOpen(szFilename, pszMode, ppStream);
211 else
212 {
213 AssertMsgFailed(("The filename is too long cch=%d\n", cch));
214 rc = VERR_FILENAME_TOO_LONG;
215 }
216 return rc;
217}
218
219
220/**
221 * Opens a file stream.
222 *
223 * @returns iprt status code.
224 * @param pszMode The open mode. See fopen() standard.
225 * Format: <a|r|w>[+][b|t]
226 * @param ppStream Where to store the opened stream.
227 * @param pszFilenameFmt Filename path format string.
228 * @param ... Arguments to the format string.
229 */
230RTR3DECL(int) RTStrmOpenF(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, ...)
231{
232 va_list args;
233 va_start(args, pszFilenameFmt);
234 int rc = RTStrmOpenFV(pszMode, ppStream, pszFilenameFmt, args);
235 va_end(args);
236 return rc;
237}
238
239
240/**
241 * Closes the specified stream.
242 *
243 * @returns iprt status code.
244 * @param pStream The stream to close.
245 */
246RTR3DECL(int) RTStrmClose(PRTSTREAM pStream)
247{
248 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
249 {
250 if (!fclose(pStream->pFile))
251 {
252 pStream->u32Magic = 0xdeaddead;
253 pStream->pFile = NULL;
254 RTMemFree(pStream);
255 return VINF_SUCCESS;
256 }
257 return RTErrConvertFromErrno(errno);
258 }
259 else
260 {
261 AssertMsgFailed(("Invalid stream!\n"));
262 return VERR_INVALID_PARAMETER;
263 }
264}
265
266
267/**
268 * Get the pending error of the stream.
269 *
270 * @returns iprt status code. of the stream.
271 * @param pStream The stream.
272 */
273RTR3DECL(int) RTStrmError(PRTSTREAM pStream)
274{
275 int rc;
276 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
277 rc = pStream->i32Error;
278 else
279 {
280 AssertMsgFailed(("Invalid stream!\n"));
281 rc = VERR_INVALID_PARAMETER;
282 }
283 return rc;
284}
285
286
287/**
288 * Clears stream error condition.
289 *
290 * All stream operations save RTStrmClose and this will fail
291 * while an error is asserted on the stream
292 *
293 * @returns iprt status code.
294 * @param pStream The stream.
295 */
296RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream)
297{
298 int rc;
299 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
300 {
301 clearerr(pStream->pFile);
302 ASMAtomicXchgS32(&pStream->i32Error, VINF_SUCCESS);
303 rc = VINF_SUCCESS;
304 }
305 else
306 {
307 AssertMsgFailed(("Invalid stream!\n"));
308 rc = VERR_INVALID_PARAMETER;
309 }
310 return rc;
311}
312
313
314/**
315 * Rewinds the stream.
316 *
317 * Stream errors will be reset on success.
318 *
319 * @returns IPRT status code.
320 *
321 * @param pStream The stream.
322 *
323 * @remarks Not all streams are rewindable and that behavior is currently
324 * undefined for those.
325 */
326RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
327{
328 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
329 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
330
331 int rc;
332 clearerr(pStream->pFile);
333 errno = 0;
334 if (!fseek(pStream->pFile, 0, SEEK_SET))
335 {
336 ASMAtomicXchgS32(&pStream->i32Error, VINF_SUCCESS);
337 rc = VINF_SUCCESS;
338 }
339 else
340 {
341 rc = RTErrConvertFromErrno(errno);
342 ASMAtomicXchgS32(&pStream->i32Error, rc);
343 }
344
345 return rc;
346}
347
348
349/**
350 * Reads from a file stream.
351 *
352 * @returns iprt status code.
353 * @param pStream The stream.
354 * @param pvBuf Where to put the read bits.
355 * Must be cbRead bytes or more.
356 * @param cbRead Number of bytes to read.
357 * @param pcbRead Where to store the number of bytes actually read.
358 * If NULL cbRead bytes are read or an error is returned.
359 */
360RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead)
361{
362 int rc;
363 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
364 {
365 rc = pStream->i32Error;
366 if (RT_SUCCESS(rc))
367 {
368 if (pcbRead)
369 {
370 /*
371 * Can do with a partial read.
372 */
373 *pcbRead = fread(pvBuf, 1, cbRead, pStream->pFile);
374 if ( *pcbRead == cbRead
375 || !ferror(pStream->pFile))
376 return VINF_SUCCESS;
377 if (feof(pStream->pFile))
378 {
379 if (*pcbRead)
380 return VINF_EOF;
381 rc = VERR_EOF;
382 }
383 else if (ferror(pStream->pFile))
384 rc = VERR_READ_ERROR;
385 else
386 {
387 AssertMsgFailed(("This shouldn't happen\n"));
388 rc = VERR_INTERNAL_ERROR;
389 }
390 }
391 else
392 {
393 /*
394 * Must read it all!
395 */
396 if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1)
397 return VINF_SUCCESS;
398
399 /* possible error/eof. */
400 if (feof(pStream->pFile))
401 rc = VERR_EOF;
402 else if (ferror(pStream->pFile))
403 rc = VERR_READ_ERROR;
404 else
405 {
406 AssertMsgFailed(("This shouldn't happen\n"));
407 rc = VERR_INTERNAL_ERROR;
408 }
409 }
410 ASMAtomicXchgS32(&pStream->i32Error, rc);
411 }
412 }
413 else
414 {
415 AssertMsgFailed(("Invalid stream!\n"));
416 rc = VERR_INVALID_PARAMETER;
417 }
418 return rc;
419}
420
421
422/**
423 * Writes to a file stream.
424 *
425 * @returns iprt status code.
426 * @param pStream The stream.
427 * @param pvBuf Where to get the bits to write from.
428 * @param cbWrite Number of bytes to write.
429 * @param pcbWritten Where to store the number of bytes actually written.
430 * If NULL cbWrite bytes are written or an error is returned.
431 */
432RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
433{
434 int rc;
435 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
436 {
437 rc = pStream->i32Error;
438 if (RT_SUCCESS(rc))
439 {
440 if (pcbWritten)
441 {
442 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
443 *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);
444 IPRT_ALIGNMENT_CHECKS_ENABLE();
445 if ( *pcbWritten == cbWrite
446 || !ferror(pStream->pFile))
447 return VINF_SUCCESS;
448 rc = VERR_WRITE_ERROR;
449 }
450 else
451 {
452 /*
453 * Must write it all!
454 */
455 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
456 size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile);
457 IPRT_ALIGNMENT_CHECKS_ENABLE();
458 if (cbWritten == 1)
459 return VINF_SUCCESS;
460 if (!ferror(pStream->pFile))
461 return VINF_SUCCESS; /* WEIRD! But anyway... */
462
463 rc = VERR_WRITE_ERROR;
464 }
465 ASMAtomicXchgS32(&pStream->i32Error, rc);
466 }
467 }
468 else
469 {
470 AssertMsgFailed(("Invalid stream!\n"));
471 rc = VERR_INVALID_PARAMETER;
472 }
473 return rc;
474}
475
476
477/**
478 * Reads a character from a file stream.
479 *
480 * @returns The char as an unsigned char cast to int.
481 * @returns -1 on failure.
482 * @param pStream The stream.
483 */
484RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
485{
486 unsigned char ch;
487 int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
488 if (RT_SUCCESS(rc))
489 return ch;
490 return -1;
491}
492
493
494/**
495 * Writes a character to a file stream.
496 *
497 * @returns iprt status code.
498 * @param pStream The stream.
499 * @param ch The char to write.
500 */
501RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
502{
503 return RTStrmWriteEx(pStream, &ch, 1, NULL);
504}
505
506
507/**
508 * Writes a string to a file stream.
509 *
510 * @returns iprt status code.
511 * @param pStream The stream.
512 * @param pszString The string to write.
513 * No newlines or anything is appended or prepended.
514 * The terminating '\\0' is not written, of course.
515 */
516RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
517{
518 size_t cch = strlen(pszString);
519 return RTStrmWriteEx(pStream, pszString, cch, NULL);
520}
521
522
523/**
524 * Reads a line from a file stream.
525 * A line ends with a '\\n', '\\0' or the end of the file.
526 *
527 * @returns iprt status code.
528 * @returns VINF_BUFFER_OVERFLOW if the buffer wasn't big enough to read an entire line.
529 * @param pStream The stream.
530 * @param pszString Where to store the line.
531 * The line will *NOT* contain any '\\n'.
532 * @param cchString The size of the string buffer.
533 */
534RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cchString)
535{
536 int rc;
537 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
538 {
539 if (pszString && cchString > 1)
540 {
541 rc = pStream->i32Error;
542 if (RT_SUCCESS(rc))
543 {
544 cchString--; /* save space for the terminator. */
545#ifdef HAVE_FWRITE_UNLOCKED
546 flockfile(pStream->pFile);
547#endif
548 for (;;)
549 {
550#ifdef HAVE_FWRITE_UNLOCKED
551 int ch = fgetc_unlocked(pStream->pFile);
552#else
553 int ch = fgetc(pStream->pFile);
554#endif
555 if (ch == EOF)
556 {
557#ifdef HAVE_FWRITE_UNLOCKED
558 if (feof_unlocked(pStream->pFile))
559#else
560 if (feof(pStream->pFile))
561#endif
562 {
563 rc = VERR_EOF;
564 break;
565 }
566#ifdef HAVE_FWRITE_UNLOCKED
567 if (ferror_unlocked(pStream->pFile))
568#else
569 if (ferror(pStream->pFile))
570#endif
571 rc = VERR_READ_ERROR;
572 else
573 {
574 AssertMsgFailed(("This shouldn't happen\n"));
575 rc = VERR_INTERNAL_ERROR;
576 }
577 break;
578 }
579 if (ch == '\0' || ch == '\n' || ch == '\r')
580 break;
581 *pszString++ = ch;
582 if (--cchString <= 0)
583 {
584 rc = VINF_BUFFER_OVERFLOW;
585 break;
586 }
587 }
588#ifdef HAVE_FWRITE_UNLOCKED
589 funlockfile(pStream->pFile);
590#endif
591
592 *pszString = '\0';
593 if (RT_FAILURE(rc))
594 ASMAtomicXchgS32(&pStream->i32Error, rc);
595 }
596 }
597 else
598 {
599 AssertMsgFailed(("no buffer or too small buffer!\n"));
600 rc = VERR_INVALID_PARAMETER;
601 }
602 }
603 else
604 {
605 AssertMsgFailed(("Invalid stream!\n"));
606 rc = VERR_INVALID_PARAMETER;
607 }
608 return rc;
609}
610
611
612/**
613 * Flushes a stream.
614 *
615 * @returns iprt status code.
616 * @param pStream The stream to flush.
617 */
618RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
619{
620 if (!fflush(pStream->pFile))
621 return VINF_SUCCESS;
622 return RTErrConvertFromErrno(errno);
623}
624
625
626/**
627 * Output callback.
628 *
629 * @returns number of bytes written.
630 * @param pvArg User argument.
631 * @param pachChars Pointer to an array of utf-8 characters.
632 * @param cchChars Number of bytes in the character array pointed to by pachChars.
633 */
634static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
635{
636 if (cchChars)
637 {
638 PRTSTREAM pStream = (PRTSTREAM)pvArg;
639 int rc = pStream->i32Error;
640 if (RT_SUCCESS(rc))
641 {
642 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
643#ifdef HAVE_FWRITE_UNLOCKED
644 if (fwrite_unlocked(pachChars, cchChars, 1, pStream->pFile) != 1)
645#else
646 if (fwrite(pachChars, cchChars, 1, pStream->pFile) != 1)
647#endif
648 ASMAtomicXchgS32(&pStream->i32Error, VERR_WRITE_ERROR);
649 IPRT_ALIGNMENT_CHECKS_ENABLE();
650 }
651 }
652 /* else: ignore termination call. */
653 return cchChars;
654}
655
656
657/**
658 * Prints a formatted string to the specified stream.
659 *
660 * @returns Number of bytes printed.
661 * @param pStream The stream to print to.
662 * @param pszFormat IPRT format string.
663 * @param args Arguments specified by pszFormat.
664 */
665RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
666{
667 int rc;
668 if (pStream && pStream->u32Magic == RTSTREAM_MAGIC)
669 {
670 rc = pStream->i32Error;
671 if (RT_SUCCESS(rc))
672 {
673 /** @todo consider making this thread safe... */
674#ifdef HAVE_FWRITE_UNLOCKED
675 flockfile(pStream->pFile);
676 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
677 funlockfile(pStream->pFile);
678#else
679 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
680#endif
681 Assert(rc >= 0);
682 }
683 else
684 rc = -1;
685 }
686 else
687 {
688 AssertMsgFailed(("Invalid stream!\n"));
689 rc = -1;
690 }
691 return rc;
692}
693
694
695/**
696 * Prints a formatted string to the specified stream.
697 *
698 * @returns Number of bytes printed.
699 * @param pStream The stream to print to.
700 * @param pszFormat IPRT format string.
701 * @param ... Arguments specified by pszFormat.
702 */
703RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
704{
705 va_list args;
706 va_start(args, pszFormat);
707 int rc = RTStrmPrintfV(pStream, pszFormat, args);
708 va_end(args);
709 return rc;
710}
711
712
713/**
714 * Prints a formatted string to the standard output stream (g_pStdOut).
715 *
716 * @returns Number of bytes printed.
717 * @param pszFormat IPRT format string.
718 * @param args Arguments specified by pszFormat.
719 */
720RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
721{
722 return RTStrmPrintfV(g_pStdOut, pszFormat, args);
723}
724
725
726/**
727 * Prints a formatted string to the standard output stream (g_pStdOut).
728 *
729 * @returns Number of bytes printed.
730 * @param pszFormat IPRT format string.
731 * @param ... Arguments specified by pszFormat.
732 */
733RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
734{
735 va_list args;
736 va_start(args, pszFormat);
737 int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
738 va_end(args);
739 return rc;
740}
741
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