📄 pchannel.cxx
字号:
/*
* pchannel.cxx
*
* Operating System utilities.
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: pchannel.cxx,v $
* Revision 1.1 2006/06/29 04:18:19 joegenbaclor
* *** empty log message ***
*
* Revision 1.36 2005/11/30 12:47:42 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.35 2005/10/06 08:20:14 csoutheren
* Changed WriteString to ensure it always writes all data even with partial writes
*
* Revision 1.34 2005/09/18 11:05:36 dominance
* include/ptlib/channel.h, include/ptlib/pstring.h, src/ptlib/common/contain.cxx,
* src/ptlib/common/pchannel.cxx:
* correct the STL defined checking to use proper syntax.
*
* include/ptlib/object.h:
* re-add typedef to compile on mingw
*
* make/ptlib-config.in:
* import a long-standing fix from the Debian packs which allows usage of
* ptlib-config without manually adding -lpt for each of the subsequent
* projects
*
* Revision 1.33 2005/08/05 20:41:42 csoutheren
* Added unix support for scattered read/write
*
* Revision 1.32 2004/07/03 03:00:46 rjongbloed
* Fixed MSVC warning
*
* Revision 1.31 2004/07/03 01:48:28 rjongbloed
* Fixed memory leak caused by buggy iostream, can't do init twice. Thanks Norbert Bartalsky
*
* Revision 1.30 2004/06/08 01:31:08 csoutheren
* Make the test sense correct for the init(NULL)
*
* Revision 1.29 2004/06/08 01:29:00 csoutheren
* Removed memory leak on VS.net caused by unobvious iostream allocation
*
* Revision 1.28 2004/04/09 06:38:11 rjongbloed
* Fixed compatibility with STL based streams, eg as used by VC++2003
*
* Revision 1.27 2004/04/03 08:22:21 csoutheren
* Remove pseudo-RTTI and replaced with real RTTI
*
* Revision 1.26 2004/03/22 10:15:28 rjongbloed
* Added classes similar to PWaitAndSignal to automatically unlock a PReadWriteMutex
* when goes out of scope.
*
* Revision 1.25 2004/02/24 11:19:32 rjongbloed
* Fixed seekpos() function on channel to read data when seeking beyond end of corrent stream.
*
* Revision 1.24 2003/12/19 04:31:27 csoutheren
* Changed GetLastReadCount and GetLastWriteCount to be virtual
*
* Revision 1.23 2003/04/23 00:37:04 craigs
* More casts to avoid problems on MacOSX thanks to Shawn Hsiao
*
* Revision 1.22 2003/03/19 00:10:24 robertj
* Added ability to use seekoff() in a PChannel streambuf that is not a file.
*
* Revision 1.21 2003/02/11 07:22:43 robertj
* Fixed strange behaviour in ReadString(P_MAX_INDEX) with DOS text files where
* it would get extra garbage at the end of the string, thanks Joerg Schoemer.
*
* Revision 1.20 2003/02/10 01:01:03 robertj
* Fixed portability issue for lseek() calls, should just look for -1 return
* value to indicate error, thanks Joerg Schoemer
*
* Revision 1.19 2002/12/19 03:37:05 craigs
* Simplified PChannel::WriteString
*
* Revision 1.18 2002/04/09 02:30:18 robertj
* Removed GCC3 variable as __GNUC__ can be used instead, thanks jason Spence
*
* Revision 1.17 2002/01/26 23:57:45 craigs
* Changed for GCC 3.0 compatibility, thanks to manty@manty.net
*
* Revision 1.16 2001/11/13 04:13:22 robertj
* Added ability to adjust size of ios buffer on PChannels.
*
* Revision 1.15 2001/09/27 10:23:42 craigs
* CHanged ReadString to allow read until end of input with P_MAX_INDEX arg
*
* Revision 1.14 2001/09/11 02:36:52 robertj
* Fixed crash bug when ReadString() gets I/O error.
*
* Revision 1.13 2001/09/10 21:58:31 craigs
* Fixed cut and paste problem that broke PIndirectChannel::Write
*
* Revision 1.12 2001/09/10 02:51:23 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.11 2001/06/04 10:13:38 robertj
* Added compare function to compare value of os_handle.
* Added has function based on os_handle value.
*
* Revision 1.10 2001/01/02 06:07:07 robertj
* Fixed race condition in reopening indirect channel, thanks Bertrand Croq.
*
* Revision 1.9 2000/11/14 08:25:58 robertj
* Added function to propagate the error text through to indirect channel.
*
* Revision 1.8 2000/08/22 08:33:37 robertj
* Removed PAssert() for write to unattached indirect channel, now sets
* return code so is similay to "unopened file" semantics.
*
* Revision 1.7 2000/06/26 11:17:20 robertj
* Nucleus++ port (incomplete).
*
* Revision 1.6 1999/07/06 08:55:05 robertj
* Fixed bug in PFile::Copy, does not write last chunk of data to new file.
*
* Revision 1.5 1999/06/17 14:44:42 robertj
* Fixed incorrect comparison of open write channel
*
* Revision 1.4 1999/06/17 13:38:11 robertj
* Fixed race condition on indirect channel close, mutex needed in PIndirectChannel.
*
* Revision 1.3 1999/02/22 10:10:12 robertj
* Changed channel output flush to remove double Write() call.
*
* Revision 1.2 1999/01/31 00:57:18 robertj
* Fixed bug when opening an already open file, should close it!
*
* Revision 1.1 1998/11/30 12:46:19 robertj
* Initial revision
*
*/
#include <ptlib.h>
#include <ctype.h>
///////////////////////////////////////////////////////////////////////////////
// PChannel
PChannelStreamBuffer::PChannelStreamBuffer(PChannel * chan)
: channel(PAssertNULL(chan))
{
}
BOOL PChannelStreamBuffer::SetBufferSize(PINDEX newSize)
{
return input.SetSize(newSize) && output.SetSize(newSize);
}
int PChannelStreamBuffer::overflow(int c)
{
if (pbase() == NULL) {
char * p = output.GetPointer(1024);
setp(p, p+output.GetSize());
}
int bufSize = pptr() - pbase();
if (bufSize > 0) {
setp(pbase(), epptr());
if (!channel->Write(pbase(), bufSize))
return EOF;
}
if (c != EOF) {
*pptr() = (char)c;
pbump(1);
}
return 0;
}
int PChannelStreamBuffer::underflow()
{
if (eback() == NULL) {
char * p = input.GetPointer(1024);
char * e = p+input.GetSize();
setg(p, e, e);
}
if (gptr() != egptr())
return (BYTE)*gptr();
if (!channel->Read(eback(), egptr() - eback()) ||
channel->GetErrorCode() != PChannel::NoError)
return EOF;
PINDEX count = channel->GetLastReadCount();
char * p = egptr() - count;
memmove(p, eback(), count);
setg(eback(), p, egptr());
return (BYTE)*p;
}
int PChannelStreamBuffer::sync()
{
int inAvail = egptr() - gptr();
if (inAvail > 0) {
setg(eback(), egptr(), egptr());
if (PIsDescendant(channel, PFile))
((PFile *)channel)->SetPosition(-inAvail, PFile::Current);
}
if (pptr() > pbase())
return overflow();
return 0;
}
#ifdef __USE_STL__
streampos PChannelStreamBuffer::seekoff(off_type off, ios_base::seekdir dir, ios_base::openmode)
#else
streampos PChannelStreamBuffer::seekoff(streamoff off, ios::seek_dir dir, int)
#endif
{
sync();
if (PIsDescendant(channel, PFile)) {
PFile * file = (PFile *)channel;
file->SetPosition(off, (PFile::FilePositionOrigin)dir);
return file->GetPosition();
}
// If we have an input stream and the buffer is empty then force a read so
// we can seek ahead.
if (egptr() == gptr()) {
int c = underflow();
if (c == EOF)
return EOF;
}
while (off-- > 0) {
if (sbumpc() == EOF)
return EOF;
}
return egptr() - gptr();
}
#ifdef __USE_STL__
streampos PChannelStreamBuffer::seekpos(pos_type pos, ios_base::openmode mode)
{
return seekoff(pos, ios_base::beg, mode);
}
#endif
#ifdef _MSC_VER
#pragma warning(disable:4355)
#endif
PChannel::PChannel()
: iostream(new PChannelStreamBuffer(this)),
readTimeout(PMaxTimeInterval), writeTimeout(PMaxTimeInterval)
{
os_handle = -1;
memset(lastErrorCode, 0, sizeof(lastErrorCode));
memset(lastErrorNumber, 0, sizeof(lastErrorNumber));
lastReadCount = lastWriteCount = 0;
Construct();
}
#ifdef _MSC_VER
#pragma warning(default:4355)
#endif
PChannel::~PChannel()
{
flush();
Close();
delete (PChannelStreamBuffer *)rdbuf();
#ifndef _WIN32
init(NULL);
#endif
}
PObject::Comparison PChannel::Compare(const PObject & obj) const
{
PAssert(PIsDescendant(&obj, PChannel), PInvalidCast);
int h1 = GetHandle();
int h2 = ((const PChannel&)obj).GetHandle();
if (h1 < h2)
return LessThan;
if (h1 > h2)
return GreaterThan;
return EqualTo;
}
PINDEX PChannel::HashFunction() const
{
return GetHandle()%97;
}
BOOL PChannel::IsOpen() const
{
return os_handle >= 0;
}
PINDEX PChannel::GetLastReadCount() const
{
return lastReadCount;
}
PINDEX PChannel::GetLastWriteCount() const
{
return lastWriteCount;
}
int PChannel::ReadChar()
{
BYTE c;
BOOL retVal = Read(&c, 1);
return (retVal && lastReadCount == 1) ? c : -1;
}
int PChannel::ReadCharWithTimeout(PTimeInterval & timeout)
{
SetReadTimeout(timeout);
PTimeInterval startTick = PTimer::Tick();
int c;
if ((c = ReadChar()) < 0) // Timeout or aborted
return -1;
timeout -= PTimer::Tick() - startTick;
return c;
}
BOOL PChannel::ReadBlock(void * buf, PINDEX len)
{
char * ptr = (char *)buf;
PINDEX numRead = 0;
while (numRead < len && Read(ptr+numRead, len - numRead))
numRead += lastReadCount;
lastReadCount = numRead;
return lastReadCount == len;
}
PString PChannel::ReadString(PINDEX len)
{
PString str;
if (len == P_MAX_INDEX) {
PINDEX l = 0;
for (;;) {
char * p = l + str.GetPointer(l+1000+1);
if (!Read(p, 1000))
break;
l += lastReadCount;
}
str.SetSize(l+1);
/*Need to put in a null at the end to allow for MSDOS/Win32 text files
which returns fewer bytes than actually read as it shrinks the data into
the removed carriage returns, so it actually changes the buffer beyond
what it indicated. */
str[l] = '\0';
}
else {
if (!ReadBlock(str.GetPointer(len+1), len))
return PString::Empty();
}
return str;
}
BOOL PChannel::WriteString(const PString & str)
{
PINDEX len = str.GetLength();
PINDEX written = 0;
while (written < len) {
if (!Write((const char *)str + written, len - written)) {
lastWriteCount += written;
return FALSE;
}
written += lastWriteCount;
}
lastWriteCount = written;
return TRUE;
}
BOOL PChannel::ReadAsync(void * buf, PINDEX len)
{
BOOL retVal = Read(buf, len);
OnReadComplete(buf, lastReadCount);
return retVal;
}
void PChannel::OnReadComplete(void *, PINDEX)
{
}
BOOL PChannel::WriteChar(int c)
{
PAssert(c >= 0 && c < 256, PInvalidParameter);
char buf = (char)c;
return Write(&buf, 1);
}
BOOL PChannel::WriteAsync(const void * buf, PINDEX len)
{
BOOL retVal = Write(buf, len);
OnWriteComplete(buf, lastWriteCount);
return retVal;
}
void PChannel::OnWriteComplete(const void *, PINDEX)
{
}
BOOL PChannel::SetBufferSize(PINDEX newSize)
{
return ((PChannelStreamBuffer *)rdbuf())->SetBufferSize(newSize);
}
enum {
NextCharEndOfString = -1,
NextCharDelay = -2,
NextCharSend = -3,
NextCharWait = -4
};
static int HexDigit(char c)
{
if (!isxdigit(c))
return 0;
int hex = c - '0';
if (hex < 10)
return hex;
hex -= 'A' - '9' - 1;
if (hex < 16)
return hex;
return hex - ('a' - 'A');
}
static int GetNextChar(const PString & command,
PINDEX & pos, PTimeInterval * time = NULL)
{
int temp;
if (command[pos] == '\0')
return NextCharEndOfString;
if (command[pos] != '\\')
return command[pos++];
switch (command[++pos]) {
case '\0' :
return NextCharEndOfString;
case 'a' : // alert (ascii value 7)
pos++;
return 7;
case 'b' : // backspace (ascii value 8)
pos++;
return 8;
case 'f' : // formfeed (ascii value 12)
pos++;
return 12;
case 'n' : // newline (ascii value 10)
pos++;
return 10;
case 'r' : // return (ascii value 13)
pos++;
return 13;
case 't' : // horizontal tab (ascii value 9)
pos++;
return 9;
case 'v' : // vertical tab (ascii value 11)
pos++;
return 11;
case 'x' : // followed by hh where nn is hex number (ascii value 0xhh)
if (isxdigit(command[++pos])) {
temp = HexDigit(command[pos++]);
if (isxdigit(command[pos]))
temp += HexDigit(command[pos++]);
return temp;
}
return command[pos];
case 's' :
pos++;
return NextCharSend;
case 'd' : // ns delay for n seconds/milliseconds
case 'w' :
temp = command[pos] == 'd' ? NextCharDelay : NextCharWait;
long milliseconds = 0;
while (isdigit(command[++pos]))
milliseconds = milliseconds*10 + command[pos] - '0';
if (milliseconds <= 0)
milliseconds = 1;
if (command[pos] == 'm')
pos++;
else {
milliseconds *= 1000;
if (command[pos] == 's')
pos++;
}
if (time != NULL)
*time = milliseconds;
return temp;
}
if (command[pos] < '0' || command[pos] > '7')
return command[pos++];
// octal number
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -