VirtualBox

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

Last change on this file since 1816 was 1816, checked in by vboxsync, 18 years ago

moved magics to a common header to avoid duplicating the same defines all over the place.

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