mingw stream improvements

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@23254 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2006-08-11 13:27:10 +00:00
parent 32b9892e7b
commit 212c286e3f
6 changed files with 665 additions and 253 deletions

View file

@ -1,11 +1,18 @@
2006-08-11 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSTask.m: Fixup for path validation error on mingw32
* Headers/Foundation/NSStream.h: improve documentation.
* Source/GSStream.h:
* Source/GSStream.m:
* Source/unix/NSStream.m:
* Source/win32/NSStreamWin32.m:
Add lots of error checking, simplify a little, get local streams on
mingw to close down properly.
2006-08-10 Richard Frith-Macdonald <rfm@gnu.org>
* Source\GSStream.m:
* Source\win32\NSStreamWin32.m:
* Source/GSStream.m:
* Source/win32/NSStreamWin32.m:
Fix error in pipe read offset. Imitial implementation of local
'unix domain sockets' (actually named pipe) stream.
@ -16,10 +23,10 @@
2006-08-10 Richard Frith-Macdonald <rfm@gnu.org>
* Source\GSStream.h:
* Source\GSStream.m:
* Source\unix\NSStream.m:
* Source\win32\NSStreamWin32.m:
* Source/GSStream.h:
* Source/GSStream.m:
* Source/unix/NSStream.m:
* Source/win32/NSStreamWin32.m:
Ensure all events are posted. Fixup for winsock write signalling.
2006-08-09 Richard Frith-Macdonald <rfm@gnu.org>
@ -930,13 +937,13 @@
* Source/NSUserDefaults.m: Restructure for lazy creation of defaults
database ... only create when we attempt to write.
* Source\NSSocketPort.m: Include GSPrivate.h
* Source\NSRunLoop.m: ditto
* Source\unix\GSRunLoopCtxt.m: ditto
* Source\GSPrivate.h: ditto
* Source\win32\GSFileHandleWin32.m: ditto
* Source\win32\GSRunLoopCtxt.m: ditto
* Headers\Foundation\NSNotificationQueue.h: Move library internal
* Source/NSSocketPort.m: Include GSPrivate.h
* Source/NSRunLoop.m: ditto
* Source/unix/GSRunLoopCtxt.m: ditto
* Source/GSPrivate.h: ditto
* Source/win32/GSFileHandleWin32.m: ditto
* Source/win32/GSRunLoopCtxt.m: ditto
* Headers/Foundation/NSNotificationQueue.h: Move library internal
function declarations to GSPrivate.h, thanks to Lloyd Dupont for
spotting problem.

View file

