?? stdio.cpp
字號:
// Copyright 1998, Ben Goetter. All rights reserved.
/* stdio.cpp */
#if (defined(WIN32_PLATFORM_PSPC) || (_WIN32_WCE >= 300)) || defined(x86)
#else
#include "stdinc.h"
#include <limits.h>
#define SZ_DEFAULT_CAPTION __T("STDIO")
//#include "assert.h"
#define IMPLEMENTS_STDIO
#include "stdio.h"
#include "stdioimp.h"
#define MAXOPENFILES 10
#define assert(x) 1
// These two have streams defined and owned by the client
FILE StdioStdin = {FALSE, NULL};
FILE StdioStdout = {FALSE, NULL};
// These all have streams owned by stdio itself
FILE RgStdioFiles[MAXOPENFILES] = {
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL},
{FALSE, NULL}
};
inline BOOL IsInvalidFile(FILE* pfile)
{
return ((NULL == pfile) || (!pfile->fActive));
}
// DLL entry point
HANDLE g_hinstDll = NULL;
BOOL WINAPI DllMain(HANDLE hinstDll, DWORD fdwReason, LPVOID)
{
if (DLL_PROCESS_ATTACH == fdwReason)
{
g_hinstDll = hinstDll;
}
else if (DLL_PROCESS_DETACH == fdwReason)
{
for (int i = 0; i < MAXOPENFILES; ++i)
if (RgStdioFiles[i].fActive)
delete RgStdioFiles[i].pstream;
}
return TRUE;
}
//$ BUGBUG - this package needs errno/strerror,
// or some other way of getting an errorcode out of the stdio-ish interface
// Also - support _fmode, ferror, _doserrno
// A small buffer
// that allocates itself from the frame whenever possible
class CLocalTempBuffer
{
private:
BYTE _rgbFrame[256];
BYTE* _pbAlloc;
public:
CLocalTempBuffer() : _pbAlloc(NULL) {}
~CLocalTempBuffer()
{ if (_pbAlloc) ::LocalFree(_pbAlloc); }
BOOL Init(unsigned cb)
{
assert(NULL == _pbAlloc);
if (cb > sizeof(_rgbFrame))
{
_pbAlloc = (BYTE*)::LocalAlloc(LMEM_FIXED, cb);
if (NULL == _pbAlloc)
return FALSE;
}
return TRUE;
}
BYTE* GetBuffer() const
{ return ((_pbAlloc == NULL) ? (BYTE*)_rgbFrame : _pbAlloc); }
};
// Exported to main wndproc holder - called on WM_HIBERNATE
void StdioOnHibernate()
{
//$ BUGBUG - may be called from UI thread while streams in use by SIOD
// should guard with critical section
if (StdioStdin.fActive)
StdioStdin.pstream->OnHibernate();
if (StdioStdout.fActive)
StdioStdout.pstream->OnHibernate();
for (int i = 0; i < MAXOPENFILES; ++i)
if (RgStdioFiles[i].fActive)
RgStdioFiles[i].pstream->OnHibernate();
}
void StdioInitStdin(CXStdioStream* pstreamStdin)
{
StdioStdin.pstream = pstreamStdin;
StdioStdin.fActive = (NULL != pstreamStdin);
}
void StdioInitStdout(CXStdioStream* pstreamStdout)
{
StdioStdout.pstream = pstreamStdout;
StdioStdout.fActive = (NULL != pstreamStdout);
}
CXStdioStream::CXStdioStream() :
_fPushback(FALSE), _chPushback(0),
_fReading(FALSE), _fWriting(FALSE), _fAppending(FALSE),
_fCooked(FALSE)
{}
CXStdioStream::~CXStdioStream()
{}
void CXStdioStream::Flush()
{
// Default implementation only clears pushback buffer
// (good enough for stdin/out)
_fPushback = FALSE;
}
unsigned CXStdioStream::Tell()
{
// not implemented in stdin/stdout
return 0;
}
BOOL CXStdioStream::Seek(int ibOffset, int nType)
{
// not implemented in stdin/stdout
return FALSE;
}
void CXStdioStream::Read(BYTE* pbIn, unsigned cbInMax, unsigned* pcbInActual)
{
// not implemented in stdout
*pcbInActual = 0;
}
void CXStdioStream::Write(const BYTE* pbOut, unsigned cbOut, unsigned* pcbOutActual)
{
// not implemented in stdin
*pcbOutActual = 0;
}
void CXStdioStream::Pushback(unsigned char ch)
{
_chPushback = ch;
_fPushback = TRUE;
}
int CXStdioStream::CharReady()
{
// The default - only needs override for tty devices
return 1;
}
CXBufferedStream::CXBufferedStream()
: _pbBuffer(NULL), _cbBuffer(0)
{}
CXBufferedStream::~CXBufferedStream()
{
if (_pbBuffer)
::LocalFree(_pbBuffer);
}
BOOL CXBufferedStream::RequireBuffer(unsigned cbRequired)
{
if (cbRequired > _cbBuffer)
{
if (_pbBuffer)
{
::LocalFree(_pbBuffer);
_pbBuffer = NULL;
_cbBuffer = 0;
}
// Round to multiple of 256b as anti-heap-frag measure
const unsigned cbAlloc = (((cbRequired-1)/256)+1) * 256;
_pbBuffer = (BYTE*)::LocalAlloc(LMEM_FIXED, cbAlloc);
if (!_pbBuffer)
{
return FALSE; // Signal EOF
}
_cbBuffer = cbAlloc;
}
return TRUE;
}
void CXBufferedStream::ReleaseBuffer()
{
if (_pbBuffer)
{
::LocalFree(_pbBuffer);
_pbBuffer = NULL;
_cbBuffer = 0;
}
}
CXBufferedReadStream::CXBufferedReadStream() :
#if 0 // for stdin Seek/Tell - nyi
_cbReadTotal(0),
#endif
_cbActual(0), _ibCurrent(0)
{}
CXBufferedReadStream::~CXBufferedReadStream()
{}
void CXBufferedReadStream::OnHibernate()
{
if (_cbBuffer > 0 && (0 == _cbActual || _cbActual == _ibCurrent))
{
ResetReadBuffer();
ReleaseBuffer();
}
}
void CXBufferedReadStream::Read(BYTE* pbIn, unsigned cbInMax, unsigned* pcbInActual)
{
unsigned cbReadActual = 0;
if (_fPushback && cbInMax > 0)
{
*pbIn++ = _chPushback;
_fPushback = FALSE;
--cbInMax;
++cbReadActual;
}
while (cbInMax > 0) // didn't get it all out of pushback? what a surprise
{
if (cbInMax > 0 && _cbActual > 0 && _ibCurrent < _cbActual)
{
// Get what we can out of the read-buffer
unsigned cbAvailable = min(cbInMax, (_cbActual-_ibCurrent));
memcpy(pbIn, _pbBuffer+_ibCurrent, cbAvailable);
pbIn += cbAvailable;
cbInMax -= cbAvailable;
_ibCurrent += cbAvailable;
cbReadActual += cbAvailable;
}
if (cbInMax > 0) // As necessary, reload the read-buffer
{
ResetReadBuffer();
if (!LoadReadBuffer(cbInMax)) // pass remaining amt needed as hint
break;
}
}
#if 0 // track for stdin/stdout Seek/Tell
_cbReadTotal += cbReadActual;
#endif
*pcbInActual = cbReadActual;
}
// Convert to MBCS, flattening CRLF sequences to LF as we go
BOOL CookInputTextWA(const WCHAR* pwchSrc, unsigned cchSrc,
char* pchDst0, unsigned cbInAvail, unsigned* pcbEmitted,
BOOL* pfHadTrailingCR,
unsigned* pcchCRsEaten)
{
char* pchDst = pchDst0; // retain original for pcbEmitted calc
unsigned cchCRsEaten = 0; // count number of CRLF sequences flattened
if (cchSrc > 0 && cbInAvail > 0)
{
const WCHAR* pwchEol = NULL;
while (pwchEol = wcsstr(pwchSrc, __T("\r\n")))
{
const int cchLine = pwchEol - pwchSrc;
if (cchLine > 0)
{
const int cbMbcs = ::WideCharToMultiByte(CP_ACP, 0,
pwchSrc, cchLine,
pchDst, cbInAvail, NULL, NULL);
if (0 == cbMbcs)
return FALSE; // Signal EOF
pchDst += cbMbcs;
cbInAvail -= cbMbcs;
}
*pchDst++ = '\n';
--cbInAvail;
pwchSrc = pwchEol + 2; // CRLF
cchSrc -= cchLine+2;
++cchCRsEaten;
}
}
if (cchSrc > 0 && cbInAvail > 0)
{
// Need to detect a final CR character,
// to catch the case where a CRLF sequence crosses the boundary of a read buffer.
// Caller can look ahead and patch the final char in the outstring as necessary
if (NULL != pfHadTrailingCR)
*pfHadTrailingCR = (__T('\r') == pwchSrc[cchSrc-1]);
const int cbMbcs = ::WideCharToMultiByte(CP_ACP, 0,
pwchSrc, cchSrc,
pchDst, cbInAvail, NULL, NULL);
if (0 == cbMbcs)
return FALSE; // Signal EOF
pchDst += cbMbcs;
}
else
{
if (NULL != pfHadTrailingCR)
*pfHadTrailingCR = FALSE; // all were converted
}
*pcbEmitted = pchDst - pchDst0;
if (NULL != pcchCRsEaten)
*pcchCRsEaten = cchCRsEaten;
return TRUE;
}
// Flatten CRLF sequences to LF - no Unicode decoding necessary
BOOL CookInputTextAA(const char* pchSrc, unsigned cchSrc,
char* pchDst0, unsigned cbInAvail, unsigned* pcbEmitted,
BOOL* pfHadTrailingCR,
unsigned* pcchCRsEaten)
{
char* pchDst = pchDst0; // retain original for pcbEmitted calc
unsigned cchCRsEaten = 0; // count number of CRLF sequences flattened
if (cchSrc > 0 && cbInAvail > 0)
{
const char* pchEol = NULL;
while (pchEol = strstr(pchSrc, "\r\n"))
{
const int cchLine = pchEol - pchSrc;
if (cchLine > 0)
{
const int cbCopy = min(cchLine, (int)cbInAvail);
memcpy(pchDst, pchSrc, cbCopy);
pchDst += cbCopy;
cbInAvail -= cbCopy;
}
*pchDst++ = '\n';
--cbInAvail;
pchSrc = pchEol + 2; // CRLF
cchSrc -= cchLine+2;
++cchCRsEaten;
}
}
if (cchSrc > 0 && cbInAvail > 0)
{
// Need to detect a final CR character,
// to catch the case where a CRLF sequence crosses the boundary of a read buffer.
// Caller can look ahead and patch the final char in the outstring as necessary
if (NULL != pfHadTrailingCR)
*pfHadTrailingCR = ('\r' == pchSrc[cchSrc-1]);
const int cbCopy = min(cchSrc, cbInAvail);
memcpy(pchDst, pchSrc, cbCopy);
pchDst += cbCopy;
}
else
{
if (NULL != pfHadTrailingCR)
*pfHadTrailingCR = FALSE; // all were converted
}
*pcbEmitted = pchDst - pchDst0;
if (NULL != pcchCRsEaten)
*pcchCRsEaten = cchCRsEaten;
return TRUE;
}
// Convert to Unicode, expanding newlines into CRNL sequences
BOOL CookOutputTextAW(const char* pchSrc, unsigned cbSrc, unsigned cbNewlines,
WCHAR* pwchDst, 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)
{
const int cch = ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
pchSrc, cbLine,
pwchDst, cchAvailable);
if (0 == cch)
{
*pcchEmitted = cchOut;
return FALSE;
}
cchOut += cch;
pwchDst += cch;
cchAvailable -= cch;
}
// Emit a 'cooked' newline sequence to replace the newline
*pwchDst++ = __T('\r');
*pwchDst++ = __T('\n');
cchOut += 2;
cchAvailable -= 2;
cbSrc = cb;
pchSrc = pch;
}
}
}
if (cbSrc > 0)
{
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -