Changeset 95886 in vbox for trunk/src/VBox
- Timestamp:
- Jul 28, 2022 1:39:36 AM (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/stream.cpp
r95587 r95886 27 27 28 28 29 /********************************************************************************************************************************* 30 * Defined Constants And Macros * 31 *********************************************************************************************************************************/ 32 /** @def RTSTREAM_STANDALONE 33 * Standalone streams w/o depending on stdio.h, using our RTFile API for 34 * file/whatever access. */ 35 #if defined(IPRT_NO_CRT) || defined(DOXYGEN_RUNNING) 36 # define RTSTREAM_STANDALONE 37 #endif 38 29 39 #if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */ 30 #define HAVE_FWRITE_UNLOCKED 31 #endif 40 # ifndef RTSTREAM_STANDALONE 41 # define HAVE_FWRITE_UNLOCKED 42 # endif 43 #endif 44 45 /** @def RTSTREAM_WITH_TEXT_MODE 46 * Indicates whether we need to support the 'text' mode files and convert 47 * CRLF to LF while reading and writing. */ 48 #if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING) 49 # define RTSTREAM_WITH_TEXT_MODE 50 #endif 51 32 52 33 53 … … 44 64 #include <iprt/string.h> 45 65 #include <iprt/assert.h> 46 #include <iprt/alloc.h>47 66 #include <iprt/ctype.h> 48 67 #include <iprt/err.h> 68 #ifdef RTSTREAM_STANDALONE 69 # include <iprt/file.h> 70 #endif 71 #include <iprt/mem.h> 49 72 #include <iprt/param.h> 50 73 #include <iprt/string.h> … … 53 76 #include "internal/magics.h" 54 77 55 #include <stdio.h> 56 #include <errno.h> 57 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 58 # include <io.h> 59 # include <fcntl.h> 78 #ifndef RTSTREAM_STANDALONE 79 # include <stdio.h> 80 # include <errno.h> 81 # if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 82 # include <io.h> 83 # include <fcntl.h> 84 # endif 60 85 #endif 61 86 #ifdef RT_OS_WINDOWS 62 87 # include <iprt/utf16.h> 63 88 # include <iprt/win/windows.h> 64 #el se89 #elif !defined(RTSTREAM_STANDALONE) 65 90 # include <termios.h> 66 91 # include <unistd.h> … … 68 93 #endif 69 94 70 #if def RT_OS_OS295 #if defined(RT_OS_OS2) && !defined(RTSTREAM_STANDALONE) 71 96 # define _O_TEXT O_TEXT 72 97 # define _O_BINARY O_BINARY … … 77 102 * Structures and Typedefs * 78 103 *********************************************************************************************************************************/ 104 #ifdef RTSTREAM_STANDALONE 105 /** The buffer direction. */ 106 typedef enum RTSTREAMBUFDIR 107 { 108 RTSTREAMBUFDIR_NONE = 0, 109 RTSTREAMBUFDIR_READ, 110 RTSTREAMBUFDIR_WRITE 111 } RTSTREAMBUFDIR; 112 113 /** The buffer style. */ 114 typedef enum RTSTREAMBUFSTYLE 115 { 116 RTSTREAMBUFSTYLE_UNBUFFERED = 0, 117 RTSTREAMBUFSTYLE_LINE, 118 RTSTREAMBUFSTYLE_FULL 119 } RTSTREAMBUFSTYLE; 120 121 #endif 122 79 123 /** 80 124 * File stream. … … 86 130 /** File stream error. */ 87 131 int32_t volatile i32Error; 132 #ifndef RTSTREAM_STANDALONE 88 133 /** Pointer to the LIBC file stream. */ 89 134 FILE *pFile; 135 #else 136 /** Indicates which standard handle this is supposed to be. 137 * Set to RTHANDLESTD_INVALID if not one of the tree standard streams. */ 138 RTHANDLESTD enmStdHandle; 139 /** The IPRT handle backing this stream. 140 * This is initialized lazily using enmStdHandle for the three standard 141 * streams. */ 142 RTFILE hFile; 143 /** Buffer. */ 144 char *pchBuf; 145 /** Buffer allocation size. */ 146 size_t cbBufAlloc; 147 /** Offset of the first valid byte in the buffer. */ 148 size_t offBufFirst; 149 /** Offset of the end of valid bytes in the buffer (exclusive). */ 150 size_t offBufEnd; 151 /** The stream buffer direction. */ 152 RTSTREAMBUFDIR enmBufDir; 153 /** The buffering style (unbuffered, line, full). */ 154 RTSTREAMBUFSTYLE enmBufStyle; 155 # ifdef RTSTREAM_WITH_TEXT_MODE 156 /** Indicates that we've got a CR ('\\r') beyond the end of official buffer 157 * and need to check if there is a LF following it. This member is ignored 158 * in binary mode. */ 159 bool fPendingCr; 160 # endif 161 #endif 90 162 /** Stream is using the current process code set. */ 91 163 bool fCurrentCodeSet; … … 94 166 /** Whether to recheck the stream mode before writing. */ 95 167 bool fRecheckMode; 96 #if ndef HAVE_FWRITE_UNLOCKED168 #if !defined(HAVE_FWRITE_UNLOCKED) || defined(RTSTREAM_STANDALONE) 97 169 /** Critical section for serializing access to the stream. */ 98 170 PRTCRITSECT pCritSect; … … 123 195 static RTSTREAM g_StdIn = 124 196 { 125 RTSTREAM_MAGIC, 126 0, 127 stdin, 128 /*.fCurrentCodeSet = */ true, 129 /*.fBinary = */ false, 130 /*.fRecheckMode = */ true 197 /* .u32Magic = */ RTSTREAM_MAGIC, 198 /* .i32Error = */ 0, 199 #ifndef RTSTREAM_STANDALONE 200 /* .pFile = */ stdin, 201 #else 202 /* .enmStdHandle = */ RTHANDLESTD_INPUT, 203 /* .hFile = */ NIL_RTFILE, 204 /* .pchBuf = */ NULL, 205 /* .cbBufAlloc = */ 0, 206 /* .offBufFirst = */ 0, 207 /* .offBufEnd = */ 0, 208 /* .enmBufDir = */ RTSTREAMBUFDIR_NONE, 209 /* .enmBufStyle = */ RTSTREAMBUFSTYLE_UNBUFFERED, 210 # ifdef RTSTREAM_WITH_TEXT_MODE 211 /* .fPendingCr = */ false, 212 # endif 213 #endif 214 /* .fCurrentCodeSet = */ true, 215 /* .fBinary = */ false, 216 /* .fRecheckMode = */ true, 131 217 #ifndef HAVE_FWRITE_UNLOCKED 132 ,NULL218 /* .pCritSect = */ NULL 133 219 #endif 134 220 }; … … 137 223 static RTSTREAM g_StdErr = 138 224 { 139 RTSTREAM_MAGIC, 140 0, 141 stderr, 142 /*.fCurrentCodeSet = */ true, 143 /*.fBinary = */ false, 144 /*.fRecheckMode = */ true 225 /* .u32Magic = */ RTSTREAM_MAGIC, 226 /* .i32Error = */ 0, 227 #ifndef RTSTREAM_STANDALONE 228 /* .pFile = */ stderr, 229 #else 230 /* .enmStdHandle = */ RTHANDLESTD_ERROR, 231 /* .hFile = */ NIL_RTFILE, 232 /* .pchBuf = */ NULL, 233 /* .cbBufAlloc = */ 0, 234 /* .offBufFirst = */ 0, 235 /* .offBufEnd = */ 0, 236 /* .enmBufDir = */ RTSTREAMBUFDIR_NONE, 237 /* .enmBufStyle = */ RTSTREAMBUFSTYLE_UNBUFFERED, 238 # ifdef RTSTREAM_WITH_TEXT_MODE 239 /* .fPendingCr = */ false, 240 # endif 241 #endif 242 /* .fCurrentCodeSet = */ true, 243 /* .fBinary = */ false, 244 /* .fRecheckMode = */ true, 145 245 #ifndef HAVE_FWRITE_UNLOCKED 146 ,NULL246 /* .pCritSect = */ NULL 147 247 #endif 148 248 }; … … 151 251 static RTSTREAM g_StdOut = 152 252 { 153 RTSTREAM_MAGIC, 154 0, 155 stdout, 156 /*.fCurrentCodeSet = */ true, 157 /*.fBinary = */ false, 158 /*.fRecheckMode = */ true 253 /* .u32Magic = */ RTSTREAM_MAGIC, 254 /* .i32Error = */ 0, 255 #ifndef RTSTREAM_STANDALONE 256 /* .pFile = */ stderr, 257 #else 258 /* .enmStdHandle = */ RTHANDLESTD_OUTPUT, 259 /* .hFile = */ NIL_RTFILE, 260 /* .pchBuf = */ NULL, 261 /* .cbBufAlloc = */ 0, 262 /* .offBufFirst = */ 0, 263 /* .offBufEnd = */ 0, 264 /* .enmBufDir = */ RTSTREAMBUFDIR_NONE, 265 /* .enmBufStyle = */ RTSTREAMBUFSTYLE_LINE, 266 # ifdef RTSTREAM_WITH_TEXT_MODE 267 /* .fPendingCr = */ false, 268 # endif 269 #endif 270 /* .fCurrentCodeSet = */ true, 271 /* .fBinary = */ false, 272 /* .fRecheckMode = */ true, 159 273 #ifndef HAVE_FWRITE_UNLOCKED 160 ,NULL274 /* .pCritSect = */ NULL 161 275 #endif 162 276 }; … … 254 368 * @param pszFilename Path to the file to open. 255 369 * @param pszMode The open mode. See fopen() standard. 256 * Format: <a|r|w>[+][b |t]370 * Format: <a|r|w>[+][b] 257 371 * @param ppStream Where to store the opened stream. 258 372 */ … … 260 374 { 261 375 /* 262 * Validate input .376 * Validate input and look for things we care for in the pszMode string. 263 377 */ 264 if (!pszMode || !*pszMode) 265 { 266 AssertMsgFailed(("No pszMode!\n")); 267 return VERR_INVALID_PARAMETER; 268 } 269 if (!pszFilename) 270 { 271 AssertMsgFailed(("No pszFilename!\n")); 272 return VERR_INVALID_PARAMETER; 273 } 274 bool fOk = true; 275 bool fBinary = false; 378 AssertReturn(pszMode && *pszMode, VERR_INVALID_FLAGS); 379 AssertReturn(pszFilename, VERR_INVALID_PARAMETER); 380 381 bool fOk = true; 382 bool fBinary = false; 383 #ifdef RTSTREAM_STANDALONE 384 uint64_t fOpen = RTFILE_O_DENY_NONE; 385 #endif 276 386 switch (*pszMode) 277 387 { … … 281 391 switch (pszMode[1]) 282 392 { 393 case 'b': 394 fBinary = true; 395 RT_FALL_THRU(); 283 396 case '\0': 397 #ifdef RTSTREAM_STANDALONE 398 fOpen |= *pszMode == 'a' ? RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_APPEND 399 : *pszMode == 'w' ? RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE 400 : RTFILE_O_OPEN | RTFILE_O_READ; 401 #endif 284 402 break; 285 403 286 404 case '+': 405 #ifdef RTSTREAM_STANDALONE 406 fOpen |= *pszMode == 'a' ? RTFILE_O_OPEN_CREATE | RTFILE_O_READ | RTFILE_O_WRITE | RTFILE_O_APPEND 407 : *pszMode == 'w' ? RTFILE_O_CREATE_REPLACE | RTFILE_O_READ | RTFILE_O_WRITE 408 : RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE; 409 #endif 287 410 switch (pszMode[2]) 288 411 { … … 290 413 break; 291 414 292 //case 't':293 // break;294 295 415 case 'b': 296 416 fBinary = true; … … 301 421 break; 302 422 } 303 break;304 305 //case 't':306 // break;307 308 case 'b':309 fBinary = true;310 423 break; 311 424 … … 321 434 if (!fOk) 322 435 { 323 AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b |t]'\n", pszMode));436 AssertMsgFailed(("Invalid pszMode='%s', '<a|r|w>[+][b]'\n", pszMode)); 324 437 return VINF_SUCCESS; 325 438 } … … 328 441 * Allocate the stream handle and try open it. 329 442 */ 330 PRTSTREAM pStream = (PRTSTREAM)RTMemAlloc(sizeof(*pStream)); 443 int rc = VERR_NO_MEMORY; 444 PRTSTREAM pStream = (PRTSTREAM)RTMemAllocZ(sizeof(*pStream)); 331 445 if (pStream) 332 446 { 333 pStream->u32Magic = RTSTREAM_MAGIC; 334 pStream->i32Error = VINF_SUCCESS; 335 pStream->fCurrentCodeSet = false; 336 pStream->fBinary = fBinary; 337 pStream->fRecheckMode = false; 447 pStream->u32Magic = RTSTREAM_MAGIC; 448 #ifdef RTSTREAM_STANDALONE 449 pStream->enmStdHandle = RTHANDLESTD_INVALID; 450 pStream->hFile = NIL_RTFILE; 451 pStream->pchBuf = NULL; 452 pStream->cbBufAlloc = 0; 453 pStream->offBufFirst = 0; 454 pStream->offBufEnd = 0; 455 pStream->enmBufDir = RTSTREAMBUFDIR_NONE; 456 pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL; 457 # ifdef RTSTREAM_WITH_TEXT_MODE 458 pStream->fPendingCr = false, 459 # endif 460 #endif 461 pStream->i32Error = VINF_SUCCESS; 462 pStream->fCurrentCodeSet = false; 463 pStream->fBinary = fBinary; 464 pStream->fRecheckMode = false; 338 465 #ifndef HAVE_FWRITE_UNLOCKED 339 pStream->pCritSect = NULL; 340 #endif /* HAVE_FWRITE_UNLOCKED */ 466 pStream->pCritSect = NULL; 467 #endif 468 #ifdef RTSTREAM_STANDALONE 469 rc = RTFileOpen(&pStream->hFile, pszFilename, fOpen); 470 #else 341 471 pStream->pFile = fopen(pszFilename, pszMode); 472 rc = pStream->pFile ? VINF_SUCCESS : RTErrConvertFromErrno(errno); 342 473 if (pStream->pFile) 474 #endif 475 if (RT_SUCCESS(rc)) 343 476 { 344 477 *ppStream = pStream; … … 346 479 } 347 480 RTMemFree(pStream); 348 return RTErrConvertFromErrno(errno); 349 } 350 return VERR_NO_MEMORY; 481 } 482 return rc; 351 483 } 352 484 … … 357 489 * @returns iprt status code. 358 490 * @param pszMode The open mode. See fopen() standard. 359 * Format: <a|r|w>[+][b |t]491 * Format: <a|r|w>[+][b] 360 492 * @param ppStream Where to store the opened stream. 361 493 * @param pszFilenameFmt Filename path format string. … … 383 515 * @returns iprt status code. 384 516 * @param pszMode The open mode. See fopen() standard. 385 * Format: <a|r|w>[+][b |t]517 * Format: <a|r|w>[+][b] 386 518 * @param ppStream Where to store the opened stream. 387 519 * @param pszFilenameFmt Filename path format string. … … 406 538 RTR3DECL(int) RTStrmClose(PRTSTREAM pStream) 407 539 { 540 /* 541 * Validate input. 542 */ 408 543 if (!pStream) 409 544 return VINF_SUCCESS; 410 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER); 411 412 if (!fclose(pStream->pFile)) 413 { 414 pStream->u32Magic = 0xdeaddead; 415 pStream->pFile = NULL; 545 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 546 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC); 547 548 /* We don't implement closing any of the standard handles at present. */ 549 AssertReturn(pStream != &g_StdIn, VERR_NOT_SUPPORTED); 550 AssertReturn(pStream != &g_StdOut, VERR_NOT_SUPPORTED); 551 AssertReturn(pStream != &g_StdErr, VERR_NOT_SUPPORTED); 552 553 /* 554 * Invalidate the stream and destroy the critical section first. 555 */ 556 pStream->u32Magic = 0xdeaddead; 416 557 #ifndef HAVE_FWRITE_UNLOCKED 417 if (pStream->pCritSect) 418 { 419 RTCritSectEnter(pStream->pCritSect); 420 RTCritSectLeave(pStream->pCritSect); 421 RTCritSectDelete(pStream->pCritSect); 422 RTMemFree(pStream->pCritSect); 423 pStream->pCritSect = NULL; 424 } 425 #endif 426 RTMemFree(pStream); 427 return VINF_SUCCESS; 428 } 429 430 return RTErrConvertFromErrno(errno); 558 if (pStream->pCritSect) 559 { 560 RTCritSectEnter(pStream->pCritSect); 561 RTCritSectLeave(pStream->pCritSect); 562 RTCritSectDelete(pStream->pCritSect); 563 RTMemFree(pStream->pCritSect); 564 pStream->pCritSect = NULL; 565 } 566 #endif 567 568 /* 569 * Flush and close the underlying file. 570 */ 571 #ifdef RTSTREAM_STANDALONE 572 int const rc1 = RTStrmFlush(pStream); 573 AssertRC(rc1); 574 int const rc2 = RTFileClose(pStream->hFile); 575 AssertRC(rc2); 576 int const rc = RT_SUCCESS(rc1) ? rc2 : rc1; 577 #else 578 int const rc = !fclose(pStream->pFile) ? VINF_SUCCESS : RTErrConvertFromErrno(errno); 579 #endif 580 581 /* 582 * Destroy the stream. 583 */ 584 #ifdef RTSTREAM_STANDALONE 585 pStream->hFile = NIL_RTFILE; 586 RTMemFree(pStream->pchBuf); 587 pStream->pchBuf = NULL; 588 pStream->cbBufAlloc = 0; 589 pStream->offBufFirst = 0; 590 pStream->offBufEnd = 0; 591 #else 592 pStream->pFile = NULL; 593 #endif 594 RTMemFree(pStream); 595 return rc; 431 596 } 432 597 … … 440 605 RTR3DECL(int) RTStrmError(PRTSTREAM pStream) 441 606 { 442 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER); 607 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 608 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC); 443 609 return pStream->i32Error; 444 610 } … … 456 622 RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream) 457 623 { 458 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER); 459 624 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 625 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC); 626 627 #ifndef RTSTREAM_STANDALONE 460 628 clearerr(pStream->pFile); 629 #endif 461 630 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS); 462 631 return VINF_SUCCESS; … … 487 656 } 488 657 489 490 RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars) 491 { 492 int rc = VINF_SUCCESS; 493 494 AssertPtrReturn(pStream, VERR_INVALID_HANDLE); 495 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 496 AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER); 497 498 int fh = fileno(pStream->pFile); 499 if (isatty(fh)) 500 { 501 #ifdef RT_OS_WINDOWS 502 DWORD dwMode; 503 HANDLE hCon = (HANDLE)_get_osfhandle(fh); 504 if (GetConsoleMode(hCon, &dwMode)) 505 *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT); 506 else 507 rc = RTErrConvertFromWin32(GetLastError()); 658 #ifdef RTSTREAM_STANDALONE 659 660 /** 661 * Deals with NIL_RTFILE in rtStrmGetFile. 662 */ 663 DECL_NO_INLINE(static, RTFILE) rtStrmGetFileNil(PRTSTREAM pStream) 664 { 665 # ifdef RT_OS_WINDOWS 666 DWORD dwStdHandle; 667 switch (pStream->enmStdHandle) 668 { 669 case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break; 670 case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break; 671 case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break; 672 default: return NIL_RTFILE; 673 } 674 HANDLE hHandle = GetStdHandle(dwStdHandle); 675 if (hHandle != INVALID_HANDLE_VALUE && hHandle != NULL) 676 { 677 int rc = RTFileFromNative(&pStream->hFile, (uintptr_t)hHandle); 678 if (RT_SUCCESS(rc)) 679 { 680 /* Switch to full buffering if not a console handle. */ 681 DWORD dwMode; 682 if (!GetConsoleMode(hHandle, &dwMode)) 683 pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL; 684 685 return pStream->hFile; 686 } 687 } 688 689 # else 690 uintptr_t uNative; 691 switch (pStream->enmStdHandle) 692 { 693 case RTHANDLESTD_INPUT: uNative = RTFILE_NATIVE_STDIN; break; 694 case RTHANDLESTD_OUTPUT: uNative = RTFILE_NATIVE_STDOUT; break; 695 case RTHANDLESTD_ERROR: uNative = RTFILE_NATIVE_STDERR; break; 696 default: return NIL_RTFILE; 697 } 698 int rc = RTFileFromNative(&pStream->hFile, uNative); 699 if (RT_SUCCESS(rc)) 700 { 701 /* Switch to full buffering if not a console handle. */ 702 if (!isatty((int)uNative)) 703 pStream->enmBufStyle = RTSTREAMBUFDIR_FULL; 704 705 return pStream->hFile; 706 } 707 708 # endif 709 return NIL_RTFILE; 710 } 711 712 /** 713 * For lazily resolving handles for the standard streams. 714 */ 715 DECLINLINE(RTFILE) rtStrmGetFile(PRTSTREAM pStream) 716 { 717 RTFILE hFile = pStream->hFile; 718 if (hFile != NIL_RTFILE) 719 return hFile; 720 return rtStrmGetFileNil(pStream); 721 } 722 723 #endif /* RTSTREAM_STANDALONE */ 724 725 726 /** 727 * Wrapper around isatty, assumes caller takes care of stream locking/whatever 728 * is needed. 729 */ 730 DECLINLINE(bool) rtStrmIsTerminal(PRTSTREAM pStream) 731 { 732 #ifdef RTSTREAM_STANDALONE 733 RTFILE hFile = rtStrmGetFile(pStream); 734 if (hFile != NIL_RTFILE) 735 { 736 HANDLE hNative = (HANDLE)RTFileToNative(hFile); 737 DWORD dwType = GetFileType(hNative); 738 if (dwType == FILE_TYPE_CHAR) 739 { 740 DWORD dwMode; 741 if (GetConsoleMode(hNative, &dwMode)) 742 return true; 743 } 744 } 745 return false; 746 508 747 #else 509 struct termios Termios;510 511 int rcPosix = tcgetattr(fh, &Termios);512 if (!rcPosix)513 *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);514 else515 rc = RTErrConvertFromErrno(errno);516 #endif517 }518 else519 rc = VERR_INVALID_HANDLE;520 521 return rc;522 }523 524 525 RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)526 {527 int rc = VINF_SUCCESS;528 529 AssertPtrReturn(pStream, VERR_INVALID_HANDLE);530 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);531 532 int fh = fileno(pStream->pFile);533 if (isatty(fh))534 {535 #ifdef RT_OS_WINDOWS536 DWORD dwMode;537 HANDLE hCon = (HANDLE)_get_osfhandle(fh);538 if (GetConsoleMode(hCon, &dwMode))539 {540 if (fEchoChars)541 dwMode |= ENABLE_ECHO_INPUT;542 else543 dwMode &= ~ENABLE_ECHO_INPUT;544 if (!SetConsoleMode(hCon, dwMode))545 rc = RTErrConvertFromWin32(GetLastError());546 }547 else548 rc = RTErrConvertFromWin32(GetLastError());549 #else550 struct termios Termios;551 552 int rcPosix = tcgetattr(fh, &Termios);553 if (!rcPosix)554 {555 if (fEchoChars)556 Termios.c_lflag |= ECHO;557 else558 Termios.c_lflag &= ~ECHO;559 560 rcPosix = tcsetattr(fh, TCSAFLUSH, &Termios);561 if (rcPosix != 0)562 rc = RTErrConvertFromErrno(errno);563 }564 else565 rc = RTErrConvertFromErrno(errno);566 #endif567 }568 else569 rc = VERR_INVALID_HANDLE;570 571 return rc;572 }573 574 575 RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)576 {577 AssertPtrReturn(pStream, false);578 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);579 580 748 if (pStream->pFile) 581 749 { 582 750 int fh = fileno(pStream->pFile); 583 if (isatty(fh) )584 { 585 # ifdef RT_OS_WINDOWS751 if (isatty(fh) != 0) 752 { 753 # ifdef RT_OS_WINDOWS 586 754 DWORD dwMode; 587 755 HANDLE hCon = (HANDLE)_get_osfhandle(fh); 588 756 if (GetConsoleMode(hCon, &dwMode)) 589 757 return true; 758 # else 759 return true; 760 # endif 761 } 762 } 763 return false; 764 #endif 765 } 766 767 768 static int rtStrmInputGetEchoCharsNative(uintptr_t hNative, bool *pfEchoChars) 769 { 770 #ifdef RT_OS_WINDOWS 771 DWORD dwMode; 772 if (GetConsoleMode((HANDLE)hNative, &dwMode)) 773 *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT); 774 else 775 { 776 DWORD dwErr = GetLastError(); 777 if (dwErr == ERROR_INVALID_HANDLE) 778 return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE; 779 return RTErrConvertFromWin32(dwErr); 780 } 590 781 #else 591 return true; 592 #endif 593 } 594 } 595 return false; 782 struct termios Termios; 783 int rcPosix = tcgetattr((int)fh, &Termios); 784 if (!rcPosix) 785 *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO); 786 else 787 return errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno); 788 #endif 789 return VINF_SUCCESS; 790 } 791 792 793 794 RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars) 795 { 796 AssertPtrReturn(pStream, VERR_INVALID_HANDLE); 797 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 798 AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER); 799 800 #ifdef RTSTREAM_STANDALONE 801 return rtStrmInputGetEchoCharsNative(RTFileToNative(pStream->hFile), pfEchoChars); 802 #else 803 int rc; 804 int fh = fileno(pStream->pFile); 805 if (isatty(fh)) 806 { 807 # ifdef RT_OS_WINDOWS 808 rc = rtStrmInputGetEchoCharsNative(_get_osfhandle(fh), pfEchoChars); 809 # else 810 rc = rtStrmInputGetEchoCharsNative(fh, pfEchoChars); 811 # endif 812 } 813 else 814 rc = VERR_INVALID_FUNCTION; 815 return rc; 816 #endif 817 } 818 819 820 static int rtStrmInputSetEchoCharsNative(uintptr_t hNative, bool fEchoChars) 821 { 822 int rc; 823 #ifdef RT_OS_WINDOWS 824 DWORD dwMode; 825 if (GetConsoleMode((HANDLE)hNative, &dwMode)) 826 { 827 if (fEchoChars) 828 dwMode |= ENABLE_ECHO_INPUT; 829 else 830 dwMode &= ~ENABLE_ECHO_INPUT; 831 if (SetConsoleMode((HANDLE)hNative, dwMode)) 832 rc = VINF_SUCCESS; 833 else 834 rc = RTErrConvertFromWin32(GetLastError()); 835 } 836 else 837 { 838 DWORD dwErr = GetLastError(); 839 if (dwErr == ERROR_INVALID_HANDLE) 840 return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE; 841 return RTErrConvertFromWin32(dwErr); 842 } 843 #else 844 struct termios Termios; 845 int rcPosix = tcgetattr(fh, &Termios); 846 if (!rcPosix) 847 { 848 if (fEchoChars) 849 Termios.c_lflag |= ECHO; 850 else 851 Termios.c_lflag &= ~ECHO; 852 853 rcPosix = tcsetattr(fh, TCSAFLUSH, &Termios); 854 if (rcPosix == 0) 855 rc = VINF_SUCCESS; 856 else 857 rc = RTErrConvertFromErrno(errno); 858 } 859 else 860 rc = errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno); 861 #endif 862 return rc; 863 } 864 865 866 RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars) 867 { 868 AssertPtrReturn(pStream, VERR_INVALID_HANDLE); 869 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 870 871 #ifdef RTSTREAM_STANDALONE 872 return rtStrmInputSetEchoCharsNative(RTFileToNative(pStream->hFile), fEchoChars); 873 #else 874 int rc; 875 int fh = fileno(pStream->pFile); 876 if (isatty(fh)) 877 { 878 # ifdef RT_OS_WINDOWS 879 rc = rtStrmInputSetEchoCharsNative(_get_osfhandle(fh), fEchoChars); 880 # else 881 rc = rtStrmInputSetEchoCharsNative(fh, fEchoChars); 882 # endif 883 } 884 else 885 rc = VERR_INVALID_FUNCTION; 886 return rc; 887 #endif 888 } 889 890 891 RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream) 892 { 893 AssertPtrReturn(pStream, false); 894 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false); 895 896 return rtStrmIsTerminal(pStream); 596 897 } 597 898 … … 605 906 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 606 907 607 if (pStream->pFile) 608 { 609 int fh = fileno(pStream->pFile); 610 if (isatty(fh)) 611 { 908 if (rtStrmIsTerminal(pStream)) 909 { 612 910 #ifdef RT_OS_WINDOWS 613 CONSOLE_SCREEN_BUFFER_INFO Info; 614 HANDLE hCon = (HANDLE)_get_osfhandle(fh); 615 RT_ZERO(Info); 616 if (GetConsoleScreenBufferInfo(hCon, &Info)) 911 # ifdef RTSTREAM_STANDALONE 912 HANDLE hCon = (HANDLE)RTFileToNative(pStream->hFile); 913 # else 914 HANDLE hCon = (HANDLE)_get_osfhandle(fileno(pStream->pFile)); 915 # endif 916 CONSOLE_SCREEN_BUFFER_INFO Info; 917 RT_ZERO(Info); 918 if (GetConsoleScreenBufferInfo(hCon, &Info)) 919 { 920 *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80; 921 return VINF_SUCCESS; 922 } 923 return RTErrConvertFromWin32(GetLastError()); 924 925 #elif defined(RT_OS_OS2) && !defined(TIOCGWINSZ) /* only OS/2 should currently miss this */ 926 return VINF_SUCCESS; /* just pretend for now. */ 927 928 #else 929 struct winsize Info; 930 RT_ZERO(Info); 931 int rc = ioctl(fileno(pStream->pFile), TIOCGWINSZ, &Info); 932 if (rc >= 0) 933 { 934 *pcchWidth = Info.ws_col ? Info.ws_col : 80; 935 return VINF_SUCCESS; 936 } 937 return RTErrConvertFromErrno(errno); 938 #endif 939 } 940 return VERR_INVALID_FUNCTION; 941 } 942 943 944 #ifdef RTSTREAM_STANDALONE 945 946 DECLINLINE(void) rtStrmBufInvalidate(PRTSTREAM pStream) 947 { 948 pStream->enmBufDir = RTSTREAMBUFDIR_NONE; 949 pStream->offBufEnd = 0; 950 pStream->offBufFirst = 0; 951 } 952 953 954 static int rtStrmBufFlushWrite(PRTSTREAM pStream, size_t cbToFlush) 955 { 956 Assert(cbToFlush <= pStream->offBufEnd - pStream->offBufFirst); 957 958 /** @todo do nonblocking & incomplete writes? */ 959 size_t offBufFirst = pStream->offBufFirst; 960 int rc = RTFileWrite(rtStrmGetFile(pStream), &pStream->pchBuf[offBufFirst], cbToFlush, NULL); 961 if (RT_SUCCESS(rc)) 962 { 963 offBufFirst += cbToFlush; 964 if (offBufFirst >= pStream->offBufEnd) 965 pStream->offBufEnd = 0; 966 else 967 { 968 /* Shift up the remaining content so the next write can take full 969 advantage of the buffer size. */ 970 size_t cbLeft = pStream->offBufEnd - offBufFirst; 971 memmove(pStream->pchBuf, &pStream->pchBuf[offBufFirst], cbLeft); 972 pStream->offBufEnd = cbLeft; 973 } 974 pStream->offBufFirst = 0; 975 return VINF_SUCCESS; 976 } 977 return rc; 978 } 979 980 981 static int rtStrmBufFlushWriteMaybe(PRTSTREAM pStream, bool fInvalidate) 982 { 983 if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE) 984 { 985 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst; 986 if (cbInBuffer > 0) 987 { 988 int rc = rtStrmBufFlushWrite(pStream, cbInBuffer); 989 if (fInvalidate) 990 pStream->enmBufDir = RTSTREAMBUFDIR_NONE; 991 return rc; 992 } 993 } 994 if (fInvalidate) 995 rtStrmBufInvalidate(pStream); 996 return VINF_SUCCESS; 997 } 998 999 1000 /** 1001 * Worker for rtStrmBufCheckErrorAndSwitchToReadMode and 1002 * rtStrmBufCheckErrorAndSwitchToWriteMode that allocates a buffer. 1003 * 1004 * Only updates cbBufAlloc and pchBuf, callers deals with error fallout. 1005 */ 1006 static int rtStrmBufAlloc(PRTSTREAM pStream) 1007 { 1008 size_t cbBuf = pStream->enmBufStyle == RTSTREAMBUFSTYLE_FULL ? _64K : _16K; 1009 do 1010 { 1011 pStream->pchBuf = (char *)RTMemAllocZ(cbBuf); 1012 if (RT_LIKELY(pStream->pchBuf)) 1013 { 1014 pStream->cbBufAlloc = cbBuf; 1015 return VINF_SUCCESS; 1016 } 1017 cbBuf /= 2; 1018 } while (cbBuf >= 256); 1019 return VERR_NO_MEMORY; 1020 } 1021 1022 1023 /** 1024 * Checks the stream error status, flushed any pending writes, ensures there is 1025 * a buffer allocated and switches the stream to the read direction. 1026 * 1027 * @returns IPRT status code (same as i32Error). 1028 * @param pStream The stream. 1029 */ 1030 static int rtStrmBufCheckErrorAndSwitchToReadMode(PRTSTREAM pStream) 1031 { 1032 int rc = pStream->i32Error; 1033 if (RT_SUCCESS(rc)) 1034 { 1035 /* 1036 * We're very likely already in read mode and can return without doing 1037 * anything here. 1038 */ 1039 if (pStream->enmBufDir == RTSTREAMBUFDIR_READ) 1040 return VINF_SUCCESS; 1041 1042 /* 1043 * Flush any pending writes before switching the buffer to read: 1044 */ 1045 rc = rtStrmBufFlushWriteMaybe(pStream, false /*fInvalidate*/); 1046 if (RT_SUCCESS(rc)) 1047 { 1048 pStream->enmBufDir = RTSTREAMBUFDIR_READ; 1049 pStream->offBufEnd = 0; 1050 pStream->offBufFirst = 0; 1051 pStream->fPendingCr = false; 1052 1053 /* 1054 * Read direction implies a buffer, so make sure we've got one and 1055 * change to NONE direction if allocating one fails. 1056 */ 1057 if (pStream->pchBuf) 617 1058 { 618 *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;1059 Assert(pStream->cbBufAlloc >= 256); 619 1060 return VINF_SUCCESS; 620 1061 } 621 return RTErrConvertFromWin32(GetLastError()); 622 623 #elif defined(TIOCGWINSZ) || !defined(RT_OS_OS2) /* only OS/2 should currently miss this */ 624 struct winsize Info; 625 RT_ZERO(Info); 626 int rc = ioctl(fh, TIOCGWINSZ, &Info); 627 if (rc >= 0) 1062 1063 rc = rtStrmBufAlloc(pStream); 1064 if (RT_SUCCESS(rc)) 1065 return VINF_SUCCESS; 1066 1067 pStream->enmBufDir = RTSTREAMBUFDIR_NONE; 1068 } 1069 ASMAtomicWriteS32(&pStream->i32Error, rc); 1070 } 1071 return rc; 1072 } 1073 1074 1075 /** 1076 * Checks the stream error status, ensures there is a buffer allocated and 1077 * switches the stream to the write direction. 1078 * 1079 * @returns IPRT status code (same as i32Error). 1080 * @param pStream The stream. 1081 */ 1082 static int rtStrmBufCheckErrorAndSwitchToWriteMode(PRTSTREAM pStream) 1083 { 1084 int rc = pStream->i32Error; 1085 if (RT_SUCCESS(rc)) 1086 { 1087 /* 1088 * We're very likely already in write mode and can return without doing 1089 * anything here. 1090 */ 1091 if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE) 1092 return VINF_SUCCESS; 1093 1094 /* 1095 * A read buffer does not need any flushing, so we just have to make 1096 * sure there is a buffer present before switching to the write direction. 1097 */ 1098 pStream->enmBufDir = RTSTREAMBUFDIR_WRITE; 1099 pStream->offBufEnd = 0; 1100 pStream->offBufFirst = 0; 1101 if (pStream->pchBuf) 1102 { 1103 Assert(pStream->cbBufAlloc >= 256); 1104 return VINF_SUCCESS; 1105 } 1106 1107 rc = rtStrmBufAlloc(pStream); 1108 if (RT_SUCCESS(rc)) 1109 return VINF_SUCCESS; 1110 1111 pStream->enmBufDir = RTSTREAMBUFDIR_NONE; 1112 ASMAtomicWriteS32(&pStream->i32Error, rc); 1113 } 1114 return rc; 1115 } 1116 1117 1118 /** 1119 * Reads more bytes into the buffer. 1120 * 1121 * @returns IPRT status code (same as i32Error). 1122 * @param pStream The stream. 1123 */ 1124 static int rtStrmBufFill(PRTSTREAM pStream) 1125 { 1126 /* 1127 * Check preconditions 1128 */ 1129 Assert(pStream->i32Error == VINF_SUCCESS); 1130 Assert(pStream->enmBufDir == RTSTREAMBUFDIR_READ); 1131 AssertPtr(pStream->pchBuf); 1132 Assert(pStream->cbBufAlloc >= 256); 1133 Assert(pStream->offBufFirst <= pStream->cbBufAlloc); 1134 Assert(pStream->offBufEnd <= pStream->cbBufAlloc); 1135 Assert(pStream->offBufFirst <= pStream->offBufEnd); 1136 1137 /* 1138 * If there is data in the buffer, move it up to the start. 1139 */ 1140 size_t cbInBuffer; 1141 if (!pStream->offBufFirst) 1142 cbInBuffer = pStream->offBufEnd; 1143 else 1144 { 1145 cbInBuffer = pStream->offBufEnd - pStream->offBufFirst; 1146 if (cbInBuffer) 1147 memmove(pStream->pchBuf, &pStream->pchBuf[pStream->offBufFirst], cbInBuffer); 1148 pStream->offBufFirst = 0; 1149 pStream->offBufEnd = cbInBuffer; 1150 } 1151 1152 /* 1153 * Add pending CR to the buffer. 1154 */ 1155 size_t const offCrLfConvStart = cbInBuffer; 1156 Assert(cbInBuffer + 2 <= pStream->cbBufAlloc); 1157 if (!pStream->fPendingCr || pStream->fBinary) 1158 { /* likely */ } 1159 else 1160 { 1161 pStream->pchBuf[cbInBuffer] = '\r'; 1162 pStream->fPendingCr = false; 1163 pStream->offBufEnd = ++cbInBuffer; 1164 } 1165 1166 /* 1167 * Read data till the buffer is full. 1168 */ 1169 size_t cbRead = 0; 1170 int rc = RTFileRead(rtStrmGetFile(pStream), &pStream->pchBuf[cbInBuffer], pStream->cbBufAlloc - cbInBuffer, &cbRead); 1171 if (RT_SUCCESS(rc)) 1172 { 1173 cbInBuffer += cbRead; 1174 pStream->offBufEnd = cbInBuffer; 1175 1176 if (cbInBuffer != 0) 1177 { 1178 if (pStream->fBinary) 1179 return VINF_SUCCESS; 1180 } 1181 else 1182 { 1183 /** @todo this shouldn't be sticky, should it? */ 1184 ASMAtomicWriteS32(&pStream->i32Error, VERR_EOF); 1185 return VERR_EOF; 1186 } 1187 1188 /* 1189 * Do CRLF -> LF conversion in the buffer. 1190 */ 1191 char *pchCur = &pStream->pchBuf[offCrLfConvStart]; 1192 size_t cbLeft = cbInBuffer - offCrLfConvStart; 1193 while (cbLeft > 0) 1194 { 1195 Assert(&pchCur[cbLeft] == &pStream->pchBuf[pStream->offBufEnd]); 1196 char *pchCr = (char *)memchr(pchCur, '\r', cbLeft); 1197 if (pchCr) 628 1198 { 629 *pcchWidth = Info.ws_col ? Info.ws_col : 80; 630 return VINF_SUCCESS; 1199 size_t offCur = (size_t)(pchCr - pchCur); 1200 if (offCur + 1 < cbLeft) 1201 { 1202 if (pchCr[1] == '\n') 1203 { 1204 /* Found one '\r\n' sequence. Look for more before shifting the buffer content. */ 1205 cbLeft -= offCur; 1206 pchCur = pchCr; 1207 1208 do 1209 { 1210 *pchCur++ = '\n'; /* dst */ 1211 cbLeft -= 2; 1212 pchCr += 2; /* src */ 1213 } while (cbLeft >= 2 && pchCr[0] == '\r' && pchCr[1] == '\n'); 1214 1215 memmove(&pchCur, pchCr, cbLeft); 1216 } 1217 else 1218 { 1219 cbLeft -= offCur + 1; 1220 pchCur = pchCr + 1; 1221 } 1222 } 1223 else 1224 { 1225 Assert(pchCr == &pStream->pchBuf[pStream->offBufEnd - 1]); 1226 pStream->fPendingCr = true; 1227 pStream->offBufEnd = --cbInBuffer; 1228 break; 1229 } 631 1230 } 632 return RTErrConvertFromErrno(errno); 633 #endif 634 } 635 } 636 return VERR_INVALID_FUNCTION; 637 } 638 1231 else 1232 break; 1233 } 1234 1235 return VINF_SUCCESS; 1236 } 1237 1238 /* 1239 * If there is data in the buffer, don't raise the error till it has all 1240 * been consumed, ASSUMING that another fill call will follow and that the 1241 * error condition will reoccur then. 1242 * 1243 * Note! We may currently end up not converting a CRLF pair, if it's 1244 * split over a temporary EOF condition, since we forces the caller 1245 * to read the CR before requesting more data. However, it's not a 1246 * very likely scenario, so we'll just leave it like that for now. 1247 */ 1248 if (cbInBuffer) 1249 return VINF_SUCCESS; 1250 ASMAtomicWriteS32(&pStream->i32Error, rc); 1251 return rc; 1252 } 1253 1254 1255 /** 1256 * Copies @a cbSrc bytes from @a pvSrc and into the buffer, flushing as needed 1257 * to make space available. 1258 * 1259 * 1260 * @returns IPRT status code (errors not assigned to i32Error). 1261 * @param pStream The stream. 1262 * @param pvSrc The source buffer. 1263 * @param cbSrc Number of bytes to copy from @a pvSrc. 1264 * @param pcbTotal A total counter to update with what was copied. 1265 */ 1266 static int rtStrmBufCopyTo(PRTSTREAM pStream, const void *pvSrc, size_t cbSrc, size_t *pcbTotal) 1267 { 1268 Assert(cbSrc > 0); 1269 for (;;) 1270 { 1271 size_t cbToCopy = RT_MIN(pStream->cbBufAlloc - pStream->offBufEnd, cbSrc); 1272 if (cbToCopy) 1273 { 1274 memcpy(&pStream->pchBuf[pStream->offBufEnd], pvSrc, cbToCopy); 1275 pStream->offBufEnd += cbToCopy; 1276 pvSrc = (const char *)pvSrc + cbToCopy; 1277 *pcbTotal += cbToCopy; 1278 cbSrc -= cbToCopy; 1279 if (!cbSrc) 1280 break; 1281 } 1282 1283 int rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst); 1284 if (RT_FAILURE(rc)) 1285 return rc; 1286 } 1287 return VINF_SUCCESS; 1288 } 1289 1290 #endif /* RTSTREAM_STANDALONE */ 639 1291 640 1292 … … 656 1308 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 657 1309 658 int rc; 1310 #ifdef RTSTREAM_STANDALONE 1311 rtStrmLock(pStream); 1312 int const rc1 = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/); 1313 int const rc2 = RTFileSeek(rtStrmGetFile(pStream), 0, RTFILE_SEEK_BEGIN, NULL); 1314 int rc = RT_SUCCESS(rc1) ? rc2 : rc1; 1315 ASMAtomicWriteS32(&pStream->i32Error, rc); 1316 rtStrmUnlock(pStream); 1317 #else 659 1318 clearerr(pStream->pFile); 660 1319 errno = 0; 1320 int rc; 661 1321 if (!fseek(pStream->pFile, 0, SEEK_SET)) 662 {663 ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);664 1322 rc = VINF_SUCCESS; 665 }666 1323 else 667 {668 1324 rc = RTErrConvertFromErrno(errno); 669 ASMAtomicWriteS32(&pStream->i32Error, rc); 670 } 671 1325 ASMAtomicWriteS32(&pStream->i32Error, rc); 1326 #endif 672 1327 return rc; 673 1328 } … … 681 1336 static void rtStreamRecheckMode(PRTSTREAM pStream) 682 1337 { 683 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)1338 #if (defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)) && !defined(RTSTREAM_STANDALONE) 684 1339 int fh = fileno(pStream->pFile); 685 1340 if (fh >= 0) … … 707 1362 * @param pvBuf Where to put the read bits. 708 1363 * Must be cbRead bytes or more. 709 * @param cb ReadNumber of bytes to read.1364 * @param cbToRead Number of bytes to read. 710 1365 * @param pcbRead Where to store the number of bytes actually read. 711 1366 * If NULL cbRead bytes are read or an error is returned. 712 1367 */ 713 RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbRead, size_t *pcbRead) 714 { 715 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER); 716 1368 RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead) 1369 { 1370 AssertPtrReturn(pStream, VERR_INVALID_HANDLE); 1371 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 1372 1373 #ifdef RTSTREAM_STANDALONE 1374 rtStrmLock(pStream); 1375 int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream); 1376 #else 717 1377 int rc = pStream->i32Error; 1378 #endif 718 1379 if (RT_SUCCESS(rc)) 719 1380 { … … 721 1382 rtStreamRecheckMode(pStream); 722 1383 1384 #ifdef RTSTREAM_STANDALONE 1385 1386 /* 1387 * Copy data thru the read buffer for now as that'll handle both binary 1388 * and text modes seamlessly. We could optimize larger reads here when 1389 * in binary mode, that can wait till the basics work, I think. 1390 */ 1391 size_t cbTotal = 0; 1392 if (cbToRead > 0) 1393 for (;;) 1394 { 1395 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst; 1396 if (cbInBuffer > 0) 1397 { 1398 size_t cbToCopy = RT_MIN(cbInBuffer, cbToRead); 1399 memcpy(pvBuf, &pStream->pchBuf[pStream->offBufFirst], cbToCopy); 1400 cbTotal += cbToRead; 1401 cbToRead -= cbToCopy; 1402 pvBuf = (char *)pvBuf + cbToCopy; 1403 if (!cbToRead) 1404 break; 1405 } 1406 rc = rtStrmBufFill(pStream); 1407 if (RT_SUCCESS(rc)) 1408 { /* likely */ } 1409 else 1410 { 1411 if (rc == VERR_EOF && pcbRead && cbTotal > 0) 1412 rc = VINF_EOF; 1413 break; 1414 } 1415 } 1416 if (pcbRead) 1417 *pcbRead = cbTotal; 1418 1419 #else /* !RTSTREAM_STANDALONE */ 723 1420 if (pcbRead) 724 1421 { … … 726 1423 * Can do with a partial read. 727 1424 */ 728 *pcbRead = fread(pvBuf, 1, cb Read, pStream->pFile);729 if ( *pcbRead == cb Read1425 *pcbRead = fread(pvBuf, 1, cbToRead, pStream->pFile); 1426 if ( *pcbRead == cbToRead 730 1427 || !ferror(pStream->pFile)) 731 return VINF_SUCCESS; 732 if (feof(pStream->pFile)) 733 { 734 if (*pcbRead) 735 return VINF_EOF; 736 rc = VERR_EOF; 737 } 1428 rc = VINF_SUCCESS; 1429 else if (feof(pStream->pFile)) 1430 rc = *pcbRead ? VINF_EOF : VERR_EOF; 738 1431 else if (ferror(pStream->pFile)) 739 1432 rc = VERR_READ_ERROR; … … 749 1442 * Must read it all! 750 1443 */ 751 if (fread(pvBuf, cbRead, 1, pStream->pFile) == 1) 752 return VINF_SUCCESS; 753 1444 if (fread(pvBuf, cbToRead, 1, pStream->pFile) == 1) 1445 rc = VINF_SUCCESS; 754 1446 /* possible error/eof. */ 755 if (feof(pStream->pFile))1447 else if (feof(pStream->pFile)) 756 1448 rc = VERR_EOF; 757 1449 else if (ferror(pStream->pFile)) … … 763 1455 } 764 1456 } 765 ASMAtomicWriteS32(&pStream->i32Error, rc); 766 } 1457 #endif /* !RTSTREAM_STANDALONE */ 1458 if (RT_FAILURE(rc)) 1459 ASMAtomicWriteS32(&pStream->i32Error, rc); 1460 } 1461 #ifdef RTSTREAM_STANDALONE 1462 rtStrmUnlock(pStream); 1463 #endif 767 1464 return rc; 768 1465 } … … 785 1482 786 1483 787 #ifdef RT_OS_WINDOWS 1484 #if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE) 1485 788 1486 /** 789 1487 * Check if the stream is for a Window console. … … 808 1506 return false; 809 1507 } 1508 1509 1510 static int rtStrmWriteWinConsoleLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, HANDLE hCon) 1511 { 1512 int rc; 1513 # ifdef HAVE_FWRITE_UNLOCKED 1514 if (!fflush_unlocked(pStream->pFile)) 1515 # else 1516 if (!fflush(pStream->pFile)) 1517 # endif 1518 { 1519 /** @todo Consider buffering later. For now, we'd rather correct output than 1520 * fast output. */ 1521 DWORD cwcWritten = 0; 1522 PRTUTF16 pwszSrc = NULL; 1523 size_t cwcSrc = 0; 1524 rc = RTStrToUtf16Ex((const char *)pvBuf, cbToWrite, &pwszSrc, 0, &cwcSrc); 1525 if (RT_SUCCESS(rc)) 1526 { 1527 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL)) 1528 { 1529 /* try write char-by-char to avoid heap problem. */ 1530 cwcWritten = 0; 1531 while (cwcWritten != cwcSrc) 1532 { 1533 DWORD cwcThis; 1534 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL)) 1535 { 1536 if (!pcbWritten || cwcWritten == 0) 1537 rc = RTErrConvertFromErrno(GetLastError()); 1538 break; 1539 } 1540 if (cwcThis != 1) /* Unable to write current char (amount)? */ 1541 break; 1542 cwcWritten++; 1543 } 1544 } 1545 if (RT_SUCCESS(rc)) 1546 { 1547 if (cwcWritten == cwcSrc) 1548 { 1549 if (pcbWritten) 1550 *pcbWritten = cbToWrite; 1551 } 1552 else if (pcbWritten) 1553 { 1554 PCRTUTF16 pwszCur = pwszSrc; 1555 const char *pszCur = (const char *)pvBuf; 1556 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten) 1557 { 1558 RTUNICP CpIgnored; 1559 RTUtf16GetCpEx(&pwszCur, &CpIgnored); 1560 RTStrGetCpEx(&pszCur, &CpIgnored); 1561 } 1562 *pcbWritten = pszCur - (const char *)pvBuf; 1563 } 1564 else 1565 rc = VERR_WRITE_ERROR; 1566 } 1567 RTUtf16Free(pwszSrc); 1568 } 1569 } 1570 else 1571 rc = RTErrConvertFromErrno(errno); 1572 return rc; 1573 } 1574 810 1575 #endif /* RT_OS_WINDOWS */ 1576 1577 static int rtStrmWriteWorkerLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fMustWriteAll) 1578 { 1579 #ifdef RTSTREAM_STANDALONE 1580 /* 1581 * Check preconditions. 1582 */ 1583 Assert(pStream->enmBufDir == RTSTREAMBUFDIR_WRITE); 1584 Assert(pStream->cbBufAlloc >= 256); 1585 Assert(pStream->offBufFirst <= pStream->cbBufAlloc); 1586 Assert(pStream->offBufEnd <= pStream->cbBufAlloc); 1587 Assert(pStream->offBufFirst <= pStream->offBufEnd); 1588 1589 /* 1590 * We write everything via the buffer, letting the buffer flushing take 1591 * care of console output hacks and similar. 1592 */ 1593 RT_NOREF(fMustWriteAll); 1594 int rc = VINF_SUCCESS; 1595 size_t cbTotal = 0; 1596 if (cbToWrite > 0) 1597 { 1598 # ifdef RTSTREAM_WITH_TEXT_MODE 1599 const char *pchLf; 1600 if ( !pStream->fBinary 1601 && (pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite)) != NULL) 1602 for (;;) 1603 { 1604 /* Deal with everything up to the newline. */ 1605 size_t const cbToLf = (size_t)(pchLf - (const char *)pvBuf); 1606 if (cbToLf > 0) 1607 { 1608 rc = rtStrmBufCopyTo(pStream, pvBuf, cbToLf, &cbTotal); 1609 if (RT_FAILURE(rc)) 1610 break; 1611 } 1612 1613 /* Copy the CRLF sequence into the buffer in one go to avoid complications. */ 1614 if (pStream->cbBufAlloc - pStream->offBufEnd < 2) 1615 { 1616 rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst); 1617 if (RT_FAILURE(rc)) 1618 break; 1619 Assert(pStream->cbBufAlloc - pStream->offBufEnd >= 2); 1620 } 1621 pStream->pchBuf[pStream->offBufEnd++] = '\r'; 1622 pStream->pchBuf[pStream->offBufEnd++] = '\n'; 1623 1624 /* Advance past the newline. */ 1625 pvBuf = (const char *)pvBuf + 1 + cbToLf; 1626 cbTotal += 1 + cbToLf; 1627 cbToWrite -= 1 + cbToLf; 1628 if (!cbToWrite) 1629 break; 1630 1631 /* More newlines? */ 1632 pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite); 1633 if (!pchLf) 1634 { 1635 rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal); 1636 break; 1637 } 1638 } 1639 else 1640 # endif 1641 rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal); 1642 1643 /* 1644 * If line buffered or unbuffered, we probably have to do some flushing now. 1645 */ 1646 if (RT_SUCCESS(rc) && pStream->enmBufStyle != RTSTREAMBUFSTYLE_FULL) 1647 { 1648 Assert(pStream->enmBufStyle == RTSTREAMBUFSTYLE_LINE || pStream->enmBufStyle == RTSTREAMBUFSTYLE_UNBUFFERED); 1649 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst; 1650 if (cbInBuffer > 0) 1651 { 1652 if ( pStream->enmBufStyle != RTSTREAMBUFSTYLE_LINE 1653 || pStream->pchBuf[pStream->offBufEnd - 1] == '\n') 1654 rc = rtStrmBufFlushWrite(pStream, cbInBuffer); 1655 else 1656 { 1657 const char *pchToFlush = &pStream->pchBuf[pStream->offBufFirst]; 1658 const char *pchLastLf = (const char *)memrchr(pchToFlush, '\n', cbInBuffer); 1659 if (pchLastLf) 1660 rc = rtStrmBufFlushWrite(pStream, (size_t)(&pchLastLf[1] - pchToFlush)); 1661 } 1662 } 1663 } 1664 } 1665 if (pcbWritten) 1666 *pcbWritten = cbTotal; 1667 return rc; 1668 1669 1670 #else 1671 if (!fMustWriteAll) 1672 { 1673 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */ 1674 # ifdef HAVE_FWRITE_UNLOCKED 1675 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbToWrite, pStream->pFile); 1676 # else 1677 *pcbWritten = fwrite(pvBuf, 1, cbToWrite, pStream->pFile); 1678 # endif 1679 IPRT_ALIGNMENT_CHECKS_ENABLE(); 1680 if ( *pcbWritten == cbToWrite 1681 # ifdef HAVE_FWRITE_UNLOCKED 1682 || !ferror_unlocked(pStream->pFile)) 1683 # else 1684 || !ferror(pStream->pFile)) 1685 # endif 1686 return VINF_SUCCESS; 1687 } 1688 else 1689 { 1690 /* Must write it all! */ 1691 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */ 1692 # ifdef HAVE_FWRITE_UNLOCKED 1693 size_t cbWritten = fwrite_unlocked(pvBuf, cbToWrite, 1, pStream->pFile); 1694 # else 1695 size_t cbWritten = fwrite(pvBuf, cbToWrite, 1, pStream->pFile); 1696 # endif 1697 if (pcbWritten) 1698 *pcbWritten = cbWritten; 1699 IPRT_ALIGNMENT_CHECKS_ENABLE(); 1700 if (cbWritten == 1) 1701 return VINF_SUCCESS; 1702 # ifdef HAVE_FWRITE_UNLOCKED 1703 if (!ferror_unlocked(pStream->pFile)) 1704 # else 1705 if (!ferror(pStream->pFile)) 1706 # endif 1707 return VINF_SUCCESS; /* WEIRD! But anyway... */ 1708 } 1709 return VERR_WRITE_ERROR; 1710 #endif 1711 } 811 1712 812 1713 … … 817 1718 * @param pStream The stream. 818 1719 * @param pvBuf What to write. 819 * @param cb WriteHow much to write.1720 * @param cbToWrite How much to write. 820 1721 * @param pcbWritten Where to optionally return the number of bytes 821 1722 * written. 822 1723 * @param fSureIsText Set if we're sure this is UTF-8 text already. 823 1724 */ 824 static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, 825 bool fSureIsText) 826 { 1725 static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText) 1726 { 1727 #ifdef RTSTREAM_STANDALONE 1728 int rc = rtStrmBufCheckErrorAndSwitchToWriteMode(pStream); 1729 #else 827 1730 int rc = pStream->i32Error; 1731 #endif 828 1732 if (RT_FAILURE(rc)) 829 1733 return rc; … … 831 1735 rtStreamRecheckMode(pStream); 832 1736 833 #if def RT_OS_WINDOWS1737 #if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE) 834 1738 /* 835 1739 * Use the unicode console API when possible in order to avoid stuff … … 838 1742 HANDLE hCon; 839 1743 if (rtStrmIsConsoleUnlocked(pStream, &hCon)) 840 { 841 # ifdef HAVE_FWRITE_UNLOCKED 842 if (!fflush_unlocked(pStream->pFile)) 843 # else 844 if (!fflush(pStream->pFile)) 845 # endif 846 { 847 /** @todo Consider buffering later. For now, we'd rather correct output than 848 * fast output. */ 849 DWORD cwcWritten = 0; 850 PRTUTF16 pwszSrc = NULL; 851 size_t cwcSrc = 0; 852 rc = RTStrToUtf16Ex((const char *)pvBuf, cbWrite, &pwszSrc, 0, &cwcSrc); 853 if (RT_SUCCESS(rc)) 854 { 855 if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL)) 856 { 857 /* try write char-by-char to avoid heap problem. */ 858 cwcWritten = 0; 859 while (cwcWritten != cwcSrc) 860 { 861 DWORD cwcThis; 862 if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL)) 863 { 864 if (!pcbWritten || cwcWritten == 0) 865 rc = RTErrConvertFromErrno(GetLastError()); 866 break; 867 } 868 if (cwcThis != 1) /* Unable to write current char (amount)? */ 869 break; 870 cwcWritten++; 871 } 872 } 873 if (RT_SUCCESS(rc)) 874 { 875 if (cwcWritten == cwcSrc) 876 { 877 if (pcbWritten) 878 *pcbWritten = cbWrite; 879 } 880 else if (pcbWritten) 881 { 882 PCRTUTF16 pwszCur = pwszSrc; 883 const char *pszCur = (const char *)pvBuf; 884 while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten) 885 { 886 RTUNICP CpIgnored; 887 RTUtf16GetCpEx(&pwszCur, &CpIgnored); 888 RTStrGetCpEx(&pszCur, &CpIgnored); 889 } 890 *pcbWritten = pszCur - (const char *)pvBuf; 891 } 892 else 893 rc = VERR_WRITE_ERROR; 894 } 895 RTUtf16Free(pwszSrc); 896 } 897 } 898 else 899 rc = RTErrConvertFromErrno(errno); 900 if (RT_FAILURE(rc)) 901 ASMAtomicWriteS32(&pStream->i32Error, rc); 902 return rc; 903 } 904 #endif /* RT_OS_WINDOWS */ 1744 rc = rtStrmWriteWinConsoleLocked(pStream, pvBuf, cbToWrite, pcbWritten, hCon); 1745 #endif /* RT_OS_WINDOWS && !RTSTREAM_STANDALONE */ 905 1746 906 1747 /* … … 912 1753 */ 913 1754 /** @todo Skip this if the current code set is UTF-8. */ 914 if ( pStream->fCurrentCodeSet915 && !pStream->fBinary916 && ( fSureIsText917 || rtStrmIsUtf8Text(pvBuf, cbWrite))918 )1755 else if ( pStream->fCurrentCodeSet 1756 && !pStream->fBinary 1757 && ( fSureIsText 1758 || rtStrmIsUtf8Text(pvBuf, cbToWrite)) 1759 ) 919 1760 { 920 1761 char *pszSrcFree = NULL; 921 1762 const char *pszSrc = (const char *)pvBuf; 922 if (pszSrc[cb Write - 1])923 { 924 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cb Write);1763 if (pszSrc[cbToWrite - 1]) 1764 { 1765 pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbToWrite); 925 1766 if (pszSrc == NULL) 926 1767 rc = VERR_NO_STR_MEMORY; … … 933 1774 { 934 1775 size_t cchSrcCurCP = strlen(pszSrcCurCP); 935 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */ 936 #ifdef HAVE_FWRITE_UNLOCKED 937 ssize_t cbWritten = fwrite_unlocked(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile); 938 #else 939 ssize_t cbWritten = fwrite(pszSrcCurCP, cchSrcCurCP, 1, pStream->pFile); 940 #endif 941 IPRT_ALIGNMENT_CHECKS_ENABLE(); 942 if (cbWritten == 1) 943 { 944 if (pcbWritten) 945 *pcbWritten = cbWrite; 946 } 947 #ifdef HAVE_FWRITE_UNLOCKED 948 else if (!ferror_unlocked(pStream->pFile)) 949 #else 950 else if (!ferror(pStream->pFile)) 951 #endif 952 { 953 if (pcbWritten) 954 *pcbWritten = 0; 955 } 956 else 957 rc = VERR_WRITE_ERROR; 1776 size_t cbWritten = 0; 1777 rc = rtStrmWriteWorkerLocked(pStream, pszSrcCurCP, cchSrcCurCP, &cbWritten, true /*fMustWriteAll*/); 1778 if (pcbWritten) 1779 *pcbWritten = cbWritten == cchSrcCurCP ? cbToWrite : 0; 958 1780 RTStrFree(pszSrcCurCP); 959 1781 } 960 1782 RTStrFree(pszSrcFree); 961 1783 } 962 963 if (RT_FAILURE(rc)) 964 ASMAtomicWriteS32(&pStream->i32Error, rc); 965 return rc; 966 } 967 1784 } 968 1785 /* 969 1786 * Otherwise, just write it as-is. 970 1787 */ 971 if (pcbWritten)972 {973 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */974 #ifdef HAVE_FWRITE_UNLOCKED975 *pcbWritten = fwrite_unlocked(pvBuf, 1, cbWrite, pStream->pFile);976 #else977 *pcbWritten = fwrite(pvBuf, 1, cbWrite, pStream->pFile);978 #endif979 IPRT_ALIGNMENT_CHECKS_ENABLE();980 if ( *pcbWritten == cbWrite981 #ifdef HAVE_FWRITE_UNLOCKED982 || !ferror_unlocked(pStream->pFile))983 #else984 || !ferror(pStream->pFile))985 #endif986 return VINF_SUCCESS;987 rc = VERR_WRITE_ERROR;988 }989 1788 else 990 { 991 /* Must write it all! */ 992 IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */ 993 #ifdef HAVE_FWRITE_UNLOCKED 994 size_t cbWritten = fwrite_unlocked(pvBuf, cbWrite, 1, pStream->pFile); 995 #else 996 size_t cbWritten = fwrite(pvBuf, cbWrite, 1, pStream->pFile); 997 #endif 998 IPRT_ALIGNMENT_CHECKS_ENABLE(); 999 if (cbWritten == 1) 1000 return VINF_SUCCESS; 1001 #ifdef HAVE_FWRITE_UNLOCKED 1002 if (!ferror_unlocked(pStream->pFile)) 1003 #else 1004 if (!ferror(pStream->pFile)) 1005 #endif 1006 return VINF_SUCCESS; /* WEIRD! But anyway... */ 1007 1008 rc = VERR_WRITE_ERROR; 1009 } 1010 ASMAtomicWriteS32(&pStream->i32Error, rc); 1011 1789 rc = rtStrmWriteWorkerLocked(pStream, pvBuf, cbToWrite, pcbWritten, pcbWritten == NULL); 1790 1791 /* 1792 * Update error status on failure and return. 1793 */ 1794 if (RT_FAILURE(rc)) 1795 ASMAtomicWriteS32(&pStream->i32Error, rc); 1012 1796 return rc; 1013 1797 } … … 1020 1804 * @param pStream The stream. 1021 1805 * @param pvBuf What to write. 1022 * @param cb WriteHow much to write.1806 * @param cbToWrite How much to write. 1023 1807 * @param pcbWritten Where to optionally return the number of bytes 1024 1808 * written. 1025 1809 * @param fSureIsText Set if we're sure this is UTF-8 text already. 1026 1810 */ 1027 static int rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten, bool fSureIsText)1811 DECLINLINE(int) rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText) 1028 1812 { 1029 1813 rtStrmLock(pStream); 1030 int rc = rtStrmWriteLocked(pStream, pvBuf, cb Write, pcbWritten, fSureIsText);1814 int rc = rtStrmWriteLocked(pStream, pvBuf, cbToWrite, pcbWritten, fSureIsText); 1031 1815 rtStrmUnlock(pStream); 1032 1816 return rc; … … 1040 1824 * @param pStream The stream. 1041 1825 * @param pvBuf Where to get the bits to write from. 1042 * @param cb WriteNumber of bytes to write.1826 * @param cbToWrite Number of bytes to write. 1043 1827 * @param pcbWritten Where to store the number of bytes actually written. 1044 * If NULL cbWrite bytes are written or an error is returned. 1045 */ 1046 RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbWrite, size_t *pcbWritten) 1828 * If NULL cbToWrite bytes are written or an error is 1829 * returned. 1830 */ 1831 RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) 1047 1832 { 1048 1833 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER); 1049 return rtStrmWrite(pStream, pvBuf, cb Write, pcbWritten, false);1834 return rtStrmWrite(pStream, pvBuf, cbToWrite, pcbWritten, false); 1050 1835 } 1051 1836 … … 1099 1884 RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString) 1100 1885 { 1101 AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER); 1102 int rc; 1103 if (pszString && cbString > 1) 1104 { 1105 rc = pStream->i32Error; 1106 if (RT_SUCCESS(rc)) 1107 { 1108 cbString--; /* save space for the terminator. */ 1109 rtStrmLock(pStream); 1110 for (;;) 1886 AssertPtrReturn(pStream, VERR_INVALID_HANDLE); 1887 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 1888 AssertReturn(pszString, VERR_INVALID_POINTER); 1889 AssertReturn(cbString >= 2, VERR_INVALID_PARAMETER); 1890 1891 rtStrmLock(pStream); 1892 1893 #ifdef RTSTREAM_STANDALONE 1894 int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream); 1895 #else 1896 int rc = pStream->i32Error; 1897 #endif 1898 if (RT_SUCCESS(rc)) 1899 { 1900 cbString--; /* Reserve space for the terminator. */ 1901 1902 #ifdef RTSTREAM_STANDALONE 1903 char * const pszStringStart = pszString; 1904 #endif 1905 for (;;) 1906 { 1907 #ifdef RTSTREAM_STANDALONE 1908 /* Make sure there is at least one character in the buffer: */ 1909 size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst; 1910 if (cbInBuffer == 0) 1111 1911 { 1112 #ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */ 1113 int ch = fgetc_unlocked(pStream->pFile); 1114 #else 1115 int ch = fgetc(pStream->pFile); 1116 #endif 1117 1118 /* Deal with \r\n sequences here. We'll return lone CR, but 1119 treat CRLF as LF. */ 1120 if (ch == '\r') 1121 { 1122 #ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */ 1123 ch = fgetc_unlocked(pStream->pFile); 1124 #else 1125 ch = fgetc(pStream->pFile); 1126 #endif 1127 if (ch == '\n') 1128 break; 1129 1130 *pszString++ = '\r'; 1131 if (--cbString <= 0) 1132 { 1133 /* yeah, this is an error, we dropped a character. */ 1134 rc = VERR_BUFFER_OVERFLOW; 1135 break; 1136 } 1137 } 1138 1139 /* Deal with end of file. */ 1140 if (ch == EOF) 1141 { 1142 #ifdef HAVE_FWRITE_UNLOCKED 1143 if (feof_unlocked(pStream->pFile)) 1144 #else 1145 if (feof(pStream->pFile)) 1146 #endif 1147 { 1148 rc = VERR_EOF; 1149 break; 1150 } 1151 #ifdef HAVE_FWRITE_UNLOCKED 1152 if (ferror_unlocked(pStream->pFile)) 1153 #else 1154 if (ferror(pStream->pFile)) 1155 #endif 1156 rc = VERR_READ_ERROR; 1157 else 1158 { 1159 AssertMsgFailed(("This shouldn't happen\n")); 1160 rc = VERR_INTERNAL_ERROR; 1161 } 1912 rc = rtStrmBufFill(pStream); 1913 if (RT_SUCCESS(rc)) 1914 cbInBuffer = pStream->offBufEnd - pStream->offBufFirst; 1915 else 1162 1916 break; 1163 } 1164 1165 /* Deal with null terminator and (lone) new line. */ 1166 if (ch == '\0' || ch == '\n') 1917 } 1918 1919 /* Scan the buffer content terminating on a '\n', '\r\n' and '\0' sequence. */ 1920 const char *pchSrc = &pStream->pchBuf[pStream->offBufFirst]; 1921 const char *pchNewline = (const char *)memchr(pchSrc, '\n', cbInBuffer); 1922 const char *pchTerm = (const char *)memchr(pchSrc, '\0', cbInBuffer); 1923 size_t cbCopy; 1924 size_t cbAdvance; 1925 bool fStop = pchNewline || pchTerm; 1926 if (!fStop) 1927 cbAdvance = cbCopy = cbInBuffer; 1928 else if (!pchTerm || (pchNewline && pchTerm && (uintptr_t)pchNewline < (uintptr_t)pchTerm)) 1929 { 1930 cbCopy = (size_t)(pchNewline - pchSrc); 1931 cbAdvance = cbCopy + 1; 1932 if (cbCopy && pchNewline[-1] == '\r') 1933 cbCopy--; 1934 else if (cbCopy == 0 && (uintptr_t)pszString > (uintptr_t)pszStringStart && pszString[-1] == '\r') 1935 pszString--, cbString++; /* drop trailing '\r' that it turns out was followed by '\n' */ 1936 } 1937 else 1938 { 1939 cbCopy = (size_t)(pchTerm - pchSrc); 1940 cbAdvance = cbCopy + 1; 1941 } 1942 1943 /* Adjust for available space in the destination buffer, copy over the string 1944 characters and advance the buffer position (even on overflow). */ 1945 if (cbCopy <= cbString) 1946 pStream->offBufFirst += cbAdvance; 1947 else 1948 { 1949 rc = VERR_BUFFER_OVERFLOW; 1950 fStop = true; 1951 cbCopy = cbString; 1952 pStream->offBufFirst += cbString; 1953 } 1954 1955 memcpy(pszString, pchSrc, cbCopy); 1956 pszString += cbCopy; 1957 cbString -= cbCopy; 1958 1959 if (fStop) 1960 break; 1961 1962 #else /* !RTSTREAM_STANDALONE */ 1963 # ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */ 1964 int ch = fgetc_unlocked(pStream->pFile); 1965 # else 1966 int ch = fgetc(pStream->pFile); 1967 # endif 1968 1969 /* Deal with \r\n sequences here. We'll return lone CR, but 1970 treat CRLF as LF. */ 1971 if (ch == '\r') 1972 { 1973 # ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */ 1974 ch = fgetc_unlocked(pStream->pFile); 1975 # else 1976 ch = fgetc(pStream->pFile); 1977 # endif 1978 if (ch == '\n') 1167 1979 break; 1168 1980 1169 /* No special character, append it to the return string. */ 1170 *pszString++ = ch; 1981 *pszString++ = '\r'; 1171 1982 if (--cbString <= 0) 1172 1983 { 1173 rc = VINF_BUFFER_OVERFLOW; 1984 /* yeah, this is an error, we dropped a character. */ 1985 rc = VERR_BUFFER_OVERFLOW; 1174 1986 break; 1175 1987 } 1176 1988 } 1177 rtStrmUnlock(pStream); 1178 1179 *pszString = '\0'; 1180 if (RT_FAILURE(rc)) 1181 ASMAtomicWriteS32(&pStream->i32Error, rc); 1182 } 1183 } 1184 else 1185 { 1186 AssertMsgFailed(("no buffer or too small buffer!\n")); 1187 rc = VERR_INVALID_PARAMETER; 1188 } 1989 1990 /* Deal with end of file. */ 1991 if (ch == EOF) 1992 { 1993 # ifdef HAVE_FWRITE_UNLOCKED 1994 if (feof_unlocked(pStream->pFile)) 1995 # else 1996 if (feof(pStream->pFile)) 1997 # endif 1998 { 1999 rc = VERR_EOF; 2000 break; 2001 } 2002 # ifdef HAVE_FWRITE_UNLOCKED 2003 if (ferror_unlocked(pStream->pFile)) 2004 # else 2005 if (ferror(pStream->pFile)) 2006 # endif 2007 rc = VERR_READ_ERROR; 2008 else 2009 { 2010 AssertMsgFailed(("This shouldn't happen\n")); 2011 rc = VERR_INTERNAL_ERROR; 2012 } 2013 break; 2014 } 2015 2016 /* Deal with null terminator and (lone) new line. */ 2017 if (ch == '\0' || ch == '\n') 2018 break; 2019 2020 /* No special character, append it to the return string. */ 2021 *pszString++ = ch; 2022 if (--cbString <= 0) 2023 { 2024 rc = VINF_BUFFER_OVERFLOW; 2025 break; 2026 } 2027 #endif /* !RTSTREAM_STANDALONE */ 2028 } 2029 2030 *pszString = '\0'; 2031 if (RT_FAILURE(rc)) 2032 ASMAtomicWriteS32(&pStream->i32Error, rc); 2033 } 2034 2035 rtStrmUnlock(pStream); 1189 2036 return rc; 1190 2037 } … … 1199 2046 RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream) 1200 2047 { 2048 AssertPtrReturn(pStream, VERR_INVALID_HANDLE); 2049 AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE); 2050 2051 #ifdef RTSTREAM_STANDALONE 2052 rtStrmLock(pStream); 2053 int rc = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/); 2054 rtStrmUnlock(pStream); 2055 return rc; 2056 2057 #else 1201 2058 if (!fflush(pStream->pFile)) 1202 2059 return VINF_SUCCESS; 1203 2060 return RTErrConvertFromErrno(errno); 2061 #endif 1204 2062 } 1205 2063
Note:
See TracChangeset
for help on using the changeset viewer.