@ -70,7 +70,8 @@ typedef enum {
outputStream: (NSOutputStream **)outputStream;
/**
* Closes the receiver.
* Closes the receiver.<br />
* Repeated calls to this method on the same stream are quietly ignored.
*/
- (void) close;
@ -80,7 +81,10 @@ typedef enum {
- (id) delegate;
/**
* Opens the receiving stream.
* Opens the receiving stream.<br />
* Upon completion of the open operation, an NSStreamEventOpenCompleted
* event is sent to the recevier's delegate.<br />
* Repeated calls to this method on the same stream are quietly ignored.
*/
- (void) open;
@ -91,12 +95,17 @@ typedef enum {
/**
* Removes the receiver from the NSRunLoop specified by aRunLoop
* running in the mode.
* running in the mode.<br />
* Attempts to remove the receiver from a run loop or a mode in
* which it has not been scheduled are quietly ignored.
*/
- (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode;
/**
* Schedules the receiver on aRunLoop using the specified mode.
* Schedules the receiver on aRunLoop using the specified mode.<br />
* You must not attempt to add a stream to more than one run loop,
* but you may call this method multiple times to add the receiver
* in different modes for the same run loop.
*/
- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode;
@ -262,14 +271,17 @@ typedef enum {
* that is a stream that binds to a socket and accepts incoming connections
*/
@interface GSServerStream : NSStream
/**
* Createe a ip (ipv6) server stream
*/
+ (id) serverStreamToAddr: (NSString*)addr port: (int)port;
/**
* Create a local (unix domain) server stream
* Create a local (unix domain or named pipe) server stream
*/
+ (id) serverStreamToAddr: (NSString*)addr;
/**
* This is the method that accepts a connection and generates two streams
* as the server side inputStream and OutputStream.
@ -283,8 +295,10 @@ typedef enum {
* the designated initializer for a ip (ipv6) server stream
*/
- (id) initToAddr: (NSString*)addr port: (int)port;
/**
* the designated initializer for a local (unix domain) server stream
* the designated initializer for a local (unix domain or named pipe)
* server stream
*/
- (id) initToAddr: (NSString*)addr;

View file

@ -28,7 +28,7 @@
NSStream
|-- NSInputStream
| `--GSInputStream
| |-- GSMemoryInputStream
| |-- GSDataInputStream
| |-- GSFileInputStream
| `-- GSSocketInputStream
| |-- GSInetInputStream
@ -36,7 +36,8 @@
| `-- GSInet6InputStream
|-- NSOutputStream
| `--GSOutputStream
| |-- GSMemoryOutputStream
| |-- GSBufferOutputStream
| |-- GSDataOutputStream
| |-- GSFileOutputStream
| `-- GSSocketOutputStream
| |-- GSInetOutputStream
@ -144,7 +145,7 @@ IVARS
/**
* The concrete subclass of NSInputStream that reads from the memory
*/
@interface GSMemoryInputStream : GSInputStream
@interface GSDataInputStream : GSInputStream
{
@private
NSData *_data;
@ -158,14 +159,30 @@ IVARS
@end
/**
* The concrete subclass of NSOutputStream that writes to memory
* The concrete subclass of NSOutputStream that writes to a buffer
*/
@interface GSMemoryOutputStream : GSOutputStream
@interface GSBufferOutputStream : GSOutputStream
{
@private
uint8_t *_buffer;
unsigned _capacity;
unsigned long _pointer;
}
/**
* this is the bridge method for asynchronized operation. Do not call.
*/
- (void) _dispatch;
@end
/**
* The concrete subclass of NSOutputStream that writes to a variable sise buffer
*/
@interface GSDataOutputStream : GSOutputStream
{
@private
NSMutableData *_data;
unsigned long _pointer;
BOOL _fixedSize;
}
/**

View file

@ -31,6 +31,7 @@
#include <Foundation/NSError.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSHost.h>
#include <Foundation/NSDebug.h>
#include "GSStream.h"
@ -132,9 +133,14 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (void) close
{
NSAssert(_currentStatus != NSStreamStatusNotOpen
&& _currentStatus != NSStreamStatusClosed,
@"Attempt to close a stream not yet opened.");
if (_currentStatus == NSStreamStatusNotOpen)
{
NSDebugMLog(@"Attempt to close unopened stream %@", self);
}
if (_currentStatus == NSStreamStatusClosed)
{
NSDebugMLog(@"Attempt to close already closed stream %@", self);
}
if (_runloop)
{
unsigned i = [_modes count];
@ -186,9 +192,10 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (void) open
{
NSAssert(_currentStatus == NSStreamStatusNotOpen
|| _currentStatus == NSStreamStatusOpening,
@"Attempt to open a stream already opened.");
if (_currentStatus != NSStreamStatusNotOpen)
{
NSDebugMLog(@"Attempt to re-open stream %@", self);
}
[self _setStatus: NSStreamStatusOpen];
if (_runloop)
{
@ -364,9 +371,6 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (void) _sendEvent: (NSStreamEvent)event
{
NSStreamStatus last = [self streamStatus];
NSStreamStatus current;
if (event == NSStreamEventNone)
{
return;
@ -454,21 +458,6 @@ static RunLoopEventType typeForStream(NSStream *aStream)
[NSException raise: NSInvalidArgumentException
format: @"Unknown event (%d) passed to _sendEvent:", event];
}
/* If our status changed while the handler was dealing with an
* event, we may need to send it the new event to let it know.
*/
if ((current = [self streamStatus]) != last)
{
if (current == NSStreamStatusAtEnd)
{
[self _sendEvent: NSStreamEventEndEncountered];
}
else if (current == NSStreamStatusError)
{
[self _sendEvent: NSStreamEventErrorOccurred];
}
}
}
- (void) _setLoopID: (void *)ref
@ -493,18 +482,35 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (BOOL) runLoopShouldBlock: (BOOL*)trigger
{
if ([self _unhandledData] == YES
|| _currentStatus == NSStreamStatusError
|| _currentStatus == NSStreamStatusAtEnd)
if (_events
& (NSStreamEventHasBytesAvailable | NSStreamEventHasSpaceAvailable))
{
/* If we have an unhandled data event, we should not watch for more
* or trigger until the appropriate rad or write has been done.
* If an error has occurred, we should not watch for any events at all.
* or trigger until the appropriate read or write has been done.
*/
*trigger = NO;
return NO;
}
else if (_loopID == (void*)self)
if (_currentStatus == NSStreamStatusError &&
(_events & NSStreamEventErrorOccurred) == NSStreamEventErrorOccurred)
{
/* If an error has occurred (and been handled),
* we should not watch for any events at all.
*/
*trigger = NO;
return NO;
}
if (_currentStatus == NSStreamStatusAtEnd &&
(_events & NSStreamEventEndEncountered) == NSStreamEventEndEncountered)
{
/* If an error has occurred (and been handled),
* we should not watch for any events at all.
*/
*trigger = NO;
return NO;
}
if (_loopID == (void*)self)
{
/* If _loopID is the receiver, the stream is not receiving external
* input, so it must trigger an event when the loop runs and must not
@ -522,6 +528,7 @@ static RunLoopEventType typeForStream(NSStream *aStream)
@end
@implementation GSInputStream
+ (void) initialize
{
if (self == [GSInputStream class])
@ -529,9 +536,31 @@ static RunLoopEventType typeForStream(NSStream *aStream)
GSObjCAddClassBehavior(self, [GSStream class]);
}
}
- (BOOL) hasBytesAvailable
{
if (_currentStatus == NSStreamStatusOpen)
{
return YES;
}
if (_currentStatus == NSStreamStatusAtEnd)
{
if ((_events & NSStreamEventEndEncountered) == 0)
{
/* We have not sent the appropriate event yet, so the
* client must not have issued a read:maxLength:
* (which is the point at which we should send).
*/
return YES;
}
}
return NO;
}
@end
@implementation GSOutputStream
+ (void) initialize
{
if (self == [GSOutputStream class])
@ -539,6 +568,27 @@ static RunLoopEventType typeForStream(NSStream *aStream)
GSObjCAddClassBehavior(self, [GSStream class]);
}
}
- (BOOL) hasSpaceAvailable
{
if (_currentStatus == NSStreamStatusOpen)
{
return YES;
}
if (_currentStatus == NSStreamStatusAtEnd)
{
if ((_events & NSStreamEventEndEncountered) == 0)
{
/* We have not sent the appropriate event yet, so the
* client must not have issued a write:maxLength:
* (which is the point at which we should send).
*/
return YES;
}
}
return NO;
}
@end
@implementation GSAbstractServerStream
+ (void) initialize
@ -551,7 +601,7 @@ static RunLoopEventType typeForStream(NSStream *aStream)
@end
@implementation GSMemoryInputStream
@implementation GSDataInputStream
/**
* the designated initializer
@ -576,10 +626,28 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
{
unsigned long dataSize = [_data length];
unsigned long dataSize;
unsigned long copySize;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte read write requested"];
}
_events &= ~NSStreamEventHasSpaceAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
dataSize = [_data length];
NSAssert(dataSize >= _pointer, @"Buffer overflow!");
if (len + _pointer > dataSize)
{
@ -597,6 +665,7 @@ static RunLoopEventType typeForStream(NSStream *aStream)
else
{
[self _setStatus: NSStreamStatusAtEnd];
[self _sendEvent: NSStreamEventEndEncountered];
}
return copySize;
}
@ -639,71 +708,132 @@ static RunLoopEventType typeForStream(NSStream *aStream)
@end
@implementation GSMemoryOutputStream
@implementation GSBufferOutputStream
- (id) initToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
{
if ((self = [super init]) != nil)
{
if (!buffer)
{
_data = [NSMutableData new];
_fixedSize = NO;
}
else
{
_data = [[NSMutableData alloc] initWithBytesNoCopy: buffer
length: capacity freeWhenDone: NO];
_fixedSize = YES;
}
_buffer = buffer;
_capacity = capacity;
_pointer = 0;
}
return self;
}
- (void) dealloc
{
RELEASE(_data);
[super dealloc];
}
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
{
_events &= ~NSStreamEventHasBytesAvailable;
if (_fixedSize)
if (buffer == 0)
{
unsigned long dataLen = [_data length];
uint8_t *origin = (uint8_t *)[_data mutableBytes];
if (_pointer+len>dataLen)
len = dataLen - _pointer;
memcpy(origin+_pointer, buffer, len);
_pointer = _pointer + len;
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
else
[_data appendBytes: buffer length: len];
return len;
}
- (BOOL) hasSpaceAvailable
{
if (_fixedSize)
return [_data length]>_pointer;
else
return YES;
_events &= ~NSStreamEventHasBytesAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
if ((_pointer + len) > _capacity)
{
len = _capacity - _pointer;
if (len == 0)
{
[self _setStatus: NSStreamStatusAtEnd];
[self _sendEvent: NSStreamEventEndEncountered];
return 0;
}
}
memcpy((_buffer + _pointer), buffer, len);
_pointer += len;
return len;
}
- (id) propertyForKey: (NSString *)key
{
if ([key isEqualToString: NSStreamFileCurrentOffsetKey])
{
if (_fixedSize)
return [NSNumber numberWithLong: _pointer];
else
return [NSNumber numberWithLong:[_data length]];
return [NSNumber numberWithLong: _pointer];
}
return [super propertyForKey: key];
}
- (void) _dispatch
{
BOOL av = [self hasSpaceAvailable];
NSStreamEvent myEvent = av ? NSStreamEventHasSpaceAvailable :
NSStreamEventEndEncountered;
[self _sendEvent: myEvent];
}
@end
@implementation GSDataOutputStream
- (id) init
{
if ((self = [super init]) != nil)
{
_data = [NSMutableData new];
_pointer = 0;
}
return self;
}
- (void) dealloc
{
RELEASE(_data);
[super dealloc];
}
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
{
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
_events &= ~NSStreamEventHasBytesAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
[_data appendBytes: buffer length: len];
_pointer += len;
return len;
}
- (BOOL) hasSpaceAvailable
{
return YES;
}
- (id) propertyForKey: (NSString *)key
{
if ([key isEqualToString: NSStreamFileCurrentOffsetKey])
{
return [NSNumber numberWithLong: _pointer];
}
else if ([key isEqualToString: NSStreamDataWrittenToMemoryStreamKey])
{
return _data;
}
else if ([key isEqualToString: NSStreamDataWrittenToMemoryStreamKey])
return _data;
return [super propertyForKey: key];
}

View file

@ -297,7 +297,24 @@ static void setNonblocking(int fd)
{
int readLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte read write requested"];
}
_events &= ~NSStreamEventHasBytesAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
readLen = read((intptr_t)_loopID, buffer, len);
if (readLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError];
@ -475,7 +492,24 @@ static void setNonblocking(int fd)
{
int readLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte read write requested"];
}
_events &= ~NSStreamEventHasBytesAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
readLen = read((intptr_t)_loopID, buffer, len);
if (readLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError];
@ -676,7 +710,24 @@ static void setNonblocking(int fd)
{
int writeLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
_events &= ~NSStreamEventHasSpaceAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
writeLen = write((intptr_t)_loopID, buffer, len);
if (writeLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError];
@ -790,7 +841,24 @@ static void setNonblocking(int fd)
{
int writeLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
_events &= ~NSStreamEventHasSpaceAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
writeLen = write((intptr_t)_loopID, buffer, len);
if (writeLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError];
@ -1186,7 +1254,7 @@ static void setNonblocking(int fd)
+ (id) inputStreamWithData: (NSData *)data
{
return AUTORELEASE([[GSMemoryInputStream alloc] initWithData: data]);
return AUTORELEASE([[GSDataInputStream alloc] initWithData: data]);
}
+ (id) inputStreamWithFileAtPath: (NSString *)path
@ -1194,24 +1262,6 @@ static void setNonblocking(int fd)
return AUTORELEASE([[GSFileInputStream alloc] initWithFileAtPath: path]);
}
- (id) initWithData: (NSData *)data
{
RELEASE(self);
return [[GSMemoryInputStream alloc] initWithData: data];
}
- (id) initWithFileAtPath: (NSString *)path
{
RELEASE(self);
return [[GSFileInputStream alloc] initWithFileAtPath: path];
}
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
{
[self subclassResponsibility: _cmd];
return -1;
}
- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len
{
[self subclassResponsibility: _cmd];
@ -1224,19 +1274,31 @@ static void setNonblocking(int fd)
return NO;
}
- (id) initWithData: (NSData *)data
{
RELEASE(self);
return [[GSDataInputStream alloc] initWithData: data];
}
- (id) initWithFileAtPath: (NSString *)path
{
RELEASE(self);
return [[GSFileInputStream alloc] initWithFileAtPath: path];
}
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
{
[self subclassResponsibility: _cmd];
return -1;
}
@end
@implementation NSOutputStream
+ (id) outputStreamToMemory
{
return AUTORELEASE([[GSMemoryOutputStream alloc]
initToBuffer: NULL capacity: 0]);
}
+ (id) outputStreamToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
{
return AUTORELEASE([[GSMemoryOutputStream alloc]
return AUTORELEASE([[GSBufferOutputStream alloc]
initToBuffer: buffer capacity: capacity]);
}
@ -1246,16 +1308,21 @@ static void setNonblocking(int fd)
initToFileAtPath: path append: shouldAppend]);
}
- (id) initToMemory
+ (id) outputStreamToMemory
{
RELEASE(self);
return [[GSMemoryOutputStream alloc] initToBuffer: NULL capacity: 0];
return AUTORELEASE([[GSDataOutputStream alloc] init]);
}
- (BOOL) hasSpaceAvailable
{
[self subclassResponsibility: _cmd];
return NO;
}
- (id) initToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
{
RELEASE(self);
return [[GSMemoryOutputStream alloc] initToBuffer: buffer capacity: capacity];
return [[GSBufferOutputStream alloc] initToBuffer: buffer capacity: capacity];
}
- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend
@ -1265,18 +1332,18 @@ static void setNonblocking(int fd)
append: shouldAppend];
}
- (id) initToMemory
{
RELEASE(self);
return [[GSDataOutputStream alloc] init];
}
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
{
[self subclassResponsibility: _cmd];
return -1;
}
- (BOOL) hasSpaceAvailable
{
[self subclassResponsibility: _cmd];
return NO;
}
@end
@implementation GSServerStream

View file

@ -41,6 +41,7 @@
#include <Foundation/NSValue.h>
#include <Foundation/NSHost.h>
#include <Foundation/NSProcessInfo.h>
#include <Foundation/NSDebug.h>
#include "../GSStream.h"
@ -73,6 +74,7 @@ typedef int socklen_t;
unsigned want; // Amount of data we want to read.
DWORD size; // Number of bytes returned by read.
GSPipeOutputStream *_sibling;
BOOL hadEOF;
}
- (NSStreamStatus) _check;
- (void) _queue;
@ -154,6 +156,8 @@ typedef int socklen_t;
unsigned want;
DWORD size;
GSPipeInputStream *_sibling;
BOOL closing;
BOOL writtenEOF;
}
- (NSStreamStatus) _check;
- (void) _queue;
@ -298,13 +302,6 @@ static void setNonblocking(SOCKET fd)
return NO;
}
- (BOOL) hasBytesAvailable
{
if ([self _isOpened] && [self streamStatus] != NSStreamStatusAtEnd)
return YES;
return NO;
}
- (id) initWithFileAtPath: (NSString *)path
{
if ((self = [super init]) != nil)
@ -351,7 +348,24 @@ static void setNonblocking(SOCKET fd)
{
DWORD readLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length read requested"];
}
_events &= ~NSStreamEventHasBytesAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
if (ReadFile((HANDLE)_loopID, buffer, len, &readLen, NULL) == 0)
{
[self _recordError];
@ -360,6 +374,7 @@ static void setNonblocking(SOCKET fd)
else if (readLen == 0)
{
[self _setStatus: NSStreamStatusAtEnd];
[self _sendEvent: NSStreamEventEndEncountered];
}
return (int)readLen;
}
@ -383,26 +398,43 @@ static void setNonblocking(SOCKET fd)
- (void) close
{
if (want > 0 && handle != INVALID_HANDLE_VALUE)
{
want = 0;
CancelIo(handle);
}
length = offset = 0;
if (_loopID != INVALID_HANDLE_VALUE)
{
CloseHandle((HANDLE)_loopID);
}
if (handle != INVALID_HANDLE_VALUE && [_sibling _isOpened] == NO)
if (handle != INVALID_HANDLE_VALUE)
{
if (CloseHandle(handle) == 0)
/* If we have an outstanding read in progess, we must cancel it
* before closing the pipe.
*/
if (want > 0)
{
[self _recordError];
want = 0;
CancelIo(handle);
}
/* We can only close the pipe if there is no sibling using it.
*/
if ([_sibling _isOpened] == NO)
{
if (DisconnectNamedPipe(handle) == 0)
{
if ((errno = GetLastError()) != ERROR_PIPE_NOT_CONNECTED)
{
[self _recordError];
}
}
if (CloseHandle(handle) == 0)
{
[self _recordError];
}
}
handle = INVALID_HANDLE_VALUE;
}
length = offset = 0;
[super close];
handle = INVALID_HANDLE_VALUE;
_loopID = (void*)INVALID_HANDLE_VALUE;
}
- (void) dealloc
@ -426,13 +458,6 @@ static void setNonblocking(SOCKET fd)
return NO;
}
- (BOOL) hasBytesAvailable
{
if ([self streamStatus] == NSStreamStatusOpen)
return YES;
return NO;
}
- (id) init
{
if ((self = [super init]) != nil)
@ -459,8 +484,8 @@ static void setNonblocking(SOCKET fd)
if (GetOverlappedResult(handle, &ov, &size, TRUE) == 0)
{
errno = GetLastError();
if (errno == ERROR_HANDLE_EOF
if ((errno = GetLastError()) == ERROR_HANDLE_EOF
|| errno == ERROR_PIPE_NOT_CONNECTED
|| errno == ERROR_BROKEN_PIPE)
{
/*
@ -469,6 +494,7 @@ static void setNonblocking(SOCKET fd)
*/
offset = length = want = 0;
[self _setStatus: NSStreamStatusOpen];
hadEOF = YES;
}
else if (errno != ERROR_IO_PENDING)
{
@ -479,6 +505,12 @@ static void setNonblocking(SOCKET fd)
[self _recordError];
}
}
else if (size == 0)
{
length = want = 0;
[self _setStatus: NSStreamStatusOpen];
hadEOF = YES;
}
else
{
/*
@ -492,7 +524,7 @@ static void setNonblocking(SOCKET fd)
- (void) _queue
{
if ([self streamStatus] == NSStreamStatusOpen)
if (hadEOF == NO && [self streamStatus] == NSStreamStatusOpen)
{
int rc;
@ -508,14 +540,14 @@ static void setNonblocking(SOCKET fd)
length = size;
if (length == 0)
{
[self _setStatus: NSStreamStatusAtEnd];
hadEOF = YES;
}
}
else if ((errno = GetLastError()) == ERROR_HANDLE_EOF
|| errno == ERROR_PIPE_NOT_CONNECTED
|| errno == ERROR_BROKEN_PIPE)
{
offset = length = 0;
[self _setStatus: NSStreamStatusOpen]; // Read of zero length
hadEOF = YES;
}
else if (errno != ERROR_IO_PENDING)
{
@ -530,24 +562,38 @@ static void setNonblocking(SOCKET fd)
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
{
NSStreamStatus myStatus = [self streamStatus];
NSStreamStatus myStatus;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length read requested"];
}
_events &= ~NSStreamEventHasBytesAvailable;
myStatus = [self streamStatus];
if (myStatus == NSStreamStatusReading)
{
myStatus = [self _check];
}
if (myStatus == NSStreamStatusAtEnd)
if (myStatus == NSStreamStatusClosed)
{
return 0; // At EOF
}
if (len <= 0
|| (myStatus != NSStreamStatusReading && myStatus != NSStreamStatusOpen))
{
return -1; // Bad length or status
return 0;
}
if (offset == length)
{
if (myStatus == NSStreamStatusError)
{
[self _sendEvent: NSStreamEventErrorOccurred];
return -1; // Waiting for read.
}
if (myStatus == NSStreamStatusOpen)
{
/*
@ -555,10 +601,11 @@ static void setNonblocking(SOCKET fd)
* so we must be at EOF.
*/
[self _setStatus: NSStreamStatusAtEnd];
return 0;
[self _sendEvent: NSStreamEventEndEncountered];
}
return -1; // Waiting for read.
return 0;
}
/*
* We already have data buffered ... return some or all of it.
*/
@ -775,7 +822,24 @@ static void setNonblocking(SOCKET fd)
{
int readLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length read requested"];
}
_events &= ~NSStreamEventHasBytesAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
readLen = recv(_sock, buffer, len, 0);
if (readLen == SOCKET_ERROR)
{
@ -806,13 +870,6 @@ static void setNonblocking(SOCKET fd)
return NO;
}
- (BOOL) hasBytesAvailable
{
if ([self streamStatus] == NSStreamStatusOpen)
return YES;
return NO;
}
- (void) _dispatch
{
/*
@ -995,13 +1052,6 @@ static void setNonblocking(SOCKET fd)
[super dealloc];
}
- (BOOL) hasSpaceAvailable
{
if ([self streamStatus] == NSStreamStatusOpen)
return YES;
return NO;
}
- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend
{
if ((self = [super init]) != nil)
@ -1056,7 +1106,24 @@ static void setNonblocking(SOCKET fd)
{
DWORD writeLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
_events &= ~NSStreamEventHasSpaceAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
if (_shouldAppend == YES)
{
SetFilePointer((HANDLE)_loopID, 0, 0, FILE_END);
@ -1084,21 +1151,67 @@ static void setNonblocking(SOCKET fd)
- (void) close
{
/* If we have a write in progress, we must wait for it to complete,
* so we just set a flag to close as soon as the write finishes.
*/
if ([self streamStatus] == NSStreamStatusWriting)
{
closing = YES;
return;
}
/* Where we have a sibling, we can't close the pipe handle, so the
* only way to tell the remote end we have finished is to write a
* zero length packet to it.
*/
if ([_sibling _isOpened] == YES && writtenEOF == NO)
{
int rc;
writtenEOF = YES;
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = (HANDLE)_loopID;
size = 0;
rc = WriteFile(handle, "", 0, &size, &ov);
if (rc == 0)
{
if ((errno = GetLastError()) == ERROR_IO_PENDING)
{
[self _setStatus: NSStreamStatusWriting];
return; // Wait for write to complete
}
[self _recordError]; // Failed to write EOF
}
}
offset = want = 0;
if (_loopID != INVALID_HANDLE_VALUE)
{
CloseHandle((HANDLE)_loopID);
}
if (handle != INVALID_HANDLE_VALUE && [_sibling _isOpened] == NO)
if (handle != INVALID_HANDLE_VALUE)
{
if (CloseHandle(handle) == 0)
if ([_sibling _isOpened] == NO)
{
[self _recordError];
if (DisconnectNamedPipe(handle) == 0)
{
if ((errno = GetLastError()) != ERROR_PIPE_NOT_CONNECTED)
{
[self _recordError];
}
[self _recordError];
}
if (CloseHandle(handle) == 0)
{
[self _recordError];
}
}
handle = INVALID_HANDLE_VALUE;
}
offset = want = 0;
[super close];
_loopID = (void*)INVALID_HANDLE_VALUE;
handle = INVALID_HANDLE_VALUE;
}
- (void) dealloc
@ -1112,13 +1225,6 @@ static void setNonblocking(SOCKET fd)
[super dealloc];
}
- (BOOL) hasSpaceAvailable
{
if ([self streamStatus] == NSStreamStatusOpen)
return YES;
return NO;
}
- (id) init
{
if ((self = [super init]) != nil)
@ -1179,19 +1285,33 @@ static void setNonblocking(SOCKET fd)
{
NSStreamStatus myStatus = [self streamStatus];
_events &= ~NSStreamEventHasSpaceAvailable;
if (len < 0)
if (buffer == 0)
{
return -1;
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
_events &= ~NSStreamEventHasSpaceAvailable;
if (myStatus == NSStreamStatusWriting)
{
myStatus = [self _check];
}
if (myStatus == NSStreamStatusClosed)
{
return 0;
}
if ((myStatus != NSStreamStatusOpen && myStatus != NSStreamStatusWriting))
{
return -1;
}
if (len > (sizeof(data) - offset))
{
len = sizeof(data) - offset;
@ -1231,6 +1351,10 @@ static void setNonblocking(SOCKET fd)
offset = want = 0;
}
}
if (closing == YES && [self streamStatus] != NSStreamStatusWriting)
{
[self close];
}
return [self streamStatus];
}
@ -1313,7 +1437,7 @@ static void setNonblocking(SOCKET fd)
_sibling = sibling;
}
-(void)setPassive: (BOOL)passive
-(void) setPassive: (BOOL)passive
{
_passive = passive;
}
@ -1354,7 +1478,24 @@ static void setNonblocking(SOCKET fd)
{
int writeLen;
if (buffer == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"null pointer for buffer"];
}
if (len == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"zero byte length write requested"];
}
_events &= ~NSStreamEventHasSpaceAvailable;
if ([self streamStatus] == NSStreamStatusClosed)
{
return 0;
}
writeLen = send(_sock, buffer, len, 0);
if (writeLen == SOCKET_ERROR)
{
@ -1376,13 +1517,6 @@ static void setNonblocking(SOCKET fd)
return writeLen;
}
- (BOOL) hasSpaceAvailable
{
if ([self streamStatus] == NSStreamStatusOpen)
return YES;
return NO;
}
- (void) open
{
// could be opened because of sibling
@ -1691,16 +1825,37 @@ static void setNonblocking(SOCKET fd)
SECURITY_ATTRIBUTES saAttr;
HANDLE handle;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if ([path length] == 0)
{
NSDebugMLog(@"address nil or empty");
goto done;
}
if ([path length] > 240)
{
NSDebugMLog(@"address (%@) too long", path);
goto done;
}
if ([path rangeOfString: @"\\"].length > 0)
{
NSDebugMLog(@"illegal backslash in (%@)", path);
goto done;
}
if ([path rangeOfString: @"/"].length > 0)
{
NSDebugMLog(@"illegal slash in (%@)", path);
goto done;
}
/*
* We allocate a new within the local pipe area
*/
name = [[@"\\\\.\\pipe\\" stringByAppendingString: path]
name = [[@"\\\\.\\pipe\\GSLocal" stringByAppendingString: path]
fileSystemRepresentation];
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
handle = CreateFileW(name,
GENERIC_WRITE|GENERIC_READ,
0,
@ -1720,15 +1875,17 @@ static void setNonblocking(SOCKET fd)
outs = AUTORELEASE([GSPipeOutputStream new]);
[ins _setHandle: handle];
[ins setSibling: outs];
[outs _setHandle: handle];
[outs setSibling: ins];
done:
if (inputStream)
{
[ins setSibling: outs];
*inputStream = ins;
}
if (outputStream)
{
[outs setSibling: ins];
*outputStream = outs;
}
}
@ -1875,7 +2032,7 @@ static void setNonblocking(SOCKET fd)
+ (id) inputStreamWithData: (NSData *)data
{
return AUTORELEASE([[GSMemoryInputStream alloc] initWithData: data]);
return AUTORELEASE([[GSDataInputStream alloc] initWithData: data]);
}
+ (id) inputStreamWithFileAtPath: (NSString *)path
@ -1883,24 +2040,6 @@ static void setNonblocking(SOCKET fd)
return AUTORELEASE([[GSFileInputStream alloc] initWithFileAtPath: path]);
}
- (id) initWithData: (NSData *)data
{
RELEASE(self);
return [[GSMemoryInputStream alloc] initWithData: data];
}
- (id) initWithFileAtPath: (NSString *)path
{
RELEASE(self);
return [[GSFileInputStream alloc] initWithFileAtPath: path];
}
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
{
[self subclassResponsibility: _cmd];
return -1;
}
- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len
{
[self subclassResponsibility: _cmd];
@ -1913,19 +2052,36 @@ static void setNonblocking(SOCKET fd)
return NO;
}
- (id) initWithData: (NSData *)data
{
RELEASE(self);
return [[GSDataInputStream alloc] initWithData: data];
}
- (id) initWithFileAtPath: (NSString *)path
{
RELEASE(self);
return [[GSFileInputStream alloc] initWithFileAtPath: path];
}
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
{
[self subclassResponsibility: _cmd];
return -1;
}
@end
@implementation NSOutputStream
+ (id) outputStreamToMemory
{
return AUTORELEASE([[GSMemoryOutputStream alloc]
initToBuffer: NULL capacity: 0]);
return AUTORELEASE([[GSDataOutputStream alloc] init]);
}
+ (id) outputStreamToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
{
return AUTORELEASE([[GSMemoryOutputStream alloc]
return AUTORELEASE([[GSBufferOutputStream alloc]
initToBuffer: buffer capacity: capacity]);
}
@ -1935,16 +2091,16 @@ static void setNonblocking(SOCKET fd)
initToFileAtPath: path append: shouldAppend]);
}
- (id) initToMemory
- (BOOL) hasSpaceAvailable
{
RELEASE(self);
return [[GSMemoryOutputStream alloc] initToBuffer: NULL capacity: 0];
[self subclassResponsibility: _cmd];
return NO;
}
- (id) initToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
{
RELEASE(self);
return [[GSMemoryOutputStream alloc] initToBuffer: buffer capacity: capacity];
return [[GSBufferOutputStream alloc] initToBuffer: buffer capacity: capacity];
}
- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend
@ -1954,18 +2110,18 @@ static void setNonblocking(SOCKET fd)
append: shouldAppend];
}
- (id) initToMemory
{
RELEASE(self);
return [[GSDataOutputStream alloc] init];
}
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
{
[self subclassResponsibility: _cmd];
return -1;
}
- (BOOL) hasSpaceAvailable
{
[self subclassResponsibility: _cmd];
return NO;
}
@end
@implementation GSServerStream
@ -2213,9 +2369,30 @@ static void setNonblocking(SOCKET fd)
- (id) initToAddr: (NSString*)addr
{
if ([addr length] == 0)
{
NSDebugMLog(@"address nil or empty");
DESTROY(self);
}
if ([addr length] > 246)
{
NSDebugMLog(@"address (%@) too long", addr);
DESTROY(self);
}
if ([addr rangeOfString: @"\\"].length > 0)
{
NSDebugMLog(@"illegal backslash in (%@)", addr);
DESTROY(self);
}
if ([addr rangeOfString: @"/"].length > 0)
{
NSDebugMLog(@"illegal slash in (%@)", addr);
DESTROY(self);
}
if ((self = [super init]) != nil)
{
path = RETAIN([@"\\\\.\\pipe\\" stringByAppendingString: addr]);
path = RETAIN([@"\\\\.\\pipe\\GSLocal" stringByAppendingString: addr]);
_loopID = INVALID_HANDLE_VALUE;
handle = INVALID_HANDLE_VALUE;
}
@ -2245,7 +2422,7 @@ static void setNonblocking(SOCKET fd)
handle = CreateNamedPipeW([path fileSystemRepresentation],
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,
PIPE_TYPE_MESSAGE,
PIPE_UNLIMITED_INSTANCES,
BUFSIZ*64,
BUFSIZ*64,