VirtualBox

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

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

Make runtime build on FreeBSD.

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