VirtualBox

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

Last change on this file since 18784 was 17008, checked in by vboxsync, 16 years ago

IPRT: Expose (and fix) RTStrmOpenF and RTStrmOpenFV.

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