static int __cdecl dup2_nolock(int const source_fh, int const target_fh) throw() { if ((_osfile(source_fh) & FOPEN) == 0) { // If the source handle is not open, return an error. Noe that the // DuplicateHandle API will not detect this error, because it implies // that _osfhnd(source_fh) == INVALID_HANDLE_VALUE, and this is a // legal HANDLE value to be duplicated. errno = EBADF; _doserrno = 0; _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0)); return -1; } // If the target is open, close it first. We ignore the possibility of an // error here: an error simply means that the OS handle value may remain // bound for the duration of the process. if (_osfile(target_fh) & FOPEN) { _close_nolock(target_fh); } // Duplicate the source file onto the target file: intptr_t new_osfhandle; BOOL const result = DuplicateHandle( GetCurrentProcess(), reinterpret_cast<HANDLE>(_get_osfhandle(source_fh)), GetCurrentProcess(), &reinterpret_cast<HANDLE&>(new_osfhandle), 0, TRUE, DUPLICATE_SAME_ACCESS); if (!result) { __acrt_errno_map_os_error(GetLastError()); return -1; } __acrt_lowio_set_os_handle(target_fh, new_osfhandle); // Copy the _osfile information, with the FNOINHERIT bit cleared: _osfile(target_fh) = _osfile(source_fh) & ~FNOINHERIT; _textmode(target_fh) = _textmode(source_fh); _tm_unicode(target_fh) = _tm_unicode(source_fh); return 0; }
int __cdecl _setmode_nolock( REG1 int fh, int mode ) { int oldmode; int oldtextmode; oldmode = _osfile(fh) & FTEXT; oldtextmode = _textmode(fh); switch (mode) { case _O_BINARY : _osfile(fh) &= ~FTEXT; break; case _O_TEXT : _osfile(fh) |= FTEXT; _textmode(fh) = __IOINFO_TM_ANSI; break; case _O_U8TEXT : _osfile(fh) |= FTEXT; _textmode(fh) = __IOINFO_TM_UTF8; break; case _O_U16TEXT: case _O_WTEXT : _osfile(fh) |= FTEXT; _textmode(fh) = __IOINFO_TM_UTF16LE; break; } if (oldmode == 0) { return _O_BINARY; } if (oldtextmode == __IOINFO_TM_ANSI) { return _O_TEXT; } else { return _O_WTEXT; } }
// If a binary mode stream is open for reading and the target of the requested // seek is within the stream buffer, we can simply adjust the buffer pointer to // the new location. This allows us to avoid flushing the buffer. If the user // has set a large buffer on the stream and performs frequent seeks that are // likely to result in targets within the buffer, this is a huge performance win. // This function handles the most common cases. static bool __cdecl common_fseek_binary_mode_read_only_fast_track_nolock( __crt_stdio_stream const stream, __int64 offset, int whence ) throw() { // This fast-track path does not handle the seek-from-end case (this is not // nearly as commonly used as seeking from the beginning or from the current // position). if (whence == SEEK_END) { return false; } // This fast-track path is only useful if the stream is buffered. if (!stream.has_any_buffer()) { return false; } // This fast-track path requires a stream opened only for reading. It may // be possible to handle streams opened for writing or update similarly; // further investigation would be required. if (stream.has_any_of(_IOWRITE | _IOUPDATE)) { return false; } // The ftell function handles a case where _cnt is negative. It isn't clear // why _cnt may be negative, so if _cnt is negative, fall back to the // expensive path. if (stream->_cnt < 0) { return false; } // This fast-track path requires a binary mode file handle. When text mode // or UTF transformations are enabled, the contents of the buffer do not // exactly match the contents of the underlying file. int const fh = stream.lowio_handle(); if ((_osfile(fh) & FTEXT) != 0 || _textmode(fh) != __crt_lowio_text_mode::ansi) { return false; } // Handle the SEEK_SET case by transforming the SEEK_SET offset into a // SEEK_CUR offset: if (whence == SEEK_SET) { __int64 const lowio_position = _lseeki64_nolock(fh, 0, SEEK_CUR); if (lowio_position < 0) { return false; } __int64 const stdio_position = lowio_position - stream->_cnt; if (FAILED(LongLongSub(offset, stdio_position, &offset))) { return false; } whence = SEEK_CUR; } // Compute the maximum number of bytes that we can seek in each direction // within the buffer and verify that the requested offset is within that // range. Note that the minimum reverse seek is a negative value. __int64 const minimum_reverse_seek = -(stream->_ptr - stream->_base); __int64 const maximum_forward_seek = stream->_cnt; bool const seek_is_within_buffer = minimum_reverse_seek <= offset && offset <= maximum_forward_seek; if (!seek_is_within_buffer) { return false; } stream->_ptr += offset; // Note that the cast here is safe: The buffer will never be larger than // INT_MAX bytes in size. The setvbuf function validates this constraint. stream->_cnt -= static_cast<int>(offset); return true; }
static int __cdecl _dup_nolock( int fh ) { int newfh; /* variable for new file handle */ ULONG dosretval; /* o.s. return value */ char fileinfo; /* _osfile info for file */ intptr_t new_osfhandle; int success = FALSE; fileinfo = _osfile(fh); /* get file info for file */ if ( !(_osfile(fh) & FOPEN) ) return -1; /* create duplicate handle */ if ( (newfh = _alloc_osfhnd()) == -1 ) { errno = EMFILE; /* too many files error */ _doserrno = 0L; /* not an OS error */ return -1; /* return error to caller */ } __TRY /* * duplicate the file handle */ if ( !(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fh), GetCurrentProcess(), (PHANDLE)&new_osfhandle, 0L, TRUE, DUPLICATE_SAME_ACCESS)) ) { dosretval = GetLastError(); } else { _set_osfhnd(newfh, new_osfhandle); dosretval = 0; } if (dosretval) { /* o.s. error -- map errpr and release handle */ _dosmaperr(dosretval); } else { /* * copy the _osfile value, with the FNOINHERIT bit cleared */ _osfile(newfh) = fileinfo & ~FNOINHERIT; _textmode(newfh) = _textmode(fh); _tm_unicode(newfh) = _tm_unicode(fh); success = TRUE; } __FINALLY if (!success) { _osfile(newfh) &= ~FOPEN; } _unlock_fh(newfh); __END_TRY_FINALLY return success ? newfh : -1; }
static int __cdecl _dup2_nolock ( int fh1, int fh2 ) { ULONG dosretval; /* o.s. return code */ intptr_t new_osfhandle; /* * Re-test and take care of case of unopened source handle. This is * necessary only in the multi-thread case where the file have been * closed by another thread before the lock was asserted, but after * the initial test above. */ if ( !(_osfile(fh1) & FOPEN) ) { /* * Source handle isn't open, bail out with an error. * Note that the DuplicateHandle API will not detect this * error since it implies that _osfhnd(fh1) == * INVALID_HANDLE_VALUE, and this is a legal HANDLE value * (it's the HANDLE for the current process). */ errno = EBADF; _doserrno = 0; /* not an OS error */ _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0)); return -1; } /* * Take of the case of equal handles. */ if ( fh1 == fh2 ) /* * Since fh1 is known to be open, return 0 indicating success. * This is in conformance with the POSIX specification for * dup2. */ return 0; /* * if fh2 is open, close it. */ if ( _osfile(fh2) & FOPEN ) /* * close the handle. ignore the possibility of an error - an * error simply means that an OS handle value may remain bound * for the duration of the process. Use _close_nolock as we * already own lock */ (void) _close_nolock(fh2); /* Duplicate source file onto target file */ if ( !(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fh1), GetCurrentProcess(), (PHANDLE)&new_osfhandle, 0L, TRUE, DUPLICATE_SAME_ACCESS)) ) { dosretval = GetLastError(); } else { _set_osfhnd(fh2, new_osfhandle); dosretval = 0; } if (dosretval) { _dosmaperr(dosretval); return -1; } /* copy the _osfile information, with the FNOINHERIT bit cleared */ _osfile(fh2) = _osfile(fh1) & ~FNOINHERIT; _textmode(fh2) = _textmode(fh1); _tm_unicode(fh2) = _tm_unicode(fh1); return 0; }
/* now define version that doesn't lock/unlock, validate fh */ int __cdecl _read_nolock ( int fh, void *inputbuf, unsigned cnt ) { int bytes_read; /* number of bytes read */ char *buffer; /* buffer to read to */ int os_read; /* bytes read on OS call */ char *p, *q; /* pointers into buffer */ wchar_t *pu, *qu; /* wchar_t pointers into buffer for UTF16 */ char peekchr; /* peek-ahead character */ wchar_t wpeekchr; /* peek-ahead wchar_t */ __int64 filepos; /* file position after seek */ ULONG dosretval; /* o.s. return value */ char tmode; /* textmode - ANSI/UTF-8/UTF-16 */ void *buf; /* buffer to read to */ int retval = -2; /* return value */ unsigned inputsize = cnt; /* validate fh */ _CHECK_FH_CLEAR_OSSERR_RETURN( fh, EBADF, -1 ); _VALIDATE_CLEAR_OSSERR_RETURN((fh >= 0 && (unsigned)fh < (unsigned)_nhandle), EBADF, -1); _VALIDATE_CLEAR_OSSERR_RETURN((_osfile(fh) & FOPEN), EBADF, -1); bytes_read = 0; /* nothing read yet */ if (cnt == 0 || (_osfile(fh) & FEOFLAG)) { /* nothing to read or at EOF, so return 0 read */ return 0; } _VALIDATE_CLEAR_OSSERR_RETURN( (inputbuf != NULL), EINVAL, -1 ); tmode = _textmode(fh); switch(tmode) { case __IOINFO_TM_UTF8 : /* For a UTF-8 file, we need 2 buffers, because after reading we need to convert it into UNICODE - MultiByteToWideChar doesn't do in-place conversions. */ /* MultiByte To WideChar conversion may double the size of the buffer required & hence we divide cnt by 2 */ /* * Since we are reading UTF8 stream, cnt bytes read may vary * from cnt wchar_t characters to cnt/4 wchar_t characters. For * this reason if we need to read cnt characters, we will * allocate MBCS buffer of cnt. In case cnt is 0, we will * have 4 as minimum value. This will make sure we don't * overflow for reading from pipe case. * * * In this case the numbers of wchar_t characters that we can * read is cnt/2. This means that the buffer size that we will * require is cnt/2. */ /* For UTF8 we want the count to be an even number */ _VALIDATE_CLEAR_OSSERR_RETURN(((cnt & 1) == 0), EINVAL, -1); cnt = (cnt/2) < 4 ? 4 : (cnt/2); buf = _malloc_crt(cnt); if(!buf) { errno = ENOMEM; _doserrno = E_nomem; return -1; } break; case __IOINFO_TM_UTF16LE : /* For UTF16 the count always needs to be an even number */ _VALIDATE_CLEAR_OSSERR_RETURN(((cnt & 1) == 0), EINVAL, -1); cnt &= (~1); /* Fall Through to default */ default : /* For non-UTF8 files, we need only 1 buffer - make buf point to the users input buffer */ buf = inputbuf; } buffer = buf; if ((_osfile(fh) & (FPIPE|FDEV)) && _pipech(fh) != LF && cnt != 0) { /* a pipe/device and pipe lookahead non-empty: read the lookahead * char */ *buffer++ = _pipech(fh); ++bytes_read; --cnt; _pipech(fh) = LF; /* mark as empty */ /* For UTF16, there maybe one more look ahead char. For UTF8, there maybe 2 more look ahead chars */ if((tmode != __IOINFO_TM_ANSI) && (_pipech2(fh)[0] != LF) && cnt != 0) { *buffer++ = _pipech2(fh)[0]; ++bytes_read; --cnt; _pipech2(fh)[0] = LF; /* mark as empty */ if((tmode == __IOINFO_TM_UTF8) && (_pipech2(fh)[1] != LF) && cnt != 0) { *buffer++ = _pipech2(fh)[1]; ++bytes_read; --cnt; _pipech2(fh)[1] = LF; /* mark as empty */ } } } /* read the data */ if ( !ReadFile( (HANDLE)_osfhnd(fh), buffer, cnt, (LPDWORD)&os_read, NULL ) || os_read < 0 || (size_t)os_read > cnt) { /* ReadFile has reported an error. recognize two special cases. * * 1. map ERROR_ACCESS_DENIED to EBADF * * 2. just return 0 if ERROR_BROKEN_PIPE has occurred. it * means the handle is a read-handle on a pipe for which * all write-handles have been closed and all data has been * read. */ if ( (dosretval = GetLastError()) == ERROR_ACCESS_DENIED ) { /* wrong read/write mode should return EBADF, not EACCES */ errno = EBADF; _doserrno = dosretval; retval = -1; goto error_return; } else if ( dosretval == ERROR_BROKEN_PIPE ) { retval = 0; goto error_return; } else { _dosmaperr(dosretval); retval = -1; goto error_return; } } bytes_read += os_read; /* update bytes read */ if (_osfile(fh) & FTEXT) { /* now must translate CR-LFs to LFs in the buffer */ /* For ANSI & UTF8, we read byte by byte. For UTF16, we need to read 2 bytes (wchar_t's) at a time */ if(tmode != __IOINFO_TM_UTF16LE) { /* set CRLF flag to indicate LF at beginning of buffer */ if ( (os_read != 0) && (*(char *)buf == LF) ) _osfile(fh) |= FCRLF; else _osfile(fh) &= ~FCRLF; /* convert chars in the buffer: p is src, q is dest */ p = q = buf; while (p < (char *)buf + bytes_read) { if (*p == CTRLZ) { /* if fh is not a device, set ctrl-z flag */ if ( !(_osfile(fh) & FDEV) ) _osfile(fh) |= FEOFLAG; else *q++ = *p++; break; /* stop translating */ } else if (*p != CR) *q++ = *p++; else { /* *p is CR, so must check next char for LF */ if (p < (char *)buf + bytes_read - 1) { if (*(p+1) == LF) { p += 2; *q++ = LF; /* convert CR-LF to LF */ } else *q++ = *p++; /* store char normally */ } else { /* This is the hard part. We found a CR at end of buffer. We must peek ahead to see if next char is an LF. */ ++p; dosretval = 0; if ( !ReadFile( (HANDLE)_osfhnd(fh), &peekchr, 1, (LPDWORD)&os_read, NULL ) ) dosretval = GetLastError(); if (dosretval != 0 || os_read == 0) { /* couldn't read ahead, store CR */ *q++ = CR; } else { /* * peekchr now has the extra character -- we now * have several possibilities: * * 1. disk file and char is not LF; just seek * back and copy CR * 2. disk file and char is LF; seek back and * discard CR * 3. disk file, char is LF but this is a * one-byte read: store LF, don't seek back * 4. pipe/device and char is LF; store LF. * 5. pipe/device and char isn't LF, store CR * and put char in pipe lookahead buffer. */ if (_osfile(fh) & (FDEV|FPIPE)) { /* non-seekable device */ if (peekchr == LF) *q++ = LF; else { *q++ = CR; _pipech(fh) = peekchr; } } else { /* disk file */ if (q == buf && peekchr == LF) { /* nothing read yet; must make some progress */ *q++ = LF; } else { /* seek back */ filepos = _lseeki64_nolock(fh, -1i64, FILE_CURRENT); if (peekchr != LF) *q++ = CR; } } } } } } /* we now change bytes_read to reflect the true number of chars in the buffer */ bytes_read = (int)(q - (char *)buf); if((tmode == __IOINFO_TM_UTF8) && (bytes_read != 0)) { /* UTF8 reads need to be converted into UTF16 */ --q; /* q has gone beyond the last char */ /* * If the last byte is a standalone UTF-8 char. We then * take the whole buffer. Otherwise we skip back till we * come to a lead byte. If the leadbyte forms a complete * UTF-8 character will the remaining part of the buffer, * then again we take the whole buffer. If not, we skip to * one byte which should be the final trail byte of the * previous UTF-8 char or a standalone UTF-8 character */ if(_utf8_is_independent(*q)) { ++q; /* * Final byte is standalone, we reset q, because we * will now consider the full buffer which we have read */ } else { int ctr = 1; int cnt_trailbytes; while(!_utf8_is_leadbyte(*q) && ctr <= 4 && q >= (char *)buf) { --q; ++ctr; } cnt_trailbytes = _utf8_no_of_trailbytes(*q); if(cnt_trailbytes == 0) { /* * Should have exited the while by finding a lead * byte else, the file has incorrect UTF-8 chars */ errno = EILSEQ; retval = -1; goto error_return; } if(cnt_trailbytes + 1 == ctr) { /* * The leadbyte + the remaining bytes form a full * set */ q += ctr; } else { /* Seek back */ if (_osfile(fh) & (FDEV|FPIPE)) { /* * non-seekable device. Put the extra chars in * _pipech & _pipech2. We would have a maximum * of 3 extra chars */ _pipech(fh) = *q; ++q; if(ctr >= 2) { _pipech2(fh)[0] = *q; ++q; } if(ctr == 3) { _pipech2(fh)[1] = *q; ++q; } /* * We need to point q back to beyond whatever * we actually took in. */ q -= ctr; } else { /* We have read extra chars, so we seek back */ filepos = _lseeki64_nolock(fh, -ctr, FILE_CURRENT); } } } bytes_read = (int)(q - (char *)buf); bytes_read = MultiByteToWideChar( CP_UTF8, 0, buf, bytes_read, inputbuf, inputsize/2); if(!bytes_read) { _dosmaperr(GetLastError()); retval = -1; goto error_return; } /* MultiByteToWideChar returns no of wchar_t's. Double it */ bytes_read = bytes_read*2; } } else { /* set CRLF flag to indicate LF at beginning of buffer */ if ( (os_read != 0) && (*(wchar_t *)buf == LF) ) _osfile(fh) |= FCRLF; else _osfile(fh) &= ~FCRLF; /* convert chars in the buffer: pu is src, qu is dest */ pu = qu = (wchar_t *)buf; while ((char *)pu < (char *)buf + bytes_read) { if (*pu == CTRLZ) { /* if fh is not a device, set ctrl-z flag */ if ( !(_osfile(fh) & FDEV) ) _osfile(fh) |= FEOFLAG; else *qu++ = *pu++; break; /* stop translating */ } else if (*pu != CR) *qu++ = *pu++; else { /* *pu is CR, so must check next wchar_t for LF */ if ((char *)pu < (char *)buf + bytes_read - 2) { if (*(pu+1) == LF) { pu += 2; *qu++ = LF; /* convert CR-LF to LF */ } else *qu++ = *pu++; /* store char normally */ } else { /* This is the hard part. We found a CR at end of buffer. We must peek ahead to see if next wchar_t is an LF. */ ++pu; dosretval = 0; if ( !ReadFile( (HANDLE)_osfhnd(fh), &wpeekchr, 2, (LPDWORD)&os_read, NULL ) ) dosretval = GetLastError(); if (dosretval != 0 || os_read == 0) { /* couldn't read ahead, store CR */ *qu++ = CR; } else { /* * peekchr now has the extra character -- we * now have several possibilities: * 1. wchar_t is not LF; just seek back and * copy CR * 2. wchar_t is LF; seek back and discard CR * 3. disk file, wchar_t is LF but this is a * one-byte read: store LF, don't seek back. */ if (_osfile(fh) & (FDEV|FPIPE)) { /* non-seekable device */ if (wpeekchr == LF) *qu++ = LF; else { char * pwpeekchr = (char *)&wpeekchr; *qu++ = CR; _pipech(fh) = *pwpeekchr; ++pwpeekchr; _pipech2(fh)[0] = *pwpeekchr; _pipech2(fh)[1] = LF; /* Mark as empty */ } } else { if ((char *)qu == buf && wpeekchr == LF) { /* nothing read yet; must make some progress */ *qu++ = LF; } else { /* seek back */ filepos = _lseeki64_nolock(fh, -2, FILE_CURRENT); if (wpeekchr != LF) *qu++ = CR; } } } } } } /* we now change bytes_read to reflect the true number of chars in the buffer */ bytes_read = (int)((char *)qu - (char *)buf); } } error_return: if(buf != inputbuf) { free(buf); } return (retval == -2) ? bytes_read : retval ; }
/* now define version that doesn't lock/unlock, validate fh */ int __cdecl _write_nolock ( int fh, const void *buf, unsigned cnt ) { int lfcount; /* count of line feeds */ int charcount; /* count of chars written so far */ int written; /* count of chars written on this write */ ULONG dosretval; /* o.s. return value */ char tmode ; /* textmode - ANSI or UTF-16 */ BOOL toConsole = 0; /* true when writing to console */ BOOL isCLocale = 0; /* true when locale handle is C locale */ lfcount = charcount = 0; /* nothing written yet */ if (cnt == 0) return 0; /* nothing to do */ _VALIDATE_CLEAR_OSSERR_RETURN( (buf != NULL), EINVAL, -1 ); tmode = _textmode(fh); if(tmode == __IOINFO_TM_UTF16LE || tmode == __IOINFO_TM_UTF8) { /* For a UTF-16 file, the count must always be an even number */ _VALIDATE_CLEAR_OSSERR_RETURN(((cnt & 1) == 0), EINVAL, -1); } if (_osfile(fh) & FAPPEND) { /* appending - seek to end of file; ignore error, because maybe file doesn't allow seeking */ #if _INTEGRAL_MAX_BITS >= 64 (void)_lseeki64_nolock(fh, 0, FILE_END); #else /* _INTEGRAL_MAX_BITS >= 64 */ (void)_lseek_nolock(fh, 0, FILE_END); #endif /* _INTEGRAL_MAX_BITS >= 64 */ } /* check for text mode with LF's in the buffer */ /* * Note that in case the handle belongs to Console, write file will * generate garbage output. For user to print these characters * correctly, we will need to print ANSI. * * Also note that in case of printing to Console, we still have to * convert the characters to console codepage. */ if (_isatty(fh) && (_osfile(fh) & FTEXT)) { DWORD dwMode; _ptiddata ptd = _getptd(); isCLocale = (ptd->ptlocinfo->lc_handle[LC_CTYPE] == _CLOCALEHANDLE); toConsole = GetConsoleMode((HANDLE)_osfhnd(fh), &dwMode); } /* don't need double conversion if it's ANSI mode C locale */ if (toConsole && !(isCLocale && (tmode == __IOINFO_TM_ANSI))) { UINT consoleCP = GetConsoleCP(); char mboutbuf[MB_LEN_MAX]; wchar_t tmpchar; int size = 0; int written = 0; char *pch; for (pch = (char *)buf; (unsigned)(pch - (char *)buf) < cnt; ) { BOOL bCR; if (tmode == __IOINFO_TM_ANSI) { bCR = *pch == LF; /* * Here we need to do double convert. i.e. convert from * multibyte to unicode and then from unicode to multibyte in * Console codepage. */ if (!isleadbyte(*pch)) { if (mbtowc(&tmpchar, pch, 1) == -1) { break; } } else if ((cnt - (pch - (char*)buf)) > 1) { if (mbtowc(&tmpchar, pch, 2) == -1) { break; } /* * Increment pch to accomodate DBCS character. */ ++pch; } else { break; } ++pch; } else if (tmode == __IOINFO_TM_UTF8 || tmode == __IOINFO_TM_UTF16LE) { /* * Note that bCR set above is not valid in case of UNICODE * stream. We need to set it using unicode character. */ tmpchar = *(wchar_t *)pch; bCR = tmpchar == LF; pch += 2; } if (tmode == __IOINFO_TM_ANSI) { if( (size = WideCharToMultiByte(consoleCP, 0, &tmpchar, 1, mboutbuf, sizeof(mboutbuf), NULL, NULL)) == 0) { break; } else { if ( WriteFile( (HANDLE)_osfhnd(fh), mboutbuf, size, (LPDWORD)&written, NULL) ) { charcount += written; if (written < size) break; } else { dosretval = GetLastError(); break; } } if (bCR) { size = 1; mboutbuf[0] = CR; if (WriteFile((HANDLE)_osfhnd(fh), mboutbuf, size, (LPDWORD)&written, NULL) ) { if (written < size) break; lfcount ++; charcount++; } else { dosretval = GetLastError(); break; } } } else if ( tmode == __IOINFO_TM_UTF8 || tmode == __IOINFO_TM_UTF16LE) { if ( _putwch_nolock(tmpchar) == tmpchar ) { charcount++; } else { dosretval = GetLastError(); break; } if (bCR) /* emit carriage return */ { size = 1; tmpchar = CR; if ( _putwch_nolock(tmpchar) == tmpchar ) { charcount++; lfcount++; } else { dosretval = GetLastError(); break; } } } } } else if ( _osfile(fh) & FTEXT ) { /* text mode, translate LF's to CR/LF's on output */ dosretval = 0; /* no OS error yet */ if(tmode == __IOINFO_TM_ANSI) { char ch; /* current character */ char *p = NULL, *q = NULL; /* pointers into buf and lfbuf resp. */ char lfbuf[BUF_SIZE]; p = (char *)buf; /* start at beginning of buffer */ while ( (unsigned)(p - (char *)buf) < cnt ) { q = lfbuf; /* start at beginning of lfbuf */ /* fill the lf buf, except maybe last char */ while ( q - lfbuf < sizeof(lfbuf) - 1 && (unsigned)(p - (char *)buf) < cnt ) { ch = *p++; if ( ch == LF ) { ++lfcount; *q++ = CR; } *q++ = ch; } /* write the lf buf and update total */ if ( WriteFile( (HANDLE)_osfhnd(fh), lfbuf, (int)(q - lfbuf), (LPDWORD)&written, NULL) ) { charcount += written; if (written < q - lfbuf) break; } else { dosretval = GetLastError(); break; } } } else if ( tmode == __IOINFO_TM_UTF16LE ){ char lfbuf[BUF_SIZE]; wchar_t wch; /* current wide char */ wchar_t *pu = (wchar_t *)buf; wchar_t *qu = NULL; while ( (unsigned)((char *)pu - (char *)buf) < cnt ) { qu = (wchar_t *)lfbuf; /* start at beginning of lfbuf */ /* fill the lf buf, except maybe last wchar_t */ while ( (((char *)qu - lfbuf) < (sizeof(lfbuf) - 2)) && ((unsigned)((char *)pu - (char *)buf) < cnt )) { wch = *pu++; if ( wch == LF ) { lfcount+=2; *qu++ = CR; } *qu++ = wch; } /* write the lf buf and update total */ if ( WriteFile( (HANDLE)_osfhnd(fh), lfbuf, (int)((char*)qu - lfbuf), (LPDWORD)&written, NULL) ) { charcount += written; if (written < ((char *)qu - lfbuf)) break; } else { dosretval = GetLastError(); break; } } } else { /* * Let's divide the lfbuf in 1:2 wher 1 is for storing * widecharacters and 2 if for converting it to UTF8. This takes * into account the worst case scenario where all the UTF8 * characters are 4 byte long. */ char utf8_buf[(BUF_SIZE*2)/3]; wchar_t utf16_buf[BUF_SIZE/6]; wchar_t wch; /* current wide char */ wchar_t *pu = (wchar_t *)buf; wchar_t *qu = NULL; pu = (wchar_t *)buf; while ((unsigned)((char *)pu - (char *)buf) < cnt) { int bytes_converted = 0; qu = utf16_buf; /* start at beginning of lfbuf */ while ( (((char *)qu - (char *)utf16_buf) < (sizeof(utf16_buf) - 2)) && ((unsigned)((char *)pu - (char *)buf) < cnt )) { wch = *pu++; if ( wch == LF ) { /* no need to count the linefeeds here: we calculate the written chars in another way */ *qu++ = CR; } *qu++ = wch; } bytes_converted = WideCharToMultiByte( CP_UTF8, 0, utf16_buf, ((int)((char *)qu - (char *)utf16_buf))/2, utf8_buf, sizeof(utf8_buf), NULL, NULL); if (bytes_converted == 0) { dosretval = GetLastError(); break; } else { /* * Here we need to make every attempt to write all the * converted characters. The resaon behind this is, * incase half the bytes of a UTF8 character is * written, it may currupt whole of the stream or file. * * The loop below will make sure we exit only if all * the bytes converted are written (which makes sure no * partial MBCS is written) or there was some error in * the stream. */ int bytes_written = 0; do { if (WriteFile( (HANDLE)_osfhnd(fh), utf8_buf + bytes_written, bytes_converted - bytes_written, &written, NULL)) { bytes_written += written; } else { dosretval = GetLastError(); break; } } while ( bytes_converted > bytes_written); /* * Only way the condition below could be true is if * there was en error. In case of error we need to * break this loop as well. */ if (bytes_converted > bytes_written) { break; } /* if this chunk has been committed successfully, update charcount */ charcount = (int)((char *)pu - (char *)buf); } } } } else { /* binary mode, no translation */ if ( WriteFile( (HANDLE)_osfhnd(fh), (LPVOID)buf, cnt, (LPDWORD)&written, NULL) ) { dosretval = 0; charcount = written; } else dosretval = GetLastError(); } if (charcount == 0) { /* If nothing was written, first check if an o.s. error, otherwise we return -1 and set errno to ENOSPC, unless a device and first char was CTRL-Z */ if (dosretval != 0) { /* o.s. error happened, map error */ if (dosretval == ERROR_ACCESS_DENIED) { /* wrong read/write mode should return EBADF, not EACCES */ errno = EBADF; _doserrno = dosretval; } else _dosmaperr(dosretval); return -1; } else if ((_osfile(fh) & FDEV) && *(char *)buf == CTRLZ) return 0; else { errno = ENOSPC; _doserrno = 0; /* no o.s. error */ return -1; } } else /* return adjusted bytes written */ return charcount - lfcount; }
// This function handles the extra adjustments that need to be made to the file // position returned by ftell when a stream is opened in read mode. static __int64 __cdecl common_ftell_read_mode_nolock( __crt_stdio_stream const stream, __int64 const lowio_position, __int64 const buffer_offset ) throw() { int const fh = _fileno(stream.public_stream()); // We will need to adjust the file position of UTF-8 files to account for // UTF-8 to UTF-16 translation: __int64 const translation_factor = _textmode(fh) == __crt_lowio_text_mode::utf8 ? static_cast<__int64>(sizeof(wchar_t)) : static_cast<__int64>(sizeof(char)); // If the buffer has been exhausted, then the current lowio position is also // the current stdio position: if (stream->_cnt == 0) return lowio_position; // The lowio position points one-past-the-end of the current stdio buffer. // We need to find the position of the beginning of the buffer. To start, // we compute the number of bytes in the buffer. Note that we cannot just // use the buffer size, because the buffer will not be full if EOF is // readhed before the buffer is full. __int64 bytes_read = stream->_cnt + static_cast<__int64>(stream->_ptr - stream->_base); // If this is a binary mode stream, we can simply subtract this from the // lowio position, and combine it with the buffer offset to get the stdio // position: if ((_osfile(fh) & FTEXT) == 0) { return lowio_position - (bytes_read / translation_factor) + (buffer_offset / translation_factor); } // If this is a text mode stream, we need to adjust the number of bytes that // were read into the buffer to account for newline translation. // // If we are _not_ at EOF, the number of untranslated characters read is the // buffer size. However, if we are not at EOF, the buffer may not be full, // so we need to scan the buffer to count newline characters. (Note: we // only count newline characters if the stream is at EOF, because doing so // is more expensive than seeking to the end and seeking back). // Seek to the end of the file. If the current position is the end of the // file, then scan the buffer for newlines and adjust bytes_read: if (_lseeki64(fh, 0, SEEK_END) == lowio_position) { char const* const buffer_first = stream->_base; char const* const buffer_last = buffer_first + bytes_read; for (char const* it = buffer_first; it != buffer_last; ++it) { // We do not know whether the character preceding a newline was a // carriage reutrn, but assume that it was: if (*it == '\n') ++bytes_read; } // If the last byte was a ^Z, that character will not be present in the // buffer (it is omitted by lowio): if (stream.ctrl_z()) ++bytes_read; } // Otherwise, the current position is not at the end of the file; we need to // seek back to the original position and compute the size of the buffer: else { if (_lseeki64(fh, lowio_position, SEEK_SET) == -1) return -1; // If the number of bytes read is smaller than the small buffer and was // not user-provided, the buffer size was set to _SMALL_BUFSIZ during // the last call to __acrt_stdio_refill_and_read_{narrow,wide}_nolock: if (bytes_read <= _SMALL_BUFSIZ && stream.has_crt_buffer() && !stream.has_setvbuf_buffer()) { bytes_read = _SMALL_BUFSIZ; } // Otherwise, the buffer size is what is stated in the stream object: else { bytes_read = stream->_bufsiz; } // If the first byte in the untranslated buffer was a '\n', we assume it // was preceded by a '\r', which was discarded by the previous read // operation: if (_osfile(fh) & FCRLF) ++bytes_read; } return lowio_position - (bytes_read / translation_factor) + (buffer_offset / translation_factor); }
static __int64 __cdecl common_ftell_nolock(__crt_stdio_stream const stream) throw() { _VALIDATE_RETURN(stream.public_stream(), EINVAL, -1); int const fh = _fileno(stream.public_stream()); if (stream->_cnt < 0) stream->_cnt = 0; // Get the current lowio file position. If stdio is buffering the stream, // this position will point one past the end of the current stdio buffer. __int64 const lowio_position = _lseeki64(fh, 0, SEEK_CUR); if (lowio_position < 0) return -1; // If the stream is unbuffered or no buffering is designated, we can simply // compute the stdio position via the remaining stdio stream count: if (!stream.has_big_buffer()) return lowio_position - stream->_cnt; // The above lseek validates the handle, so it's okay to get the text mode: __crt_lowio_text_mode const text_mode = _textmode(fh); // This is the current offset into the stdio buffer; we will adjust this to // account for translation and updates as this function progresses: __int64 buffer_offset = stream->_ptr - stream->_base; // If the file is in read or write mode, we need special handling for UTF-8 // and text mode files, to account for newline translation and UTF-8 to // UTF-16 conversion: if (stream.has_any_of(_IOWRITE | _IOREAD)) { if (text_mode == __crt_lowio_text_mode::utf8 && _utf8translations(fh)) { return common_ftell_translated_utf8_nolock(stream, lowio_position); } // For text mode files, adjust the buffer offset to account for newline // translation: if (_osfile(fh) & FTEXT) { for (char const* it = stream->_base; it != stream->_ptr; ++it) { if (*it == '\n') ++buffer_offset; } } } // Otherwise, if the file is not in read/write mode, ftell cannot proceed: else if (!stream.has_all_of(_IOUPDATE)) { errno = EINVAL; return -1; } // If the current lowio position is at the beginning of the file, the stdio // position is whatever the offset is: if (lowio_position == 0) return buffer_offset; if (stream.has_all_of(_IOREAD)) { return common_ftell_read_mode_nolock(stream, lowio_position, buffer_offset); } if (text_mode == __crt_lowio_text_mode::utf8) buffer_offset /= sizeof(wchar_t); return lowio_position + buffer_offset; }