From: Andrew Ruder <[EMAIL PROTECTED]> To: [EMAIL PROTECTED] Cc: GNUstep Developers <gnustep-dev@gnu.org> Subject: [PATCH] Importing original GNUstep NSStream/GSStream Date: Tue, 24 Apr 2007 20:47:58 -0500 Message-Id: <[EMAIL PROTECTED]> X-Mailer: git-send-email 1.5.2.rc0.14.g520d-dirty In-Reply-To: <[EMAIL PROTECTED]> References: <[EMAIL PROTECTED]>
From: GNUstep Developers <gnustep-dev@gnu.org> diff --git a/libs/net/GSStream.h b/libs/net/GSStream.h index 19a1d36..34792c9 100644 --- a/libs/net/GSStream.h +++ b/libs/net/GSStream.h @@ -75,7 +75,6 @@ NSRunLoop *_runloop; /* currently scheduled loop. */\ void *_loopID; /* file descriptor etc. */\ int _events; /* Signalled events. */\ - void *_fd; \ } /** @@ -135,54 +134,14 @@ IVARS IVARS @end [EMAIL PROTECTED] GSInputStream (Private) -- (BOOL) _isOpened; -- (void) _sendEvent: (NSStreamEvent)event; -- (void) _setStatus: (NSStreamStatus)newStatus; -- (void) _setFd: (void*)fd; -- (void) _recordError; [EMAIL PROTECTED] - @interface GSOutputStream : NSOutputStream IVARS @end [EMAIL PROTECTED] GSOutputStream (Private) -- (BOOL) _isOpened; -- (void) _sendEvent: (NSStreamEvent)event; -- (void) _setStatus: (NSStreamStatus)newStatus; -- (void) _setFd: (void*)fd; -- (void) _recordError; [EMAIL PROTECTED] - @interface GSAbstractServerStream : GSServerStream IVARS @end [EMAIL PROTECTED] GSAbstractServerStream (private) -- (BOOL) _isOpened; -- (void) _sendEvent: (NSStreamEvent)event; -- (void) _setStatus: (NSStreamStatus)newStatus; -- (void) _setFd: (void*)fd; -- (void) _recordError; [EMAIL PROTECTED] - -/** - * The concrete subclass of NSInputStream that reads from the memory - */ [EMAIL PROTECTED] GSMemoryInputStream : GSInputStream -{ [EMAIL PROTECTED] - NSData *_data; - unsigned long _pointer; -} - -/** - * this is the bridge method for asynchronized operation. Do not call. - */ -- (void) _dispatch; [EMAIL PROTECTED] - /** * The concrete subclass of NSInputStream that reads from the memory */ @@ -232,23 +191,5 @@ IVARS - (void) _dispatch; @end -/** - * The concrete subclass of NSOutputStream that writes to memory - */ [EMAIL PROTECTED] GSMemoryOutputStream : GSOutputStream -{ [EMAIL PROTECTED] - NSMutableData *_data; - unsigned long _pointer; - BOOL _fixedSize; -} - -/** - * this is the bridge method for asynchronized operation. Do not call. - */ -- (void) _dispatch; [EMAIL PROTECTED] - - #endif diff --git a/libs/net/GSStream.m b/libs/net/GSStream.m index c2023d1..8c1184f 100644 --- a/libs/net/GSStream.m +++ b/libs/net/GSStream.m @@ -1,6 +1,9 @@ /** Implementation for GSStream for GNUStep Copyright (C) 2006 Free Software Foundation, Inc. - Copyright (C) 2007 Contributors to the Sidestep Project. See AUTHORS file for details + + Written by: Derek Zhou <[EMAIL PROTECTED]> + Written by: Richard Frith-Macdonald <[EMAIL PROTECTED]> + Date: 2006 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -225,10 +228,7 @@ static RunLoopEventType typeForStream(NSStream *aStream) { if ([_modes containsObject: mode]) { - if ([self _isOpened]) - { - [_runloop removeStream: self mode: mode]; - } + [_runloop removeStream: self mode: mode]; [_modes removeObject: mode]; if ([_modes count] == 0) { @@ -248,6 +248,10 @@ static RunLoopEventType typeForStream(NSStream *aStream) mode = [mode copy]; [_modes addObject: mode]; RELEASE(mode); + /* We only add open streams to the runloop .. subclasses may add + * streams when they are in the process of opening if they need + * to do so. + */ if ([self _isOpened]) { [_runloop addStream: self mode: mode]; @@ -358,10 +362,9 @@ static RunLoopEventType typeForStream(NSStream *aStream) #if defined(__MINGW32__) errno = GetLastError(); #endif - //theError = [NSError errorWithDomain: @"NSPOSIX" -// code: errno -// userInfo: nil]; - theError = nil; + theError = [NSError errorWithDomain: NSPOSIXErrorDomain + code: errno + userInfo: nil]; NSLog(@"%@ error(%d): - %@", self, errno, [NSError _last]); ASSIGN(_lastError, theError); _currentStatus = NSStreamStatusError; diff --git a/libs/net/unix/NSStream.m b/libs/net/unix/NSStream.m new file mode 100644 index 0000000..92f6867 --- /dev/null +++ b/libs/net/unix/NSStream.m @@ -0,0 +1,1687 @@ +/** Implementation for NSStream for GNUStep + Copyright (C) 2006 Free Software Foundation, Inc. + + Written by: Derek Zhou <[EMAIL PROTECTED]> + Written by: Richard Frith-Macdonald <[EMAIL PROTECTED]> + Date: 2006 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111 USA. + + */ +#include <unistd.h> +#include <errno.h> + +#include <Foundation/NSData.h> +#include <Foundation/NSArray.h> +#include <Foundation/NSRunLoop.h> +#include <Foundation/NSException.h> +#include <Foundation/NSError.h> +#include <Foundation/NSValue.h> +#include <Foundation/NSHost.h> + +#include "../GSStream.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif +#ifndef PF_LOCAL +#define PF_LOCAL PF_UNIX +#endif + +#ifndef socklen_t +#define socklen_t uint32_t +#endif + +/** + * The concrete subclass of NSInputStream that reads from a file + */ [EMAIL PROTECTED] GSFileInputStream : GSInputStream +{ [EMAIL PROTECTED] + NSString *_path; +} [EMAIL PROTECTED] + +/** + * The abstract subclass of NSInputStream that reads from a socket + */ [EMAIL PROTECTED] GSSocketInputStream : GSInputStream +{ [EMAIL PROTECTED] + GSOutputStream *_sibling; + BOOL _passive; /* YES means already connected */ +} + +/** + * get the length of the socket addr + */ +- (socklen_t) sockLen; + +/** + * get the sockaddr + */ +- (struct sockaddr*) peerAddr; + +/** + * setter for sibling + */ +- (void) setSibling: (GSOutputStream*)sibling; + +/** + * setter for passive + */ +- (void) setPassive: (BOOL)passive; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInetInputStream : GSSocketInputStream +{ + @private + struct sockaddr_in _peerAddr; +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInet6InputStream : GSSocketInputStream +{ + @private +#if defined(AF_INET6) + struct sockaddr_in6 _peerAddr; +#endif +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSLocalInputStream : GSSocketInputStream +{ + @private + struct sockaddr_un _peerAddr; +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr; + [EMAIL PROTECTED] + +/** + * The concrete subclass of NSOutputStream that writes to a file + */ [EMAIL PROTECTED] GSFileOutputStream : GSOutputStream +{ [EMAIL PROTECTED] + NSString *_path; + BOOL _shouldAppend; +} [EMAIL PROTECTED] + +/** + * The concrete subclass of NSOutputStream that writes to a socket + */ [EMAIL PROTECTED] GSSocketOutputStream : GSOutputStream +{ [EMAIL PROTECTED] + GSInputStream *_sibling; + BOOL _passive; /* YES means already connected */ +} + +/** + * get the length of the socket addr + */ +- (socklen_t) sockLen; + +/** + * get the sockaddr + */ +- (struct sockaddr*) peerAddr; + +/** + * setter for sibling + */ +- (void) setSibling: (GSInputStream*)sibling; + +/** + * setter for passive + */ +- (void) setPassive: (BOOL)passive; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInetOutputStream : GSSocketOutputStream +{ + @private + struct sockaddr_in _peerAddr; +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInet6OutputStream : GSSocketOutputStream +{ + @private +#if defined(AF_INET6) + struct sockaddr_in6 _peerAddr; +#endif +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSLocalOutputStream : GSSocketOutputStream +{ + @private + struct sockaddr_un _peerAddr; +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr; + [EMAIL PROTECTED] + +/** + * The concrete subclass of NSServerStream that accept connection from a socket + */ [EMAIL PROTECTED] GSSocketServerStream : GSAbstractServerStream +/** + * return the class of the inputStream associated with this + * type of serverStream. + */ +- (Class) _inputStreamClass; +/** + * return the class of the outputStream associated with this + * type of serverStream. + */ +- (Class) _outputStreamClass; +/** + * get the length of the socket addr + */ +- (socklen_t) sockLen; +/** + * get the sockaddr + */ +- (struct sockaddr*) serverAddr; + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInetServerStream : GSSocketServerStream +{ + @private + struct sockaddr_in _serverAddr; +} [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInet6ServerStream : GSSocketServerStream +{ + @private +#if defined(AF_INET6) + struct sockaddr_in6 _serverAddr; +#endif +} [EMAIL PROTECTED] + [EMAIL PROTECTED] GSLocalServerStream : GSSocketServerStream +{ + @private + struct sockaddr_un _serverAddr; +} [EMAIL PROTECTED] + +/** + * set the file descriptor to non-blocking + */ +static void setNonblocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + [EMAIL PROTECTED] GSFileInputStream + +- (id) initWithFileAtPath: (NSString *)path +{ + if ((self = [super init]) != nil) + { + ASSIGN(_path, path); + } + return self; +} + +- (void) dealloc +{ + if ([self _isOpened]) + { + [self close]; + } + DESTROY(_path); + [super dealloc]; +} + +- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len +{ + 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]; + readLen = -1; + } + else if (readLen == 0) + { + [self _setStatus: NSStreamStatusAtEnd]; + [self _sendEvent: NSStreamEventEndEncountered]; + } + return readLen; +} + +- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len +{ + return NO; +} + +- (BOOL) hasBytesAvailable +{ + if ([self _isOpened] && [self streamStatus] != NSStreamStatusAtEnd) + return YES; + return NO; +} + +- (id) propertyForKey: (NSString *)key +{ + if ([key isEqualToString: NSStreamFileCurrentOffsetKey]) + { + off_t offset = 0; + + if ([self _isOpened]) + offset = lseek((intptr_t)_loopID, 0, SEEK_CUR); + return [NSNumber numberWithLong: offset]; + } + return [super propertyForKey: key]; +} + +- (void) open +{ + int fd; + + fd = open([_path fileSystemRepresentation], O_RDONLY|O_NONBLOCK); + if (fd < 0) + { + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } + _loopID = (void*)(intptr_t)fd; + [super open]; +} + +- (void) close +{ + int closeReturn = close((intptr_t)_loopID); + + if (closeReturn < 0) + [self _recordError]; + [super close]; +} + +- (void) _dispatch +{ + if ([self streamStatus] == NSStreamStatusOpen) + { + [self _sendEvent: NSStreamEventHasBytesAvailable]; + } + else + { + NSLog(@"_dispatch with unexpected status %d", [self streamStatus]); + } +} [EMAIL PROTECTED] + [EMAIL PROTECTED] GSSocketInputStream + +- (socklen_t) sockLen +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +- (struct sockaddr*) peerAddr +{ + [self subclassResponsibility: _cmd]; + return NULL; +} + +- (void) setSibling: (GSOutputStream*)sibling +{ + _sibling = sibling; +} + +- (void) setPassive: (BOOL)passive +{ + _passive = passive; +} + +- (id) init +{ + if ((self = [super init]) != nil) + { + // so that unopened access will fail + _sibling = nil; + _passive = NO; + } + return self; +} + +- (void) dealloc +{ + if ([self _isOpened]) + { + [self close]; + } + [(GSSocketOutputStream*)_sibling setSibling: nil]; + _sibling = nil; + [super dealloc]; +} + +- (void) open +{ + // could be opened because of sibling + if ([self _isOpened]) + return; + if (_passive || (_sibling && [_sibling _isOpened])) + goto open_ok; + // check sibling status, avoid double connect + if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) + { + [self _setStatus: NSStreamStatusOpening]; + return; + } + else + { + int result; + + if (_runloop) + { + setNonblocking((intptr_t)_loopID); + } + result = connect((intptr_t)_loopID, [self peerAddr], [self sockLen]); + if (result < 0) + { + if (errno == EINPROGRESS && _runloop != nil) + { + unsigned i = [_modes count]; + + /* + * Need to set the status first, so that the run loop can tell + * it needs to add the stream as waiting on writable, as an + * indication of opened + */ + [self _setStatus: NSStreamStatusOpening]; + while (i-- > 0) + { + [_runloop addStream: self mode: [_modes objectAtIndex: i]]; + } + return; + } + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } + } + + open_ok: + // put itself to the runloop + [super open]; + setNonblocking((intptr_t)_loopID); +} + +- (void) close +{ + // read shutdown is ignored, because the other side may shutdown first. + if (!_sibling || [_sibling streamStatus] == NSStreamStatusClosed) + close((intptr_t)_loopID); + else + shutdown((intptr_t)_loopID, SHUT_RD); + [super close]; +} + +- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len +{ + 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]; + readLen = -1; + } + else if (readLen == 0) + { + [self _setStatus: NSStreamStatusAtEnd]; + [self _sendEvent: NSStreamEventEndEncountered]; + } + return readLen; +} + +- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len +{ + return NO; +} + +- (BOOL) hasBytesAvailable +{ + if ([self _isOpened] && [self streamStatus] != NSStreamStatusAtEnd) + return YES; + return NO; +} + +- (void) _dispatch +{ + NSStreamEvent myEvent; + + if ([self streamStatus] == NSStreamStatusOpening) + { + int error; + int result; + socklen_t len = sizeof(error); + unsigned i = [_modes count]; + + while (i-- > 0) + { + [_runloop removeStream: self mode: [_modes objectAtIndex: i]]; + } + result + = getsockopt((intptr_t)_loopID, SOL_SOCKET, SO_ERROR, &error, &len); + + if (result >= 0 && !error) + { // finish up the opening + myEvent = NSStreamEventOpenCompleted; + _passive = YES; + [self open]; + // notify sibling + [_sibling open]; + [_sibling _sendEvent: myEvent]; + } + else // must be an error + { + if (error) + errno = error; + [self _recordError]; + myEvent = NSStreamEventErrorOccurred; + [_sibling _recordError]; + [_sibling _sendEvent: myEvent]; + } + } + else if ([self streamStatus] == NSStreamStatusAtEnd) + { + myEvent = NSStreamEventEndEncountered; + } + else + { + [self _setStatus: NSStreamStatusOpen]; + myEvent = NSStreamEventHasBytesAvailable; + } + [self _sendEvent: myEvent]; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInetInputStream + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin_family = AF_INET; + _peerAddr.sin_port = htons(port); + ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInet6InputStream +#if defined(AF_INET6) +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin6_family = AF_INET6; + _peerAddr.sin6_port = htons(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} +#else +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + return nil; +} +#endif [EMAIL PROTECTED] + [EMAIL PROTECTED] GSLocalInputStream + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_un); +} + +- (struct sockaddr*) peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr +{ + const char* real_addr = [addr fileSystemRepresentation]; + + if ((self = [super init]) != nil) + { + _peerAddr.sun_family = AF_LOCAL; + if (strlen(real_addr)>sizeof(_peerAddr.sun_path)-1) // too long + { + DESTROY(self); + } + else + { + strncpy(_peerAddr.sun_path, real_addr, sizeof(_peerAddr.sun_path)-1); + } + } + return self; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSFileOutputStream + +- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend +{ + if ((self = [super init]) != nil) + { + ASSIGN(_path, path); + // so that unopened access will fail + _shouldAppend = shouldAppend; + } + return self; +} + +- (void) dealloc +{ + if ([self _isOpened]) + { + [self close]; + } + RELEASE(_path); + [super dealloc]; +} + +- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len +{ + 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]; + return writeLen; +} + +- (BOOL) hasSpaceAvailable +{ + if ([self _isOpened]) + return YES; + return NO; +} + +- (void) open +{ + int fd; + int flag = O_WRONLY | O_NONBLOCK | O_CREAT; + mode_t mode = 0666; + + if (_shouldAppend) + flag = flag | O_APPEND; + else + flag = flag | O_TRUNC; + fd = open([_path fileSystemRepresentation], flag, mode); + if (fd < 0) + { // make an error + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } + _loopID = (void*)(intptr_t)fd; + [super open]; +} + +- (void) close +{ + int closeReturn = close((intptr_t)_loopID); + if (closeReturn < 0) + [self _recordError]; + [super close]; +} + +- (id) propertyForKey: (NSString *)key +{ + if ([key isEqualToString: NSStreamFileCurrentOffsetKey]) + { + off_t offset = 0; + + if ([self _isOpened]) + offset = lseek((intptr_t)_loopID, 0, SEEK_CUR); + return [NSNumber numberWithLong: offset]; + } + return [super propertyForKey: key]; +} + +- (void) _dispatch +{ + if ([self streamStatus] == NSStreamStatusOpen) + { + [self _sendEvent: NSStreamEventHasSpaceAvailable]; + } + else + { + NSLog(@"_dispatch with unexpected status %d", [self streamStatus]); + } +} [EMAIL PROTECTED] + [EMAIL PROTECTED] GSSocketOutputStream + +- (socklen_t) sockLen +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +- (struct sockaddr*) peerAddr +{ + [self subclassResponsibility: _cmd]; + return NULL; +} + +- (void) setSibling: (GSInputStream*)sibling +{ + _sibling = sibling; +} + +- (void) setPassive: (BOOL)passive +{ + _passive = passive; +} + +- (id) init +{ + if ((self = [super init]) != nil) + { + _sibling = nil; + _passive = NO; + } + return self; +} + +- (void) dealloc +{ + if ([self _isOpened]) + { + [self close]; + } + [(GSSocketInputStream*)_sibling setSibling: nil]; + _sibling = nil; + [super dealloc]; +} + +- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len +{ + 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]; + return writeLen; +} + +- (BOOL) hasSpaceAvailable +{ + if ([self _isOpened]) + return YES; + return NO; +} + +- (void) open +{ + // could be opened because of sibling + if ([self _isOpened]) + return; + if (_passive || (_sibling && [_sibling _isOpened])) + goto open_ok; + // check sibling status, avoid double connect + if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) + { + [self _setStatus: NSStreamStatusOpening]; + return; + } + else + { + int result; + + if (_runloop) + { + setNonblocking((intptr_t)_loopID); + } + result = connect((intptr_t)_loopID, [self peerAddr], [self sockLen]); + if (result < 0) + { + if (errno == EINPROGRESS && _runloop != nil) + { + unsigned i = [_modes count]; + + /* + * Need to set the status first, so that the run loop can tell + * it needs to add the stream as waiting on writable, as an + * indication of opened + */ + [self _setStatus: NSStreamStatusOpening]; + while (i-- > 0) + { + [_runloop addStream: self mode: [_modes objectAtIndex: i]]; + } + return; + } + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } + } + + open_ok: + // put itself to the runloop + [super open]; + setNonblocking((intptr_t)_loopID); +} + +- (void) close +{ + // shutdown may fail (broken pipe). Record it. + int closeReturn; + if (!_sibling || [_sibling streamStatus]==NSStreamStatusClosed) + closeReturn = close((intptr_t)_loopID); + else + closeReturn = shutdown((intptr_t)_loopID, SHUT_WR); + if (closeReturn < 0) + [self _recordError]; + [super close]; +} + +- (void) _dispatch +{ + NSStreamEvent myEvent; + + if ([self streamStatus] == NSStreamStatusOpening) + { + int error; + socklen_t len = sizeof(error); + int result; + unsigned i = [_modes count]; + + while (i-- > 0) + { + [_runloop removeStream: self mode: [_modes objectAtIndex: i]]; + } + result + = getsockopt((intptr_t)_loopID, SOL_SOCKET, SO_ERROR, &error, &len); + if (result >= 0 && !error) + { // finish up the opening + myEvent = NSStreamEventOpenCompleted; + _passive = YES; + [self open]; + // notify sibling + [_sibling open]; + [_sibling _sendEvent: myEvent]; + } + else // must be an error + { + if (error) + errno = error; + [self _recordError]; + myEvent = NSStreamEventErrorOccurred; + [_sibling _recordError]; + [_sibling _sendEvent: myEvent]; + } + } + else if ([self streamStatus] == NSStreamStatusAtEnd) + { + myEvent = NSStreamEventEndEncountered; + } + else + { + [self _setStatus: NSStreamStatusOpen]; + myEvent = NSStreamEventHasSpaceAvailable; + } + [self _sendEvent: myEvent]; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInetOutputStream + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin_family = AF_INET; + _peerAddr.sin_port = htons(port); + ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInet6OutputStream +#if defined(AF_INET6) +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin6_family = AF_INET6; + _peerAddr.sin6_port = htons(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} +#else +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + return nil; +} +#endif [EMAIL PROTECTED] + [EMAIL PROTECTED] GSLocalOutputStream + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_un); +} + +- (struct sockaddr*) peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr +{ + const char* real_addr = [addr fileSystemRepresentation]; + + if ((self = [super init]) != nil) + { + _peerAddr.sun_family = AF_LOCAL; + if (strlen(real_addr) > sizeof(_peerAddr.sun_path)-1) // too long + { + DESTROY(self); + } + else + { + strncpy(_peerAddr.sun_path, real_addr, sizeof(_peerAddr.sun_path)-1); + } + } + return self; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] NSStream + ++ (void) getStreamsToHost: (NSHost *)host + port: (int)port + inputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + NSString *address = [host address]; + GSSocketInputStream *ins = nil; + GSSocketOutputStream *outs = nil; + int sock; + + // try ipv4 first + ins = AUTORELEASE([[GSInetInputStream alloc] + initToAddr: address port: port]); + outs = AUTORELEASE([[GSInetOutputStream alloc] + initToAddr: address port: port]); + if (!ins) + { +#if defined(PF_INET6) + ins = [[GSInet6InputStream alloc] initToAddr: address + port: port]; + outs = [[GSInet6OutputStream alloc] initToAddr: address + port: port]; + sock = socket(PF_INET6, SOCK_STREAM, 0); +#else + sock = -1; +#endif + } + else + { + sock = socket(PF_INET, SOCK_STREAM, 0); + } + + NSAssert(sock >= 0, @"Cannot open socket"); + [ins _setLoopID: (void*)(intptr_t)sock]; + [outs _setLoopID: (void*)(intptr_t)sock]; + if (inputStream) + { + [ins setSibling: outs]; + *inputStream = ins; + } + if (outputStream) + { + [outs setSibling: ins]; + *outputStream = outs; + } + return; +} + ++ (void) getLocalStreamsToPath: (NSString *)path + inputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketInputStream *ins = nil; + GSSocketOutputStream *outs = nil; + int sock; + + ins = AUTORELEASE([[GSLocalInputStream alloc] initToAddr: path]); + outs = AUTORELEASE([[GSLocalOutputStream alloc] initToAddr: path]); + sock = socket(PF_LOCAL, SOCK_STREAM, 0); + + NSAssert(sock >= 0, @"Cannot open socket"); + [ins _setLoopID: (void*)(intptr_t)sock]; + [outs _setLoopID: (void*)(intptr_t)sock]; + if (inputStream) + { + [ins setSibling: outs]; + *inputStream = ins; + } + if (outputStream) + { + [outs setSibling: ins]; + *outputStream = outs; + } + return; +} + ++ (void) pipeWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketInputStream *ins = nil; + GSSocketOutputStream *outs = nil; + int fds[2]; + int pipeReturn; + + // the type of the stream does not matter, since we are only using the fd + ins = AUTORELEASE([GSLocalInputStream new]); + outs = AUTORELEASE([GSLocalOutputStream new]); + pipeReturn = pipe(fds); + + NSAssert(pipeReturn >= 0, @"Cannot open pipe"); + [ins _setLoopID: (void*)(intptr_t)fds[0]]; + [outs _setLoopID: (void*)(intptr_t)fds[1]]; + // no need to connect + [ins setPassive: YES]; + [outs setPassive: YES]; + if (inputStream) + *inputStream = ins; + if (outputStream) + *outputStream = outs; + return; +} + +- (void) close +{ + [self subclassResponsibility: _cmd]; +} + +- (void) open +{ + [self subclassResponsibility: _cmd]; +} + +- (void) setDelegate: (id)delegate +{ + [self subclassResponsibility: _cmd]; +} + +- (id) delegate +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (BOOL) setProperty: (id)property forKey: (NSString *)key +{ + [self subclassResponsibility: _cmd]; + return NO; +} + +- (id) propertyForKey: (NSString *)key +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode +{ + [self subclassResponsibility: _cmd]; +} + +- (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode; +{ + [self subclassResponsibility: _cmd]; +} + +- (NSError *) streamError +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (NSStreamStatus) streamStatus +{ + [self subclassResponsibility: _cmd]; + return 0; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] NSInputStream + ++ (id) inputStreamWithData: (NSData *)data +{ + return AUTORELEASE([[GSDataInputStream alloc] initWithData: data]); +} + ++ (id) inputStreamWithFileAtPath: (NSString *)path +{ + return AUTORELEASE([[GSFileInputStream alloc] initWithFileAtPath: path]); +} + +- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len +{ + [self subclassResponsibility: _cmd]; + return NO; +} + +- (BOOL) hasBytesAvailable +{ + [self subclassResponsibility: _cmd]; + 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; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] NSOutputStream + ++ (id) outputStreamToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity +{ + return AUTORELEASE([[GSBufferOutputStream alloc] + initToBuffer: buffer capacity: capacity]); +} + ++ (id) outputStreamToFileAtPath: (NSString *)path append: (BOOL)shouldAppend +{ + return AUTORELEASE([[GSFileOutputStream alloc] + initToFileAtPath: path append: shouldAppend]); +} + ++ (id) outputStreamToMemory +{ + return AUTORELEASE([[GSDataOutputStream alloc] init]); +} + +- (BOOL) hasSpaceAvailable +{ + [self subclassResponsibility: _cmd]; + return NO; +} + +- (id) initToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity +{ + RELEASE(self); + return [[GSBufferOutputStream alloc] initToBuffer: buffer capacity: capacity]; +} + +- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend +{ + RELEASE(self); + return [[GSFileOutputStream alloc] initToFileAtPath: path + 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; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSServerStream + ++ (id) serverStreamToAddr: (NSString*)addr port: (int)port +{ + GSServerStream *s; + + // try inet first, then inet6 + s = [[GSInetServerStream alloc] initToAddr: addr port: port]; + if (!s) + s = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; + return AUTORELEASE(s); +} + ++ (id) serverStreamToAddr: (NSString*)addr +{ + return AUTORELEASE([[GSLocalServerStream alloc] initToAddr: addr]); +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + // try inet first, then inet6 + self = [[GSInetServerStream alloc] initToAddr: addr port: port]; + if (!self) + self = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; + return self; +} + +- (id) initToAddr: (NSString*)addr +{ + RELEASE(self); + return [[GSLocalServerStream alloc] initToAddr: addr]; +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + [self subclassResponsibility: _cmd]; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSSocketServerStream + +- (Class) _inputStreamClass +{ + [self subclassResponsibility: _cmd]; + return Nil; +} + +- (Class) _outputStreamClass +{ + [self subclassResponsibility: _cmd]; + return Nil; +} + +- (void) dealloc +{ + if ([self _isOpened]) + { + [self close]; + } + [super dealloc]; +} + +- (socklen_t) sockLen +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +- (struct sockaddr*) serverAddr +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +#define SOCKET_BACKLOG 256 + +- (void) open +{ + int bindReturn; + int listenReturn; + +#ifndef BROKEN_SO_REUSEADDR + /* + * Under decent systems, SO_REUSEADDR means that the port can be reused + * immediately that this process exits. Under some it means + * that multiple processes can serve the same port simultaneously. + * We don't want that broken behavior! + */ + int status = 1; + + setsockopt((int)(intptr_t)_loopID, SOL_SOCKET, SO_REUSEADDR, + (char *)&status, sizeof(status)); +#endif + + bindReturn = bind((int)(intptr_t)_loopID, [self serverAddr], [self sockLen]); + listenReturn = listen((intptr_t)_loopID, SOCKET_BACKLOG); + if (bindReturn < 0 || listenReturn < 0) + { + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } + setNonblocking((intptr_t)_loopID); + // put itself to the runloop + [super open]; +} + +- (void) close +{ + // close a server socket is safe + close((intptr_t)_loopID); + [super close]; +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketInputStream *ins = AUTORELEASE([[self _inputStreamClass] new]); + GSSocketOutputStream *outs = AUTORELEASE([[self _outputStreamClass] new]); + socklen_t len = [ins sockLen]; + int acceptReturn = accept((intptr_t)_loopID, [ins peerAddr], &len); + + _events &= ~NSStreamEventHasBytesAvailable; + if (acceptReturn < 0) + { // test for real error + if (errno != EWOULDBLOCK +#if defined(EAGAIN) + && errno != EAGAIN +#endif +#if defined(ECONNABORTED) + && errno != ECONNABORTED +#endif +#if defined(EPROTO) + && errno != EPROTO +#endif + && errno != EINTR) + { + [self _recordError]; + } + ins = nil; + outs = nil; + } + else + { + // no need to connect again + [ins setPassive: YES]; + [outs setPassive: YES]; + // copy the addr to outs + memcpy([outs peerAddr], [ins peerAddr], len); + [ins _setLoopID: (void*)(intptr_t)acceptReturn]; + [outs _setLoopID: (void*)(intptr_t)acceptReturn]; + } + if (inputStream) + { + [ins setSibling: outs]; + *inputStream = ins; + } + if (outputStream) + { + [outs setSibling: ins]; + *outputStream = outs; + } +} + +- (void) _dispatch +{ + NSStreamEvent myEvent; + + [self _setStatus: NSStreamStatusOpen]; + myEvent = NSStreamEventHasBytesAvailable; + [self _sendEvent: myEvent]; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInetServerStream + +- (Class) _inputStreamClass +{ + return [GSInetInputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSInetOutputStream class]; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + [super init]; + _serverAddr.sin_family = AF_INET; + _serverAddr.sin_port = htons(port); + ptonReturn = inet_pton(AF_INET, addr_c, &(_serverAddr.sin_addr)); + _loopID = (void*)(intptr_t)socket(AF_INET, SOCK_STREAM, 0); + if (ptonReturn == 0 || _loopID < 0) // error + { + RELEASE(self); + return nil; + } + NSAssert(_loopID >= 0, @"cannot open socket"); + return self; +} + [EMAIL PROTECTED] + [EMAIL PROTECTED] GSInet6ServerStream +#if defined(AF_INET6) +- (Class) _inputStreamClass +{ + return [GSInet6InputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSInet6OutputStream class]; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + [super init]; + _serverAddr.sin6_family = AF_INET6; + _serverAddr.sin6_port = htons(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &(_serverAddr.sin6_addr)); + _loopID = (void*)(intptr_t)socket(AF_INET6, SOCK_STREAM, 0); + if (ptonReturn == 0 || _loopID < 0) // error + { + RELEASE(self); + return nil; + } + NSAssert(_loopID >= 0, @"cannot open socket"); + return self; +} +#else +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + return nil; +} +#endif [EMAIL PROTECTED] + [EMAIL PROTECTED] GSLocalServerStream + +- (Class) _inputStreamClass +{ + return [GSLocalInputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSLocalOutputStream class]; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_un); +} + +- (struct sockaddr*) serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr +{ + const char* real_addr = [addr fileSystemRepresentation]; + [super init]; + _serverAddr.sun_family = AF_LOCAL; + _loopID = (void *)(intptr_t)socket(AF_LOCAL, SOCK_STREAM, 0); + if (strlen(real_addr) > sizeof(_serverAddr.sun_path)-1 || _loopID < 0) + { + RELEASE(self); + return nil; + } + strncpy(_serverAddr.sun_path, real_addr, sizeof(_serverAddr.sun_path)-1); + return self; +} + [EMAIL PROTECTED] + -- 1.5.2.rc0.14.g520d-dirty _______________________________________________ Gnustep-dev mailing list Gnustep-dev@gnu.org http://lists.gnu.org/mailman/listinfo/gnustep-dev