VirtualBox

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

Last change on this file since 8015 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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