VirtualBox

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

Last change on this file since 68836 was 68836, checked in by vboxsync, 7 years ago

IPRT: Added RTStrmIsTerminal and RTStrmQueryTerminalWidth.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 36.1 KB
Line 
1/* $Id: stream.cpp 68836 2017-09-22 16:37:46Z vboxsync $ */
2/** @file
3 * IPRT - I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
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
27
28
29#if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */
30#define HAVE_FWRITE_UNLOCKED
31#endif
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#include <iprt/stream.h>
38#include "internal/iprt.h"
39
40#include <iprt/asm.h>
41#ifndef HAVE_FWRITE_UNLOCKED
42# include <iprt/critsect.h>
43#endif
44#include <iprt/string.h>
45#include <iprt/assert.h>
46#include <iprt/alloc.h>
47#include <iprt/err.h>
48#include <iprt/param.h>
49#include <iprt/string.h>
50
51#include "internal/alignmentchecks.h"
52#include "internal/magics.h"
53
54#include <stdio.h>
55#include <errno.h>
56#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
57# include <io.h>
58# include <fcntl.h>
59#endif
60#ifdef RT_OS_WINDOWS
61# include <iprt/win/windows.h>
62#else
63# include <termios.h>
64# include <unistd.h>
65#endif
66
67#ifdef RT_OS_OS2
68# define _O_TEXT O_TEXT
69# define _O_BINARY O_BINARY
70#endif
71
72
73/*********************************************************************************************************************************
74* Structures and Typedefs *
75*********************************************************************************************************************************/
76/**
77 * File stream.
78 */
79typedef struct RTSTREAM
80{
81 /** Magic value used to validate the stream. (RTSTREAM_MAGIC) */
82 uint32_t u32Magic;
83 /** File stream error. */
84 int32_t volatile i32Error;
85 /** Pointer to the LIBC file stream. */
86 FILE *pFile;
87 /** Stream is using the current process code set. */
88 bool fCurrentCodeSet;
89 /** Whether the stream was opened in binary mode. */
90 bool fBinary;
91 /** Whether to recheck the stream mode before writing. */
92 bool fRecheckMode;
93#ifndef HAVE_FWRITE_UNLOCKED
94 /** Critical section for serializing access to the stream. */
95 PRTCRITSECT pCritSect;
96#endif
97} RTSTREAM;
98
99
100/*********************************************************************************************************************************
101* Global Variables *
102*********************************************************************************************************************************/
103/** The standard input stream. */
104static RTSTREAM g_StdIn =
105{
106 RTSTREAM_MAGIC,
107 0,
108 stdin,
109 /*.fCurrentCodeSet = */ true,
110 /*.fBinary = */ false,
111 /*.fRecheckMode = */ true
112#ifndef HAVE_FWRITE_UNLOCKED
113 , NULL
114#endif
115};
116
117/** The standard error stream. */
118static RTSTREAM g_StdErr =
119{
120 RTSTREAM_MAGIC,
121 0,
122 stderr,
123 /*.fCurrentCodeSet = */ true,
124 /*.fBinary = */ false,
125 /*.fRecheckMode = */ true
126#ifndef HAVE_FWRITE_UNLOCKED
127 , NULL
128#endif
129};
130
131/** The standard output stream. */
132static RTSTREAM g_StdOut =
133{
134 RTSTREAM_MAGIC,
135 0,
136 stdout,
137 /*.fCurrentCodeSet = */ true,
138 /*.fBinary = */ false,
139 /*.fRecheckMode = */ true
140#ifndef HAVE_FWRITE_UNLOCKED
141 , NULL
142#endif
143};
144
145/** Pointer to the standard input stream. */
146RTDATADECL(PRTSTREAM) g_pStdIn = &g_StdIn;
147
148/** Pointer to the standard output stream. */
149RTDATADECL(PRTSTREAM) g_pStdErr = &g_StdErr;
150
151/** Pointer to the standard output stream. */
152RTDATADECL(PRTSTREAM) g_pStdOut = &g_StdOut;
153
154
155#ifndef HAVE_FWRITE_UNLOCKED
156/**
157 * Allocates and acquires the lock for the stream.
158 *
159 * @returns IPRT status code.
160 * @param pStream The stream (valid).
161 */
162static int rtStrmAllocLock(PRTSTREAM pStream)
163{
164 Assert(pStream->pCritSect == NULL);
165
166 PRTCRITSECT pCritSect = (PRTCRITSECT)RTMemAlloc(sizeof(*pCritSect));
167 if (!pCritSect)
168 return VERR_NO_MEMORY;
169
170 /* The native stream lock are normally not recursive. */
171 int rc = RTCritSectInitEx(pCritSect, RTCRITSECT_FLAGS_NO_NESTING,
172 NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemSpinMutex");
173 if (RT_SUCCESS(rc))
174 {
175 rc = RTCritSectEnter(pCritSect);
176 if (RT_SUCCESS(rc))
177 {
178 if (RT_LIKELY(ASMAtomicCmpXchgPtr(&pStream->pCritSect, pCritSect, NULL)))
179 return VINF_SUCCESS;
180
181 RTCritSectLeave(pCritSect);
182 }
183 RTCritSectDelete(pCritSect);
184 }
185 RTMemFree(pCritSect);
186
187 /* Handle the lost race case... */
188 pCritSect = ASMAtomicReadPtrT(&pStream->pCritSect, PRTCRITSECT);
189 if (pCritSect)
190 return RTCritSectEnter(pCritSect);
191
192 return rc;
193}
194#endif /* !HAVE_FWRITE_UNLOCKED */
195
196
197/**
198 * Locks the stream. May have to allocate the lock as well.
199 *
200 * @param pStream The stream (valid).
201 */
202DECLINLINE(void) rtStrmLock(PRTSTREAM pStream)
203{
204#ifdef HAVE_FWRITE_UNLOCKED
205 flockfile(pStream->pFile);
206#else
207 if (RT_LIKELY(pStream->pCritSect))
208 RTCritSectEnter(pStream->pCritSect);
209 else
210 rtStrmAllocLock(pStream);
211#endif
212}
213
214
215/**
216 * Unlocks the stream.
217 *
218 * @param pStream The stream (valid).
219 */
220DECLINLINE(void) rtStrmUnlock(PRTSTREAM pStream)
221{
222#ifdef HAVE_FWRITE_UNLOCKED
223 funlockfile(pStream->pFile);
224#else
225 if (RT_LIKELY(pStream->pCritSect))
226 RTCritSectLeave(pStream->pCritSect);
227#endif
228}
229
230
231/**
232 * Opens a file stream.
233 *
234 * @returns iprt status code.
235 * @param pszFilename Path to the file to open.
236 * @param pszMode The open mode. See fopen() standard.
237 * Format: <a|r|w>[+][b|t]
238 * @param ppStream Where to store the opened stream.
239 */
240RTR3DECL(int) RTStrmOpen(const char *pszFilename, const char *pszMode, PRTSTREAM *ppStream)
241{
242 /*
243 * Validate input.
244 */
245 if (!pszMode || !*pszMode)
246 {
247 AssertMsgFailed(("No pszMode!\n"));
248 return VERR_INVALID_PARAMETER;
249 }
250 if (!pszFilename)
251 {
252 AssertMsgFailed(("No pszFilename!\n"));
253 return VERR_INVALID_PARAMETER;
254 }
255 bool fOk = true;
256 bool fBinary = false;
257 switch (*pszMode)
258 {
259 case 'a':
260 case 'w':
261 case 'r':
262 switch (pszMode[1])
263 {
264 case '\0':
265 break;
266
267 case '+':
268 switch (pszMode[2])
269 {
270 case '\0':
271 break;
272
273 //case 't':
274 // break;
275
276 case 'b':
277 fBinary = true;
278 break;
279
280 default:
281 fOk = false;
282 break;
283 }
284 break;
285
286 //case 't':
287 // break;
288
289 case 'b':
290 fBinary = true;
291 break;
292
293 default:
294 fOk = false;
295 break;
296 }
297 break;
298 default:
299 fOk = false;
300 break;
301 }
302 if (!fOk)
303 {
304 AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b|t]'\n", pszMode));
305 return VINF_SUCCESS;
306 }
307
308 /*
309 * Allocate the stream handle and try open it.
310 */
311 PRTSTREAM pStream = (PRTSTREAM)RTMemAlloc(sizeof(*pStream));
312 if (pStream)
313 {
314 pStream->u32Magic = RTSTREAM_MAGIC;
315 pStream->i32Error = VINF_SUCCESS;
316 pStream->fCurrentCodeSet = false;
317 pStream->fBinary = fBinary;
318 pStream->fRecheckMode = false;
319#ifndef HAVE_FWRITE_UNLOCKED
320 pStream->pCritSect = NULL;
321#endif /* HAVE_FWRITE_UNLOCKED */
322 pStream->pFile = fopen(pszFilename, pszMode);
323 if (pStream->pFile)
324 {
325 *ppStream = pStream;
326 return VINF_SUCCESS;
327 }
328 RTMemFree(pStream);
329 return RTErrConvertFromErrno(errno);
330 }
331 return VERR_NO_MEMORY;
332}
333
334
335/**
336 * Opens a file stream.
337 *
338 * @returns iprt status code.
339 * @param pszMode The open mode. See fopen() standard.
340 * Format: <a|r|w>[+][b|t]
341 * @param ppStream Where to store the opened stream.
342 * @param pszFilenameFmt Filename path format string.
343 * @param args Arguments to the format string.
344 */
345RTR3DECL(int) RTStrmOpenFV(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, va_list args)
346{
347 int rc;
348 char szFilename[RTPATH_MAX];
349 size_t cch = RTStrPrintfV(szFilename, sizeof(szFilename), pszFilenameFmt, args);
350 if (cch < sizeof(szFilename))
351 rc = RTStrmOpen(szFilename, pszMode, ppStream);
352 else
353 {
354 AssertMsgFailed(("The filename is too long cch=%d\n", cch));
355 rc = VERR_FILENAME_TOO_LONG;
356 }
357 return rc;
358}
359
360
361/**
362 * Opens a file stream.
363 *
364 * @returns iprt status code.
365 * @param pszMode The open mode. See fopen() standard.
366 * Format: <a|r|w>[+][b|t]
367 * @param ppStream Where to store the opened stream.
368 * @param pszFilenameFmt Filename path format string.
369 * @param ... Arguments to the format string.
370 */
371RTR3DECL(int) RTStrmOpenF(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, ...)
372{
373 va_list args;
374 va_start(args, pszFilenameFmt);
375 int rc = RTStrmOpenFV(pszMode, ppStream, pszFilenameFmt, args);
376 va_end(args);
377 return rc;
378}
379
380
381/**
382 * Closes the specified stream.
383 *
384 * @returns iprt status code.
385 * @param pStream The stream to close.
386 */
387RTR3DECL(int) RTStrmClose(PRTSTREAM pStream)
388{
389 if (!pStream)
390 return VINF_SUCCESS;
391 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
392
393 if (!fclose(pStream->pFile))
394 {
395 pStream->u32Magic = 0xdeaddead;
396 pStream->pFile = NULL;
397#ifndef HAVE_FWRITE_UNLOCKED
398 if (pStream->pCritSect)
399 {
400 RTCritSectEnter(pStream->pCritSect);
401 RTCritSectLeave(pStream->pCritSect);
402 RTCritSectDelete(pStream->pCritSect);
403 RTMemFree(pStream->pCritSect);
404 pStream->pCritSect = NULL;
405 }
406#endif
407 RTMemFree(pStream);
408 return VINF_SUCCESS;
409 }
410
411 return RTErrConvertFromErrno(errno);
412}
413
414
415/**
416 * Get the pending error of the stream.
417 *
418 * @returns iprt status code. of the stream.
419 * @param pStream The stream.
420 */
421RTR3DECL(int) RTStrmError(PRTSTREAM pStream)
422{
423 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
424 return pStream->i32Error;
425}
426
427
428/**
429 * Clears stream error condition.
430 *
431 * All stream operations save RTStrmClose and this will fail
432 * while an error is asserted on the stream
433 *
434 * @returns iprt status code.
435 * @param pStream The stream.
436 */
437RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream)
438{
439 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
440
441 clearerr(pStream->pFile);
442 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
443 return VINF_SUCCESS;
444}
445
446
447RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
448{
449 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
450 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
451 AssertReturn((unsigned)(fBinary + 1) <= 2, VERR_INVALID_PARAMETER);
452 AssertReturn((unsigned)(fCurrentCodeSet + 1) <= 2, VERR_INVALID_PARAMETER);
453
454 rtStrmLock(pStream);
455
456 if (fBinary != -1)
457 {
458 pStream->fBinary = RT_BOOL(fBinary);
459 pStream->fRecheckMode = true;
460 }
461
462 if (fCurrentCodeSet != -1)
463 pStream->fCurrentCodeSet = RT_BOOL(fCurrentCodeSet);
464
465 rtStrmUnlock(pStream);
466
467 return VINF_SUCCESS;
468}
469
470
471RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars)
472{
473 int rc = VINF_SUCCESS;
474
475 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
476 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
477 AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER);
478
479 int fh = fileno(pStream->pFile);
480 if (isatty(fh))
481 {
482#ifdef RT_OS_WINDOWS
483 DWORD dwMode;
484 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
485 if (GetConsoleMode(hCon, &dwMode))
486 *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT);
487 else
488 rc = RTErrConvertFromWin32(GetLastError());
489#else
490 struct termios Termios;
491
492 int rcPosix = tcgetattr(fh, &Termios);
493 if (!rcPosix)
494 *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);
495 else
496 rc = RTErrConvertFromErrno(errno);
497#endif
498 }
499 else
500 rc = VERR_INVALID_HANDLE;
501
502 return rc;
503}
504
505
506RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)
507{
508 int rc = VINF_SUCCESS;
509
510 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
511 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
512
513 int fh = fileno(pStream->pFile);
514 if (isatty(fh))
515 {
516#ifdef RT_OS_WINDOWS
517 DWORD dwMode;
518 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
519 if (GetConsoleMode(hCon, &dwMode))
520 {
521 if (fEchoChars)
522 dwMode |= ENABLE_ECHO_INPUT;
523 else
524 dwMode &= ~ENABLE_ECHO_INPUT;
525 if (!SetConsoleMode(hCon, dwMode))
526 rc = RTErrConvertFromWin32(GetLastError());
527 }
528 else
529 rc = RTErrConvertFromWin32(GetLastError());
530#else
531 struct termios Termios;
532
533 int rcPosix = tcgetattr(fh, &Termios);
534 if (!rcPosix)
535 {
536 if (fEchoChars)
537 Termios.c_lflag |= ECHO;
538 else
539 Termios.c_lflag &= ~ECHO;
540
541 rcPosix = tcsetattr(fh, TCSAFLUSH, &Termios);
542 if (rcPosix != 0)
543 rc = RTErrConvertFromErrno(errno);
544 }
545 else
546 rc = RTErrConvertFromErrno(errno);
547#endif
548 }
549 else
550 rc = VERR_INVALID_HANDLE;
551
552 return rc;
553}
554
555
556RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)
557{
558 AssertPtrReturn(pStream, false);
559 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);
560
561 if (pStream->pFile)
562 {
563 int fh = fileno(pStream->pFile);
564 if (isatty(fh))
565 {
566#ifdef RT_OS_WINDOWS
567 DWORD dwMode;
568 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
569 if (GetConsoleMode(hCon, &dwMode))
570 return true;
571#else
572 return true;
573#endif
574 }
575 }
576 return false;
577}
578
579
580RTR3DECL(int) RTStrmQueryTerminalWidth(PRTSTREAM pStream, uint32_t *pcchWidth)
581{
582 AssertPtrReturn(pcchWidth, VERR_INVALID_HANDLE);
583 *pcchWidth = 80;
584
585 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
586 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
587
588 if (pStream->pFile)
589 {
590 int fh = fileno(pStream->pFile);
591 if (isatty(fh))
592 {
593#ifdef RT_OS_WINDOWS
594 CONSOLE_SCREEN_BUFFER_INFO Info;
595 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
596 RT_ZERO(Info);
597 if (GetConsoleScreenBufferInfo(hCon, &Info))
598 {
599 *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;
600 return VINF_SUCCESS;
601 }
602 return RTErrConvertFromWin32(GetLastError());
603#else
604 struct winsize Info;
605 RT_ZERO(Info);
606 int rc = ioctl(fh, TIOCGWINSZ, &Info);
607 if (rc >= 0)
608 {
609 *pcchWidth = Info.ws_row ? Info.ws_row : 80;
610 return VINF_SUCCESS;
611 }
612 return RTErrConvertFromErrno(errno);
613#endif
614 }
615 }
616 return VERR_INVALID_FUNCTION;
617}
618
619
620
621/**
622 * Rewinds the stream.
623 *
624 * Stream errors will be reset on success.
625 *
626 * @returns IPRT status code.
627 *
628 * @param pStream The stream.
629 *
630 * @remarks Not all streams are rewindable and that behavior is currently
631 * undefined for those.
632 */
633RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
634{
635 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
636 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
637
638 int rc;
639 clearerr(pStream->pFile);
640 errno = 0;
641 if (!fseek(pStream->pFile, 0, SEEK_SET))
642 {
643 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
644 rc = VINF_SUCCESS;
645 }
646 else
647 {
648 rc = RTErrConvertFromErrno(errno);
649 ASMAtomicWriteS32(&pStream->i32Error, rc);
650 }
651
652 return rc;
653}
654
655
656/**
657 * Recheck the stream mode.
658 *
659 * @param pStream The stream (locked).
660 */
661static void rtStreamRecheckMode(PRTSTREAM pStream)
662{
663#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
664 int fh = fileno(pStream->pFile);
665 if (fh >= 0)
666 {
667 int fExpected = pStream->fBinary ? _O_BINARY : _O_TEXT;
668 int fActual = _setmode(fh, fExpected);
669 if (fActual != -1 && fExpected != (fActual & (_O_BINARY | _O_TEXT)))
670 {
671 fActual = _setmode(fh, fActual & (_O_BINARY | _O_TEXT));
672 pStream->fBinary = !(fActual & _O_TEXT);
673 }
674 }
675#else
676 NOREF(pStream);
677#endif
678 pStream->fRecheckMode = false;
679}
680
681
682/**
683 * Reads from a file stream.
684 *
685 * @returns iprt status code.
686 * @param pStream The stream.
687 * @param pvBuf Where to put the read bits.
688 * Must be cbRead bytes or more.
689 * @param cbRead Number of bytes to read.
690 * @param pcbRead Where to store the number of bytes actually read.
691 * If NULL cbRead bytes are read or an error is returned.
692 */
693RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead)
694{
695 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
696
697 int rc = pStream->i32Error;
698 if (RT_SUCCESS(rc))
699 {
700 if (pStream->fRecheckMode)
701 rtStreamRecheckMode(pStream);
702
703 if (pcbRead)
704 {
705 /*
706 * Can do with a partial read.
707 */
708 *pcbRead = fread(pvBuf, 1, cbRead, pStream->pFile);
709 if ( *pcbRead == cbRead
710 || !ferror(pStream->pFile))
711 return VINF_SUCCESS;
712 if (feof(pStream->pFile))
713 {
714 if (*pcbRead)
715 return VINF_EOF;
716 rc = VERR_EOF;
717 }
718 else if (ferror(pStream->pFile))
719 rc = VERR_READ_ERROR;
720 else
721 {
722 AssertMsgFailed(("This shouldn't happen\n"));
723 rc = VERR_INTERNAL_ERROR;
724 }
725 }
726 else
727 {
728 /*
729 * Must read it all!
730 */
731 if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1)
732 return VINF_SUCCESS;
733
734 /* possible error/eof. */
735 if (feof(pStream->pFile))
736 rc = VERR_EOF;
737 else if (ferror(pStream->pFile))
738 rc = VERR_READ_ERROR;
739 else
740 {
741 AssertMsgFailed(("This shouldn't happen\n"));
742 rc = VERR_INTERNAL_ERROR;
743 }
744 }
745 ASMAtomicWriteS32(&pStream->i32Error, rc);
746 }
747 return rc;
748}
749
750
751/**
752 * Check if the input text is valid UTF-8.
753 *
754 * @returns true/false.
755 * @param pvBuf Pointer to the buffer.
756 * @param cbBuf Size of the buffer.
757 */
758static bool rtStrmIsUtf8Text(const void *pvBuf, size_t cbBuf)
759{
760 NOREF(pvBuf);
761 NOREF(cbBuf);
762 /** @todo not sure this is a good idea... Better redefine RTStrmWrite. */
763 return false;
764}
765
766
767#ifdef RT_OS_WINDOWS
768/**
769 * Check if the stream is for a Window console.
770 *
771 * @returns true / false.
772 * @param pStream The stream.
773 * @param phCon Where to return the console handle.
774 */
775static bool rtStrmIsConsoleUnlocked(PRTSTREAM pStream, HANDLE *phCon)
776{
777 int fh = fileno(pStream->pFile);
778 if (isatty(fh))
779 {
780 DWORD dwMode;
781 HANDLE hCon = (HANDLE)_get_osfhandle(fh);
782 if (GetConsoleMode(hCon, &dwMode))
783 {
784 *phCon = hCon;
785 return true;
786 }
787 }
788 return false;
789}
790#endif /* RT_OS_WINDOWS */
791
792
793/**
794 * Internal write API, stream lock already held.
795 *
796 * @returns IPRT status code.
797 * @param pStream The stream.
798 * @param pvBuf What to write.
799 * @param cbWrite How much to write.
800 * @param pcbWritten Where to optionally return the number of bytes
801 * written.
802 * @param fSureIsText Set if we're sure this is UTF-8 text already.
803 */
804static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten,
805 bool fSureIsText)
806{
807 int rc = pStream->i32Error;
808 if (RT_FAILURE(rc))
809 return rc;
810 if (pStream->fRecheckMode)
811 rtStreamRecheckMode(pStream);
812
813#ifdef RT_OS_WINDOWS
814 /*
815 * Use the unicode console API when possible in order to avoid stuff
816 * getting lost in unnecessary code page translations.
817 */
818 HANDLE hCon;
819 if (rtStrmIsConsoleUnlocked(pStream, &hCon))
820 {
821# ifdef HAVE_FWRITE_UNLOCKED
822 if (!fflush_unlocked(pStream->pFile))
823# else
824 if (!fflush(pStream->pFile))
825# endif
826 {
827 /** @todo Consider buffering later. For now, we'd rather correct output than
828 * fast output. */
829 DWORD cwcWritten = 0;
830 PRTUTF16 pwszSrc = NULL;
831 size_t cwcSrc = 0;
832 rc = RTStrToUtf16Ex((const char *)pvBuf, cbWrite, &pwszSrc, 0, &cwcSrc);
833 if (RT_SUCCESS(rc))
834 {
835 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
836 {
837 /* try write char-by-char to avoid heap problem. */
838 cwcWritten = 0;
839 while (cwcWritten != cwcSrc)
840 {
841 DWORD cwcThis;
842 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
843 {
844 if (!pcbWritten || cwcWritten == 0)
845 rc = RTErrConvertFromErrno(GetLastError());
846 break;
847 }
848 if (cwcThis != 1) /* Unable to write current char (amount)? */
849 break;
850 cwcWritten++;
851 }
852 }
853 if (RT_SUCCESS(rc))
854 {
855 if (cwcWritten == cwcSrc)
856 {
857 if (pcbWritten)
858 *pcbWritten = cbWrite;
859 }
860 else if (pcbWritten)
861 {
862 PCRTUTF16 pwszCur = pwszSrc;
863 const char *pszCur = (const char *)pvBuf;
864 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
865 {
866 RTUNICP CpIgnored;
867 RTUtf16GetCpEx(&pwszCur, &CpIgnored);
868 RTStrGetCpEx(&pszCur, &CpIgnored);
869 }
870 *pcbWritten = pszCur - (const char *)pvBuf;
871 }
872 else
873 rc = VERR_WRITE_ERROR;
874 }
875 RTUtf16Free(pwszSrc);
876 }
877 }
878 else
879 rc = RTErrConvertFromErrno(errno);
880 if (RT_FAILURE(rc))
881 ASMAtomicWriteS32(&pStream->i32Error, rc);
882 return rc;
883 }
884#endif /* RT_OS_WINDOWS */
885
886 /*
887 * If we're sure it's text output, convert it from UTF-8 to the current
888 * code page before printing it.
889 *
890 * Note! Partial writes are not supported in this scenario because we
891 * cannot easily report back a written length matching the input.
892 */
893 /** @todo Skip this if the current code set is UTF-8. */
894 if ( pStream->fCurrentCodeSet
895 && !pStream->fBinary
896 && ( fSureIsText
897 || rtStrmIsUtf8Text(pvBuf, cbWrite))
898 )
899 {
900 char *pszSrcFree = NULL;
901 const char *pszSrc = (const char *)pvBuf;
902 if (pszSrc[cbWrite])
903 {
904 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbWrite);
905 if (pszSrc == NULL)
906 rc = VERR_NO_STR_MEMORY;
907 }
908 if (RT_SUCCESS(rc))
909 {
910 char *pszSrcCurCP;
911 rc = RTStrUtf8ToCurrentCP(&pszSrcCurCP, pszSrc);
912 if (RT_SUCCESS(rc))
913 {
914 size_t cchSrcCurCP = strlen(pszSrcCurCP);
915 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
916#ifdef HAVE_FWRITE_UNLOCKED
917 ssize_t cbWritten = fwrite_unlocked(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
918#else
919 ssize_t cbWritten = fwrite(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile);
920#endif
921 IPRT_ALIGNMENT_CHECKS_ENABLE();
922 if (cbWritten == 1)
923 {
924 if (pcbWritten)
925 *pcbWritten = cbWrite;
926 }
927#ifdef HAVE_FWRITE_UNLOCKED
928 else if (!ferror_unlocked(pStream->pFile))
929#else
930 else if (!ferror(pStream->pFile))
931#endif
932 {
933 if (pcbWritten)
934 *pcbWritten = 0;
935 }
936 else
937 rc = VERR_WRITE_ERROR;
938 RTStrFree(pszSrcCurCP);
939 }
940 RTStrFree(pszSrcFree);
941 }
942
943 if (RT_FAILURE(rc))
944 ASMAtomicWriteS32(&pStream->i32Error, rc);
945 return rc;
946 }
947
948 /*
949 * Otherwise, just write it as-is.
950 */
951 if (pcbWritten)
952 {
953 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
954#ifdef HAVE_FWRITE_UNLOCKED
955 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbWrite, pStream->pFile);
956#else
957 *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);
958#endif
959 IPRT_ALIGNMENT_CHECKS_ENABLE();
960 if ( *pcbWritten == cbWrite
961#ifdef HAVE_FWRITE_UNLOCKED
962 || !ferror_unlocked(pStream->pFile))
963#else
964 || !ferror(pStream->pFile))
965#endif
966 return VINF_SUCCESS;
967 rc = VERR_WRITE_ERROR;
968 }
969 else
970 {
971 /* Must write it all! */
972 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
973#ifdef HAVE_FWRITE_UNLOCKED
974 size_t cbWritten = fwrite_unlocked(pvBuf, cbWrite, 1, pStream->pFile);
975#else
976 size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile);
977#endif
978 IPRT_ALIGNMENT_CHECKS_ENABLE();
979 if (cbWritten == 1)
980 return VINF_SUCCESS;
981#ifdef HAVE_FWRITE_UNLOCKED
982 if (!ferror_unlocked(pStream->pFile))
983#else
984 if (!ferror(pStream->pFile))
985#endif
986 return VINF_SUCCESS; /* WEIRD! But anyway... */
987
988 rc = VERR_WRITE_ERROR;
989 }
990 ASMAtomicWriteS32(&pStream->i32Error, rc);
991
992 return rc;
993}
994
995
996/**
997 * Internal write API.
998 *
999 * @returns IPRT status code.
1000 * @param pStream The stream.
1001 * @param pvBuf What to write.
1002 * @param cbWrite How much to write.
1003 * @param pcbWritten Where to optionally return the number of bytes
1004 * written.
1005 * @param fSureIsText Set if we're sure this is UTF-8 text already.
1006 */
1007static int rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, bool fSureIsText)
1008{
1009 rtStrmLock(pStream);
1010 int rc = rtStrmWriteLocked(pStream, pvBuf, cbWrite, pcbWritten, fSureIsText);
1011 rtStrmUnlock(pStream);
1012 return rc;
1013}
1014
1015
1016/**
1017 * Writes to a file stream.
1018 *
1019 * @returns iprt status code.
1020 * @param pStream The stream.
1021 * @param pvBuf Where to get the bits to write from.
1022 * @param cbWrite Number of bytes to write.
1023 * @param pcbWritten Where to store the number of bytes actually written.
1024 * If NULL cbWrite bytes are written or an error is returned.
1025 */
1026RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
1027{
1028 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1029 return rtStrmWrite(pStream, pvBuf, cbWrite, pcbWritten, false);
1030}
1031
1032
1033/**
1034 * Reads a character from a file stream.
1035 *
1036 * @returns The char as an unsigned char cast to int.
1037 * @returns -1 on failure.
1038 * @param pStream The stream.
1039 */
1040RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
1041{
1042 unsigned char ch;
1043 int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
1044 if (RT_SUCCESS(rc))
1045 return ch;
1046 return -1;
1047}
1048
1049
1050/**
1051 * Writes a character to a file stream.
1052 *
1053 * @returns iprt status code.
1054 * @param pStream The stream.
1055 * @param ch The char to write.
1056 */
1057RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
1058{
1059 return rtStrmWrite(pStream, &ch, 1, NULL, true /*fSureIsText*/);
1060}
1061
1062
1063/**
1064 * Writes a string to a file stream.
1065 *
1066 * @returns iprt status code.
1067 * @param pStream The stream.
1068 * @param pszString The string to write.
1069 * No newlines or anything is appended or prepended.
1070 * The terminating '\\0' is not written, of course.
1071 */
1072RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
1073{
1074 size_t cch = strlen(pszString);
1075 return rtStrmWrite(pStream, pszString, cch, NULL, true /*fSureIsText*/);
1076}
1077
1078
1079RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString)
1080{
1081 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1082 int rc;
1083 if (pszString && cbString > 1)
1084 {
1085 rc = pStream->i32Error;
1086 if (RT_SUCCESS(rc))
1087 {
1088 cbString--; /* save space for the terminator. */
1089 rtStrmLock(pStream);
1090 for (;;)
1091 {
1092#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
1093 int ch = fgetc_unlocked(pStream->pFile);
1094#else
1095 int ch = fgetc(pStream->pFile);
1096#endif
1097
1098 /* Deal with \r\n sequences here. We'll return lone CR, but
1099 treat CRLF as LF. */
1100 if (ch == '\r')
1101 {
1102#ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
1103 ch = fgetc_unlocked(pStream->pFile);
1104#else
1105 ch = fgetc(pStream->pFile);
1106#endif
1107 if (ch == '\n')
1108 break;
1109
1110 *pszString++ = '\r';
1111 if (--cbString <= 0)
1112 {
1113 /* yeah, this is an error, we dropped a character. */
1114 rc = VERR_BUFFER_OVERFLOW;
1115 break;
1116 }
1117 }
1118
1119 /* Deal with end of file. */
1120 if (ch == EOF)
1121 {
1122#ifdef HAVE_FWRITE_UNLOCKED
1123 if (feof_unlocked(pStream->pFile))
1124#else
1125 if (feof(pStream->pFile))
1126#endif
1127 {
1128 rc = VERR_EOF;
1129 break;
1130 }
1131#ifdef HAVE_FWRITE_UNLOCKED
1132 if (ferror_unlocked(pStream->pFile))
1133#else
1134 if (ferror(pStream->pFile))
1135#endif
1136 rc = VERR_READ_ERROR;
1137 else
1138 {
1139 AssertMsgFailed(("This shouldn't happen\n"));
1140 rc = VERR_INTERNAL_ERROR;
1141 }
1142 break;
1143 }
1144
1145 /* Deal with null terminator and (lone) new line. */
1146 if (ch == '\0' || ch == '\n')
1147 break;
1148
1149 /* No special character, append it to the return string. */
1150 *pszString++ = ch;
1151 if (--cbString <= 0)
1152 {
1153 rc = VINF_BUFFER_OVERFLOW;
1154 break;
1155 }
1156 }
1157 rtStrmUnlock(pStream);
1158
1159 *pszString = '\0';
1160 if (RT_FAILURE(rc))
1161 ASMAtomicWriteS32(&pStream->i32Error, rc);
1162 }
1163 }
1164 else
1165 {
1166 AssertMsgFailed(("no buffer or too small buffer!\n"));
1167 rc = VERR_INVALID_PARAMETER;
1168 }
1169 return rc;
1170}
1171
1172
1173/**
1174 * Flushes a stream.
1175 *
1176 * @returns iprt status code.
1177 * @param pStream The stream to flush.
1178 */
1179RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
1180{
1181 if (!fflush(pStream->pFile))
1182 return VINF_SUCCESS;
1183 return RTErrConvertFromErrno(errno);
1184}
1185
1186
1187/**
1188 * Output callback.
1189 *
1190 * @returns number of bytes written.
1191 * @param pvArg User argument.
1192 * @param pachChars Pointer to an array of utf-8 characters.
1193 * @param cchChars Number of bytes in the character array pointed to by pachChars.
1194 */
1195static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
1196{
1197 if (cchChars)
1198 rtStrmWriteLocked((PRTSTREAM)pvArg, pachChars, cchChars, NULL, true /*fSureIsText*/);
1199 /* else: ignore termination call. */
1200 return cchChars;
1201}
1202
1203
1204/**
1205 * Prints a formatted string to the specified stream.
1206 *
1207 * @returns Number of bytes printed.
1208 * @param pStream The stream to print to.
1209 * @param pszFormat IPRT format string.
1210 * @param args Arguments specified by pszFormat.
1211 */
1212RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
1213{
1214 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
1215 int rc = pStream->i32Error;
1216 if (RT_SUCCESS(rc))
1217 {
1218 rtStrmLock(pStream);
1219// pStream->fShouldFlush = true;
1220 rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
1221 rtStrmUnlock(pStream);
1222 Assert(rc >= 0);
1223 }
1224 else
1225 rc = -1;
1226 return rc;
1227}
1228
1229
1230/**
1231 * Prints a formatted string to the specified stream.
1232 *
1233 * @returns Number of bytes printed.
1234 * @param pStream The stream to print to.
1235 * @param pszFormat IPRT format string.
1236 * @param ... Arguments specified by pszFormat.
1237 */
1238RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
1239{
1240 va_list args;
1241 va_start(args, pszFormat);
1242 int rc = RTStrmPrintfV(pStream, pszFormat, args);
1243 va_end(args);
1244 return rc;
1245}
1246
1247
1248/**
1249 * Dumper vprintf-like function outputting to a stream.
1250 *
1251 * @param pvUser The stream to print to. NULL means standard output.
1252 * @param pszFormat Runtime format string.
1253 * @param va Arguments specified by pszFormat.
1254 */
1255RTDECL(void) RTStrmDumpPrintfV(void *pvUser, const char *pszFormat, va_list va)
1256{
1257 RTStrmPrintfV(pvUser ? (PRTSTREAM)pvUser : g_pStdOut, pszFormat, va);
1258}
1259
1260
1261/**
1262 * Prints a formatted string to the standard output stream (g_pStdOut).
1263 *
1264 * @returns Number of bytes printed.
1265 * @param pszFormat IPRT format string.
1266 * @param args Arguments specified by pszFormat.
1267 */
1268RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
1269{
1270 return RTStrmPrintfV(g_pStdOut, pszFormat, args);
1271}
1272
1273
1274/**
1275 * Prints a formatted string to the standard output stream (g_pStdOut).
1276 *
1277 * @returns Number of bytes printed.
1278 * @param pszFormat IPRT format string.
1279 * @param ... Arguments specified by pszFormat.
1280 */
1281RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
1282{
1283 va_list args;
1284 va_start(args, pszFormat);
1285 int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
1286 va_end(args);
1287 return rc;
1288}
1289
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