VirtualBox

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

Last change on this file since 4512 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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