VirtualBox

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

Last change on this file since 20823 was 20823, checked in by vboxsync, 15 years ago

iprt/stream.cpp: alignment hacks.

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