?? stdio.cpp
字號:
// Leftovers at the end
const int cch = ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
pchSrc, cbSrc,
pwchDst, cchAvailable);
if (0 == cch)
{
*pcchEmitted = cchOut;
return FALSE;
}
cchOut += cch;
pwchDst += cch;
cchAvailable -= cch;
}
*pcchEmitted = cchOut;
return TRUE;
}
// Expanding newlines into CRNL sequences, sans Unicode
BOOL CookOutputTextAA(const char* pchSrc, unsigned cbSrc, unsigned cbNewlines,
char* pchDst, unsigned cchAvailable, unsigned* pcchEmitted )
{
unsigned cchOut = 0; // count of characters written to pwchOut
if (cbNewlines > 0 && cbSrc > 0)
{
// Seeking newlines in the sourcetext, one line at a time
// Thought: since we have cbNewlines, could use strchr here,
// decrementing a count of the # of newlines found.
// That approach would easily transition to an approach
// where the initial cbNewline count also saved offsets.
const char* pch = pchSrc;
unsigned cb = cbSrc;
while (cb--)
{
if (*pch++ == '\n') //$ BUGBUG DBCS, again
{
// Copy/convert everything but the newline
const int cbLine = cbSrc - cb - 1;
if (cbLine > 0)
{
memcpy(pchDst, pchSrc, cbLine);
cchOut += cbLine;
pchDst += cbLine;
cchAvailable -= cbLine;
}
// Emit a 'cooked' newline sequence to replace the newline
*pchDst++ = '\r';
*pchDst++ = '\n';
cchOut += 2;
cchAvailable -= 2;
cbSrc = cb;
pchSrc = pch;
}
}
}
if (cbSrc > 0)
{
// Leftovers at the end
memcpy(pchDst, pchSrc, cbSrc);
cchOut += cbSrc;
pchDst += cbSrc;
cchAvailable -= cbSrc;
}
*pcchEmitted = cchOut;
return TRUE;
}
// Count the number of newlines in source
unsigned CountNewlines(const char* pch, unsigned cb)
{
//$ BUGBUG DBCS - sigh -
// can't use strchr loop because pchOut not NUL terminated
unsigned cbNewlines = 0;
while (cb--)
if (*pch++ == '\n')
++cbNewlines;
return cbNewlines;
}
CXFileStream::CXFileStream()
: _h(NULL), _fUseUnicode(FALSE), _cchCRsEaten(0)
{
// Since this object is reused, do all real init in Open
}
CXFileStream::~CXFileStream()
{
Close();
}
BOOL CXFileStream::LoadReadBuffer(unsigned cbHint)
{
const int cchReadQuantum = 512; // used instead of 'cbHint'
if (NULL == _h)
return FALSE;
if (!_fCooked)
{
// 1 character, 1 byte. Life is simple and good.
if (!RequireBuffer(cchReadQuantum)) // set _{c,p}bBuffer
return FALSE;
DWORD cbActuallyRead = 0L;
if (!::ReadFile(_h, _pbBuffer, cchReadQuantum, &cbActuallyRead, NULL))
{
//$ BUGBUG better error logging?
return FALSE;
}
if (0 == cbActuallyRead)
return FALSE;
_cbActual = cbActuallyRead;
}
else
{
// Require space for reading text from file,
// plus space for cooking text: CRNL->NL, possibly Unicode->MBCS.
// cbRequired: one of the (1+cchReadQuantum)*cbChar is for the WCHAR input,
// and one is for the MBCS output,
// pessimistically assuming that every wchar will expand into a 2-byte dbcs sequence.
// While this seems pointless, given that dbcs input would blow SIOD out of the water,
// that might change someday....
const unsigned cbOneChar = _fUseUnicode ? sizeof(WCHAR) : sizeof(char);
const unsigned cbRead = cchReadQuantum*cbOneChar;
const unsigned cbRequired = 2*((1+cchReadQuantum)*cbOneChar);
if (!RequireBuffer(cbRequired)) // set _{c,p}bBuffer
return FALSE;
// Fetch source text into tail-end of buffer
const int ibOffset = (1+cchReadQuantum)* cbOneChar;
BYTE* pbInPreConvert = _pbBuffer+ibOffset;
DWORD cbActuallyRead = 0L;
if (!::ReadFile(_h, pbInPreConvert, cbRead, &cbActuallyRead, NULL))
return FALSE;
if (0 == cbActuallyRead)
return FALSE;
BOOL fHadTrailingCR;
unsigned cchCRsEaten;
if (_fUseUnicode)
{
// Convert to MBCS, flattening CRLF sequences to LF as we go
const unsigned cch = cbActuallyRead/sizeof(WCHAR);
((WCHAR*)pbInPreConvert)[cch] = 0;
if (!CookInputTextWA((const WCHAR*)pbInPreConvert, cch,
(char*)_pbBuffer, ibOffset, &_cbActual, &fHadTrailingCR, &cchCRsEaten))
return FALSE;
}
else
{
// Copy and flatten
((char*)pbInPreConvert)[cbActuallyRead] = 0;
if (!CookInputTextAA((const char*)pbInPreConvert, cbActuallyRead,
(char*)_pbBuffer, ibOffset, &_cbActual, &fHadTrailingCR, &cchCRsEaten))
return FALSE;
}
// Special evil case: if last character was a CR,
// we might have a CRLF sequence crossing the boundary of the read buffer.
if (fHadTrailingCR)
{
// Peek ahead to see if it is so.
// (Assumes we're working from a disk file. Were this a live IPC channel,
// we'd delete the trailing CR, then save the pending-possible-CRLF
// state to resolve in the next Read call.)
BOOL fFoundNL;
if (_fUseUnicode)
{
WCHAR chReadAhead;
if (!::ReadFile(_h, &chReadAhead, sizeof(WCHAR), &cbActuallyRead, NULL))
return FALSE;
fFoundNL = (sizeof(WCHAR) == cbActuallyRead && __T('\n') == chReadAhead);
}
else
{
char chReadAhead;
if (!::ReadFile(_h, &chReadAhead, sizeof(char), &cbActuallyRead, NULL))
return FALSE;
fFoundNL = (sizeof(char) == cbActuallyRead && '\n' == chReadAhead);
}
if (fFoundNL)
{
_pbBuffer[_cbActual] = '\n';
++cchCRsEaten;
}
if (cbActuallyRead > 0 && !fFoundNL)
::SetFilePointer(_h, -1*cbActuallyRead, NULL, FILE_CURRENT);
}
_cchCRsEaten = cchCRsEaten;
}
return (_cbActual > 0);
}
void CXFileStream::Write(const BYTE* pbOut, unsigned cbOut, unsigned* pcbOutActual)
{
if (NULL == _h)
return;
if (_fAppending)
{
// All writes move to end of file
::SetFilePointer(_h, 0, NULL, FILE_END);
}
else if (_fReading && _cbActual > 0)
{
// Write must move back to point of last "read" character
int ibOffset = _cbActual - _ibCurrent;
if (_fCooked && ibOffset > 0)
ibOffset += CountNewlines((const char*)(_pbBuffer+_ibCurrent), ibOffset);
if (_fUseUnicode && ibOffset > 0)
ibOffset *= sizeof(WCHAR);
if (ibOffset > 0)
::SetFilePointer(_h, -ibOffset, NULL, FILE_CURRENT);
}
if (_fReading)
ResetReadBuffer();
if (!_fCooked)
{
DWORD cbActualWrite = 0L;
if (!::WriteFile(_h, pbOut, cbOut, &cbActualWrite, NULL))
{
//$ BUGBUG better error logging?
*pcbOutActual = 0;
return;
}
*pcbOutActual = cbActualWrite;
}
else
{
// Use "cooked" to emit Unicode text, expand LF to CRLF.
const unsigned cbNewlines = CountNewlines((const char*)pbOut, cbOut);
const unsigned cbOneChar = _fUseUnicode ? sizeof(WCHAR) : sizeof(char);
CLocalTempBuffer buf;
if (!buf.Init( (cbOut+cbNewlines)*cbOneChar ))
{
*pcbOutActual = 0;
return;
}
BYTE* pbOutConverted = buf.GetBuffer();
unsigned cch = 0;
if (_fUseUnicode)
{
if (!CookOutputTextAW((const char*)pbOut, cbOut, cbNewlines,
(WCHAR*)pbOutConverted, cbOut+cbNewlines, &cch))
{
*pcbOutActual = 0;
return;
}
}
else
{
if (!CookOutputTextAA((const char*)pbOut, cbOut, cbNewlines,
(char*)pbOutConverted, cbOut+cbNewlines, &cch))
{
*pcbOutActual = 0;
return;
}
}
DWORD cbActualWrite = 0L;
if (!::WriteFile(_h, pbOutConverted, cbOneChar*cch, &cbActualWrite, NULL))
{
//$ BUGBUG better error logging?
*pcbOutActual = 0;
return;
}
*pcbOutActual = cbActualWrite;
}
}
unsigned CXFileStream::TellUnbuffered()
{
unsigned ib = ::SetFilePointer(_h, 0, NULL, FILE_CURRENT);
if (_fUseUnicode)
{
ib /= sizeof(WCHAR);
ib -= 1; // for BOM
}
return ib;
}
unsigned CXFileStream::Tell()
{
if (NULL == _h)
return 0;
unsigned ib = TellUnbuffered();
if (_fReading)
{
// Adjust for characters in read buffer
ib -= _cbActual;
// Adjust again for CR characters missing from read buffer
if (_fCooked)
ib -= _cchCRsEaten;
// Move forward by characters "read" in read buffer
ib += _ibCurrent;
}
return ib;
}
BOOL CXFileStream::Seek(int ibOffset, int nType)
{
if (NULL == _h)
return FALSE;
DWORD dwMethod;
switch (nType)
{
case SEEK_CUR:
dwMethod = FILE_CURRENT;
break;
case SEEK_END:
dwMethod = FILE_END;
break;
case SEEK_SET:
default:
dwMethod = FILE_BEGIN;
break;
}
//
// File offsets vs. stream buffer offsets
// ====================================================
// ^ ^ ^
// | | |
// | | |
// ibInFile | ibInFile
// - _cbActual | (TRUE file offset)
// - _cchCRsEaten |
// ibInFile
// - _cbActual
// - _cchCRsEaten
// + _ibCurrent
// (VIRTUAL file offset)
//
// Note that for the purposes of these calculations
// we place all the imaginary lost bytes represented by _cchCRsEaten
// between the last buffered-read byte - _pbBuffer+_cbActual-1 - and ibInFile.
// There is no attempt to associate a particular lost CR
// with a particular offset in the file.
if (_fReading )
{
if (nType != SEEK_END)
{
// If the new offset lands within the read-buffer, "seek" takes place therein
const unsigned ibInFile = TellUnbuffered();
const unsigned ibStartBuf = ibInFile - _cbActual - _cchCRsEaten;
const unsigned ibVirtual = ibStartBuf + _ibCurrent;
unsigned ibNewVirtual;
if (nType == SEEK_CUR)
{
// Need signed arithmetic, since relative offset is signed
ibNewVirtual = (unsigned)((int)ibVirtual + ibOffset);
}
else // SEEK_SET
{
ibNewVirtual = (unsigned)ibOffset;
}
if (ibNewVirtual >= ibStartBuf && ibNewVirtual < ibInFile)
{
_ibCurrent = ibNewVirtual - ibStartBuf;
return TRUE;
}
}
// Untested hypothesis: it is worthwhile to preserve sequential-read behavior
// when presenting reads and seeks to the filesystem.
// Adjust the relative seek offset to compensate for read-buffer position,
// i.e. the characters buffered but not yet "read,"
// since true file offset is ahead of virtual offset.
// Could instead convert all relative seeks to absolute seeks,
// but then the lack of true CRLF adjustment might bite harder.
if (nType == SEEK_CUR)
{
const unsigned dbRelative = _cbActual - _ibCurrent;
ibOffset -= ((int)dbRelative + (int)_cchCRsEaten);
}
// Having adjusted for the seek-ptr offset as necessary,
// discard the now useless readbuffer
ResetReadBuffer();
}
if (_fUseUnicode)
{
int nGuard = ibOffset; // test for overflow
ibOffset *= sizeof(WCHAR);
if ( ((ibOffset > 0) && (ibOffset < nGuard))
||((ibOffset < 0) && (ibOffset > nGuard)) )
return FALSE;
if (dwMethod != FILE_END)
{
nGuard = ibOffset;
ibOffset += sizeof(WCHAR); // for BOM
if (ibOffset < nGuard) // test again for overflow
return FALSE;
}
}
if (0xffffffff == ::SetFilePointer(_h, ibOffset, NULL, dwMethod))
return FALSE;
return TRUE;
}
void CXFileStream::Close()
{
ResetReadBuffer();
ReleaseBuffer();
if (NULL == _h)
return;
::CloseHandle(_h);
_h = NULL;
}
/* MODE flags for an opened file:
* "r": read "w": write "a": append
* "r+": read/write "w+": open empty for read/write
* "a+": read/append
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -