1 // 2 // write.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Defines _write(), which writes a buffer to a file. 7 // 8 #include <corecrt_internal_lowio.h> 9 #include <corecrt_internal_mbstring.h> 10 #include <corecrt_internal_ptd_propagation.h> 11 #include <ctype.h> 12 #include <locale.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <wchar.h> 16 17 18 19 namespace 20 { 21 struct write_result 22 { 23 DWORD error_code; 24 DWORD char_count; 25 DWORD lf_count; 26 }; 27 } 28 29 30 31 // This is the normal size of the LF => CRLF translation buffer. The default 32 // buffer is 4K, plus extra room for LF characters. Not all buffers are exactly 33 // this size, but this is used as the base size. 34 static size_t const BUF_SIZE = 5 * 1024; 35 36 37 38 // Writes a buffer to a file. The way in which the buffer is written depends on 39 // the mode in which the file was opened (e.g., if the file is a text mode file, 40 // linefeed translation will take place). 41 // 42 // On success, this function returns the number of bytes actually written (note 43 // that "bytes" here is "bytes from the original buffer;" more or fewer bytes 44 // may have actually been written, due to linefeed translation, codepage 45 // translation, and other transformations). On failure, this function returns 0 46 // and sets errno. 47 extern "C" int __cdecl _write_internal(int const fh, void const* const buffer, unsigned const size, __crt_cached_ptd_host& ptd) 48 { 49 _UCRT_CHECK_FH_CLEAR_OSSERR_RETURN(ptd, fh, EBADF, -1); 50 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, (fh >= 0 && (unsigned)fh < (unsigned)_nhandle), EBADF, -1); 51 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, (_osfile(fh) & FOPEN), EBADF, -1); 52 53 __acrt_lowio_lock_fh(fh); 54 int result = -1; 55 __try 56 { 57 if ((_osfile(fh) & FOPEN) == 0) 58 { 59 ptd.get_errno().set(EBADF); 60 ptd.get_doserrno().set(0); 61 _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0)); 62 __leave; 63 } 64 65 result = _write_nolock(fh, buffer, size, ptd); 66 } 67 __finally 68 { 69 __acrt_lowio_unlock_fh(fh); 70 } 71 __endtry 72 return result; 73 } 74 75 extern "C" int __cdecl _write(int const fh, void const* const buffer, unsigned const size) 76 { 77 __crt_cached_ptd_host ptd; 78 return _write_internal(fh, buffer, size, ptd); 79 } 80 81 static bool __cdecl write_requires_double_translation_nolock(int const fh, __crt_cached_ptd_host& ptd) throw() 82 { 83 // Double translation is required if both [a] the current locale is not the C 84 // locale or the file is open in a non-ANSI mode and [b] we are writing to the 85 // console. 86 87 // If this isn't a TTY or a text mode screen, then it isn't the console: 88 if (!_isatty(fh)) 89 { 90 return false; 91 } 92 93 if ((_osfile(fh) & FTEXT) == 0) { 94 return false; 95 } 96 97 // Get the current locale. If we're in the C locale and the file is open 98 // in ANSI mode, we don't need double translation: 99 bool const is_c_locale = ptd.get_locale()->locinfo->locale_name[LC_CTYPE] == nullptr; 100 if (is_c_locale && _textmode(fh) == __crt_lowio_text_mode::ansi) 101 { 102 return false; 103 } 104 105 // If we can't get the console mode, it's not the console: 106 DWORD mode; 107 if (!GetConsoleMode(reinterpret_cast<HANDLE>(_osfhnd(fh)), &mode)) 108 { 109 return false; 110 } 111 112 // Otherwise, double translation is required: 113 return true; 114 } 115 116 117 118 static write_result __cdecl write_double_translated_ansi_nolock( 119 int const fh, 120 _In_reads_(buffer_size) char const* const buffer, 121 unsigned const buffer_size, 122 __crt_cached_ptd_host& ptd 123 ) throw() 124 { 125 HANDLE const os_handle = reinterpret_cast<HANDLE>(_osfhnd(fh)); 126 char const* const buffer_end = buffer + buffer_size; 127 UINT const console_cp = GetConsoleOutputCP(); 128 _locale_t const locale = ptd.get_locale(); 129 bool const is_utf8 = locale->locinfo->_public._locale_lc_codepage == CP_UTF8; 130 131 write_result result = { 0 }; 132 133 for (char const* source_it = buffer; source_it < buffer_end; ) 134 { 135 char const c = *source_it; 136 137 // We require double conversion, to convert from the source multibyte 138 // to Unicode, then from Unicode back to multibyte, but in the console 139 // codepage. 140 // 141 // Here, we have to take into account that _write() might be called 142 // byte-by-byte, so when we see a lead byte without a trail byte, we 143 // have to store it and return no error. When this function is called 144 // again, that byte will be combined with the next available character. 145 wchar_t wc[2] = { 0 }; 146 int wc_used = 1; 147 if (is_utf8) 148 { 149 _ASSERTE(!_dbcsBufferUsed(fh)); 150 const int mb_buf_size = sizeof(_mbBuffer(fh)); 151 int mb_buf_used; 152 for (mb_buf_used = 0; mb_buf_used < mb_buf_size && _mbBuffer(fh)[mb_buf_used]; ++mb_buf_used) 153 {} 154 155 if (mb_buf_used > 0) 156 { 157 const int mb_len = _utf8_no_of_trailbytes(_mbBuffer(fh)[0]) + 1; 158 _ASSERTE(1 < mb_len && mb_buf_used < mb_len); 159 const int remaining_bytes = mb_len - mb_buf_used; 160 if (remaining_bytes <= (buffer_end - source_it)) 161 { 162 // We now have enough bytes to complete the code point 163 char mb_buffer[MB_LEN_MAX]; 164 165 for (int i = 0; i < mb_buf_used; ++i) 166 { 167 mb_buffer[i] = _mbBuffer(fh)[i]; 168 } 169 for (int i = 0; i < remaining_bytes; ++i) 170 { 171 mb_buffer[i + mb_buf_used] = source_it[i]; 172 } 173 174 // Clear out the temp buffer 175 for (int i = 0; i < mb_buf_used; ++i) 176 { 177 _mbBuffer(fh)[i] = 0; 178 } 179 180 mbstate_t state{}; 181 const char* str = mb_buffer; 182 if (mb_len == 4) 183 { 184 wc_used = 2; 185 } 186 if (__crt_mbstring::__mbsrtowcs_utf8(wc, &str, wc_used, &state, ptd) == -1) 187 { 188 return result; 189 } 190 source_it += (remaining_bytes - 1); 191 } 192 else 193 { 194 // Need to add some more bytes to the buffer for later 195 const auto bytes_to_add = buffer_end - source_it; 196 _ASSERTE(mb_buf_used + bytes_to_add < mb_buf_size); 197 for (int i = 0; i < bytes_to_add; ++i) 198 { 199 _mbBuffer(fh)[i + mb_buf_used] = source_it[i]; 200 } 201 // Pretend we wrote the bytes, because this isn't an error *yet*. 202 result.char_count += static_cast<DWORD>(bytes_to_add); 203 return result; 204 } 205 } 206 else 207 { 208 const int mb_len = _utf8_no_of_trailbytes(*source_it) + 1; 209 const auto available_bytes = buffer_end - source_it; 210 if (mb_len <= (available_bytes)) 211 { 212 // We have enough bytes to write the entire code point 213 mbstate_t state{}; 214 const char* str = source_it; 215 if (mb_len == 4) 216 { 217 wc_used = 2; 218 } 219 if (__crt_mbstring::__mbsrtowcs_utf8(wc, &str, wc_used, &state, ptd) == -1) 220 { 221 return result; 222 } 223 source_it += (mb_len - 1); 224 } 225 else 226 { 227 // Not enough bytes for this code point 228 _ASSERTE(available_bytes <= sizeof(_mbBuffer(fh))); 229 for (int i = 0; i < available_bytes; ++i) 230 { 231 _mbBuffer(fh)[i] = source_it[i]; 232 } 233 // Pretend we wrote the bytes, because this isn't an error *yet*. 234 result.char_count += static_cast<DWORD>(available_bytes); 235 return result; 236 } 237 } 238 } 239 else if (_dbcsBufferUsed(fh)) 240 { 241 // We already have a DBCS lead byte buffered. Take the current 242 // character, combine it with the lead byte, and convert: 243 _ASSERTE(_isleadbyte_fast_internal(_dbcsBuffer(fh), locale)); 244 245 char mb_buffer[MB_LEN_MAX]; 246 mb_buffer[0] = _dbcsBuffer(fh); 247 mb_buffer[1] = *source_it; 248 249 _dbcsBufferUsed(fh) = false; 250 251 if (_mbtowc_internal(wc, mb_buffer, 2, ptd) == -1) 252 { 253 return result; 254 } 255 } 256 else 257 { 258 if (_isleadbyte_fast_internal(*source_it, locale)) 259 { 260 if ((source_it + 1) < buffer_end) 261 { 262 // And we have more bytes to read, just convert... 263 if (_mbtowc_internal(wc, source_it, 2, ptd) == -1) 264 { 265 return result; 266 } 267 268 // Increment the source_it to accomodate the DBCS character: 269 ++source_it; 270 } 271 else 272 { 273 // And we ran out of bytes to read, so buffer the lead byte: 274 _dbcsBuffer(fh) = *source_it; 275 _dbcsBufferUsed(fh) = true; 276 277 // We lie here that we actually wrote the last character, to 278 // ensure we don't consider this an error: 279 ++result.char_count; 280 return result; 281 } 282 } 283 else 284 { 285 // single character conversion: 286 if (_mbtowc_internal(wc, source_it, 1, ptd) == -1) 287 { 288 return result; 289 } 290 } 291 } 292 293 ++source_it; 294 295 // Translate the Unicode character into Multibyte in the console codepage 296 // and write the character to the file: 297 char mb_buffer[MB_LEN_MAX]; 298 DWORD const size = static_cast<DWORD>(__acrt_WideCharToMultiByte( 299 console_cp, 0, wc, wc_used, mb_buffer, sizeof(mb_buffer), nullptr, nullptr)); 300 301 if(size == 0) 302 return result; 303 304 DWORD written; 305 if (!WriteFile(os_handle, mb_buffer, size, &written, nullptr)) 306 { 307 result.error_code = GetLastError(); 308 return result; 309 } 310 311 // When we are converting, some conversions may result in: 312 // 313 // 2 MBCS characters => 1 wide character => 1 MBCS character. 314 // 315 // For example, when printing Japanese characters in the English console 316 // codepage, each source character is transformed into a single question 317 // mark. Therefore, we want to track the number of bytes we converted, 318 // plus the linefeed count, instead of how many bytes we actually wrote. 319 result.char_count = result.lf_count + static_cast<DWORD>(source_it - buffer); 320 321 // If the write succeeded but didn't write all of the characters, return: 322 if (written < size) 323 { 324 return result; 325 } 326 327 // If the original character that we read was an LF, write a CR too: 328 // CRT_REFACTOR TODO Doesn't this write LFCR instead of CRLF? 329 if (c == LF) 330 { 331 wchar_t const cr = CR; 332 if (!WriteFile(os_handle, &cr, 1, &written, nullptr)) 333 { 334 result.error_code = GetLastError(); 335 return result; 336 } 337 338 if (written < 1) 339 { 340 return result; 341 } 342 343 ++result.lf_count; 344 ++result.char_count; 345 } 346 } 347 348 return result; 349 } 350 351 352 353 static write_result __cdecl write_double_translated_unicode_nolock( 354 _In_reads_(buffer_size) char const* const buffer, 355 _In_ _Pre_satisfies_((buffer_size % 2) == 0) unsigned const buffer_size 356 ) throw() 357 { 358 // When writing to a Unicode file (UTF-8 or UTF-16LE) that corresponds to 359 // the console, we don't actually need double translation. We just need to 360 // print each character to the console, one-by-one. (This function is 361 // named what it is because its use is guarded by the double translation 362 // check, and to match the name of the corresponding ANSI function.) 363 364 write_result result = { 0 }; 365 366 // Needed for SAL to clarify that buffer_size is even. 367 _Analysis_assume_((buffer_size/2) != ((buffer_size-1)/2)); 368 char const* const buffer_end = buffer + buffer_size; 369 for (char const* pch = buffer; pch < buffer_end; pch += 2) 370 { 371 wchar_t const c = *reinterpret_cast<wchar_t const*>(pch); 372 373 // _putwch_nolock does not depend on global state, no PTD needed to be propagated. 374 if (_putwch_nolock(c) == c) 375 { 376 result.char_count += 2; 377 } 378 else 379 { 380 result.error_code = GetLastError(); 381 return result; 382 } 383 384 // If the character was a carriage return, also emit a line feed. 385 // CRT_REFACTOR TODO Doesn't this print LFCR instead of CRLF? 386 if (c == LF) 387 { 388 // _putwch_nolock does not depend on global state, no PTD needed to be propagated. 389 if (_putwch_nolock(CR) != CR) 390 { 391 result.error_code = GetLastError(); 392 return result; 393 } 394 395 ++result.char_count; 396 ++result.lf_count; 397 } 398 } 399 400 return result; 401 } 402 403 404 405 static write_result __cdecl write_text_ansi_nolock( 406 int const fh, 407 _In_reads_(buffer_size) char const* const buffer, 408 unsigned const buffer_size 409 ) throw() 410 { 411 HANDLE const os_handle = reinterpret_cast<HANDLE>(_osfhnd(fh)); 412 char const* const buffer_end = buffer + buffer_size; 413 414 write_result result = { 0 }; 415 416 for (char const* source_it = buffer; source_it < buffer_end; ) 417 { 418 char lfbuf[BUF_SIZE]; // The LF => CRLF translation buffer 419 420 // One-past-the-end of the translation buffer. Note that we subtract 421 // one to account for the case where we're pointing to the last element 422 // in the buffer and we need to write both a CR and an LF. 423 char* const lfbuf_end = lfbuf + sizeof(lfbuf) - 1; 424 425 // Translate the source buffer into the translation buffer. Note that 426 // both source_it and lfbuf_it are incremented in the loop. 427 char* lfbuf_it = lfbuf; 428 while (lfbuf_it < lfbuf_end && source_it < buffer_end) 429 { 430 char const c = *source_it++; 431 432 if (c == LF) 433 { 434 ++result.lf_count; 435 *lfbuf_it++ = CR; 436 } 437 438 *lfbuf_it++ = c; 439 } 440 441 DWORD const lfbuf_length = static_cast<DWORD>(lfbuf_it - lfbuf); 442 443 DWORD written; 444 if (!WriteFile(os_handle, lfbuf, lfbuf_length, &written, nullptr)) 445 { 446 result.error_code = GetLastError(); 447 return result; 448 } 449 450 result.char_count += written; 451 if (written < lfbuf_length) 452 { 453 return result; // The write succeeded but didn't write everything 454 } 455 } 456 457 return result; 458 } 459 460 461 462 static write_result __cdecl write_text_utf16le_nolock( 463 int const fh, 464 _In_reads_(buffer_size) char const* const buffer, 465 unsigned const buffer_size 466 ) throw() 467 { 468 HANDLE const os_handle = reinterpret_cast<HANDLE>(_osfhnd(fh)); 469 wchar_t const* const buffer_end = reinterpret_cast<wchar_t const*>(buffer + buffer_size); 470 471 write_result result = { 0 }; 472 473 wchar_t const* source_it = reinterpret_cast<wchar_t const*>(buffer); 474 while (source_it < buffer_end) 475 { 476 wchar_t lfbuf[BUF_SIZE / sizeof(wchar_t)]; // The translation buffer 477 478 // One-past-the-end of the translation buffer. Note that we subtract 479 // one to account for the case where we're pointing to the last element 480 // in the buffer and we need to write both a CR and an LF. 481 wchar_t const* lfbuf_end = lfbuf + BUF_SIZE / sizeof(wchar_t) - 1; 482 483 // Translate the source buffer into the translation buffer. Note that 484 // both source_it and lfbuf_it are incremented in the loop. 485 wchar_t* lfbuf_it = lfbuf; 486 while (lfbuf_it < lfbuf_end && source_it < buffer_end) 487 { 488 wchar_t const c = *source_it++; 489 490 if (c == LF) 491 { 492 result.lf_count += 2; 493 *lfbuf_it++ = CR; 494 } 495 496 *lfbuf_it++ = c; 497 } 498 499 // Note that this length is in bytes, not wchar_t elemnts, since we need 500 // to tell WriteFile how many bytes (not characters) to write: 501 DWORD const lfbuf_length = static_cast<DWORD>(lfbuf_it - lfbuf) * sizeof(wchar_t); 502 503 504 // Attempt the write and return immediately if it fails: 505 DWORD written; 506 if (!WriteFile(os_handle, lfbuf, lfbuf_length, &written, nullptr)) 507 { 508 result.error_code = GetLastError(); 509 return result; 510 } 511 512 result.char_count += written; 513 if (written < lfbuf_length) 514 { 515 return result; // The write succeeded, but didn't write everything 516 } 517 } 518 519 return result; 520 } 521 522 523 524 static write_result __cdecl write_text_utf8_nolock( 525 int const fh, 526 _In_reads_(buffer_size) char const* const buffer, 527 unsigned const buffer_size 528 ) throw() 529 { 530 HANDLE const os_handle = reinterpret_cast<HANDLE>(_osfhnd(fh)); 531 wchar_t const* const buffer_end = reinterpret_cast<wchar_t const*>(buffer + buffer_size); 532 533 write_result result = { 0 }; 534 535 wchar_t const* source_it = reinterpret_cast<wchar_t const*>(buffer); 536 while (source_it < buffer_end) 537 { 538 // The translation buffer. We use two buffers: the first is used to 539 // store the UTF-16 LF => CRLF translation (this is that buffer here). 540 // The second is used for storing the conversion to UTF-8 (defined 541 // below). The sizes are selected to handle the worst-case scenario 542 // where each UTF-8 character is four bytes long. 543 wchar_t utf16_buf[BUF_SIZE / 6]; 544 545 // One-past-the-end of the translation buffer. Note that we subtract 546 // one to account for the case where we're pointing to the last element 547 // in the buffer and we need to write both a CR and an LF. 548 wchar_t const* utf16_buf_end = utf16_buf + (BUF_SIZE / 6 - 1); 549 550 // Translate the source buffer into the translation buffer. Note that 551 // both source_it and lfbuf_it are incremented in the loop. 552 wchar_t* utf16_buf_it = utf16_buf; 553 while (utf16_buf_it < utf16_buf_end && source_it < buffer_end) 554 { 555 wchar_t const c = *source_it++; 556 557 if (c == LF) 558 { 559 // No need to count the number of line-feeds translated; we 560 // track the number of written characters by counting the total 561 // number of characters written from the UTF8 buffer (see below 562 // where we update the char_count). 563 *utf16_buf_it++ = CR; 564 } 565 566 *utf16_buf_it++ = c; 567 } 568 569 // Note that this length is in characters, not bytes. 570 DWORD const utf16_buf_length = static_cast<DWORD>(utf16_buf_it - utf16_buf); 571 572 573 // This is the second translation, where we translate the UTF-16 text to 574 // UTF-8, into the UTF-8 buffer: 575 char utf8_buf[(BUF_SIZE * 2) / 3]; 576 DWORD const bytes_converted = static_cast<DWORD>(__acrt_WideCharToMultiByte( 577 CP_UTF8, 578 0, 579 utf16_buf, 580 utf16_buf_length, 581 utf8_buf, 582 sizeof(utf8_buf), 583 nullptr, 584 nullptr)); 585 586 if (bytes_converted == 0) 587 { 588 result.error_code = GetLastError(); 589 return result; 590 } 591 592 // Here, we need to make every attempt to write all of the converted 593 // characters to avoid corrupting the stream. If, for example, we write 594 // only half of the bytes of a UTF-8 character, the stream may be 595 // corrupted. 596 // 597 // This loop will ensure that we exit only if either (a) all of the 598 // bytes are written, ensuring that no partial MBCSes are written, or 599 // (b) there is an error in the stream. 600 for (DWORD bytes_written = 0; bytes_written < bytes_converted; ) 601 { 602 char const* const current = utf8_buf + bytes_written; 603 DWORD const current_size = bytes_converted - bytes_written; 604 605 DWORD written; 606 if (!WriteFile(os_handle, current, current_size, &written, nullptr)) 607 { 608 result.error_code = GetLastError(); 609 return result; 610 } 611 612 bytes_written += written; 613 } 614 615 // If this chunk was committed successfully, update the character count: 616 result.char_count = static_cast<DWORD>(reinterpret_cast<char const*>(source_it) - buffer); 617 } 618 619 return result; 620 } 621 622 623 624 static write_result __cdecl write_binary_nolock( 625 int const fh, 626 _In_reads_(buffer_size) char const* const buffer, 627 unsigned const buffer_size 628 ) throw() 629 { 630 HANDLE const os_handle = reinterpret_cast<HANDLE>(_osfhnd(fh)); 631 632 // Compared to text files, binary files are easy... 633 write_result result = { 0 }; 634 if (!WriteFile(os_handle, buffer, buffer_size, &result.char_count, nullptr)) 635 { 636 result.error_code = GetLastError(); 637 } 638 639 return result; 640 } 641 642 643 644 extern "C" int __cdecl _write_nolock(int const fh, void const* const buffer, unsigned const buffer_size, __crt_cached_ptd_host& ptd) 645 { 646 // If the buffer is empty, there is nothing to be written: 647 if (buffer_size == 0) 648 { 649 return 0; 650 } 651 652 // If the buffer is null, though... well, that is not allowed: 653 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, buffer != nullptr, EINVAL, -1); 654 655 __crt_lowio_text_mode const fh_textmode = _textmode(fh); 656 657 // If the file is open for Unicode, the buffer size must always be even: 658 if (fh_textmode == __crt_lowio_text_mode::utf16le || fh_textmode == __crt_lowio_text_mode::utf8) 659 { 660 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, buffer_size % 2 == 0, EINVAL, -1); 661 } 662 663 // If the file is opened for appending, seek to the end of the file. We 664 // ignore errors because the underlying file may not allow seeking. 665 if (_osfile(fh) & FAPPEND) 666 { 667 (void)_lseeki64_nolock_internal(fh, 0, FILE_END, ptd); 668 } 669 670 char const* const char_buffer = static_cast<char const*>(buffer); 671 672 // Dispatch the actual writing to one of the helper routines based on the 673 // text mode of the file and whether or not the file refers to the console. 674 // 675 // Note that in the event that the handle belongs to the console, WriteFile 676 // will generate garbage output. To print to the console correctly, we need 677 // to print ANSI. Also note that when printing to the console, we need to 678 // convert the characters to the console codepge. 679 write_result result = { 0 }; 680 if (write_requires_double_translation_nolock(fh, ptd)) 681 { 682 switch (fh_textmode) 683 { 684 case __crt_lowio_text_mode::ansi: 685 result = write_double_translated_ansi_nolock(fh, char_buffer, buffer_size, ptd); 686 break; 687 688 case __crt_lowio_text_mode::utf16le: 689 case __crt_lowio_text_mode::utf8: 690 _Analysis_assume_((buffer_size % 2) == 0); 691 result = write_double_translated_unicode_nolock(char_buffer, buffer_size); 692 break; 693 } 694 } 695 else if (_osfile(fh) & FTEXT) 696 { 697 switch (fh_textmode) 698 { 699 case __crt_lowio_text_mode::ansi: 700 result = write_text_ansi_nolock(fh, char_buffer, buffer_size); 701 break; 702 703 case __crt_lowio_text_mode::utf16le: 704 result = write_text_utf16le_nolock(fh, char_buffer, buffer_size); 705 break; 706 707 case __crt_lowio_text_mode::utf8: 708 result = write_text_utf8_nolock(fh, char_buffer, buffer_size); 709 break; 710 } 711 } 712 else 713 { 714 result = write_binary_nolock(fh, char_buffer, buffer_size); 715 } 716 717 718 // Why did we not write anything? Lettuce find out... 719 if (result.char_count == 0) 720 { 721 // If nothing was written, check to see if it was due to an OS error: 722 if (result.error_code != 0) 723 { 724 // An OS error occurred. ERROR_ACCESS_DENIED should be mapped in 725 // this case to EBADF, not EACCES. All other errors are mapped 726 // normally: 727 if (result.error_code == ERROR_ACCESS_DENIED) 728 { 729 ptd.get_errno().set(EBADF); 730 ptd.get_doserrno().set(result.error_code); 731 } 732 else 733 { 734 __acrt_errno_map_os_error_ptd(result.error_code, ptd); 735 } 736 737 return -1; 738 } 739 740 // If this file is a device and the first character was Ctrl+Z, then 741 // writing nothing is the expected behavior and is not an error: 742 if ((_osfile(fh) & FDEV) && *char_buffer == CTRLZ) 743 { 744 return 0; 745 } 746 747 // Otherwise, the error is reported as ENOSPC: 748 ptd.get_errno().set(ENOSPC); 749 ptd.get_doserrno().set(0); 750 return -1; 751 } 752 753 // The write succeeded. Return the adjusted number of bytes written: 754 return result.char_count - result.lf_count; 755 } 756