more mingw32 updates

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@21951 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2005-11-05 05:58:43 +00:00
parent 574ac81e17
commit 32990455b2
10 changed files with 955 additions and 297 deletions

View file

@ -1,3 +1,15 @@
2005-11-05 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GSCompatibility.m: don't generate macos-x (xml) property
lists if we can't parse them.
* Source/GSHTTPURLHandle.m: debug fix for mingw32
* Headers/Foundation/NSFileManager.h: add mingw32 conditionals.
* Headers/Foundation/NSString.h: add mingw32 conditionals.
* Source/NSMessagePort.m: remove mingw32 stuff
* Source/win32/NSMessagePort.m: added partial implementation
* Source/win32/NSMessagePortNewServer.m: added skeleton
* Source/win32/GNUmakefile: build message port classes
2005-11-04 Richard Frith-Macdonald <rfm@gnu.org>
* configure.ac: Cope with backslashes in values from GNUstep.conf

View file

@ -226,7 +226,11 @@
* with unicode strings.<br />
* Raises an exception if the character conversion is not possible.
*/
#if defined(__MINGW32__)
- (const unichar*) fileSystemRepresentationWithPath: (NSString*)path;
#else
- (const char*) fileSystemRepresentationWithPath: (NSString*)path;
#endif
- (BOOL) isExecutableFileAtPath: (NSString*)path;
- (BOOL) isDeletableFileAtPath: (NSString*)path;
@ -241,8 +245,23 @@
- (NSString*) pathContentOfSymbolicLinkAtPath: (NSString*)path;
- (BOOL) removeFileAtPath: (NSString*)path
handler: (id)handler;
/**
* Convert to OpenStep internal string format from a string in
* the local filesystem format, as returned by system functions.<br />
* This representation may vary between filesystems.<br />
* On windows, the filesystem representation is 16-bit unicode and is expected
* to have come from the variant of a system call which works
* with unicode strings.
*/
#if defined(__MINGW32__)
- (NSString*) stringWithFileSystemRepresentation: (const unichar*)string
length: (unsigned int)len;
#else
- (NSString*) stringWithFileSystemRepresentation: (const char*)string
length: (unsigned int)len;
#endif
- (NSArray*) subpathsAtPath: (NSString*)path;
@end /* NSFileManager */

View file

@ -339,10 +339,14 @@ enum {
* automatically freed soon after it is returned, so copy it if you need it
* for long.<br />
* NB. On ms-windows the filesystem representation of a path is a 16-bit
* unicode character string, so you should only path the value returned by
* unicode character string, so you should only pass the value returned by
* this method to functions expecting wide characters.
*/
#if defined(__MINGW32__)
- (const unichar*) fileSystemRepresentation;
#else
- (const char*) fileSystemRepresentation;
#endif
/**
* Converts the receiver to a C string path using the character encoding
@ -350,10 +354,15 @@ enum {
* into buffer if it is shorter than size, otherwise NO is returned.<br />
* NB. On ms-windows the filesystem representation of a path is a 16-bit
* unicode character string, so the buffer you pass to this method must be
* twice as long as the number of characters you expect to receive.
* twice as many bytes as the size (number of characters) you expect to receive.
*/
#if defined(__MINGW32__)
- (BOOL) getFileSystemRepresentation: (unichar*)buffer
maxLength: (unsigned int)size;
#else
- (BOOL) getFileSystemRepresentation: (char*)buffer
maxLength: (unsigned int)size;
#endif
/**
* Returns a string containing the last path component of the receiver.<br />

View file

@ -223,6 +223,7 @@ NSZone.m \
externs.m \
objc-load.m
ifneq ($(GNUSTEP_TARGET_OS), mingw32)
BASE_MFILES += \
NSMessagePort.m \

View file

@ -50,8 +50,12 @@ BOOL GSMacOSXCompatibleGeometry(void)
BOOL GSMacOSXCompatiblePropertyLists(void)
{
#if defined(HAVE_LIBXML)
if (GSUserDefaultsFlag(NSWriteOldStylePropertyLists) == YES)
return NO;
return GSUserDefaultsFlag(GSMacOSXCompatible);
#else
return NO;
#endif
}

View file

@ -221,8 +221,13 @@ static void debugRead(GSHTTPURLHandle *handle, NSData *data)
int d;
[debugLock lock];
#if defined(__MINGW__)
d = _wopen((const unichar*)[debugFile fileSystemRepresentation],
O_WRONLY|O_CREAT|O_APPEND, 0644);
#else
d = open([debugFile fileSystemRepresentation],
O_WRONLY|O_CREAT|O_APPEND, 0644);
O_WRONLY|O_CREAT|O_APPEND, 0644);
#endif
if (d >= 0)
{
s = [NSString stringWithFormat: @"\nRead for %x at %@ %u bytes - '",
@ -240,8 +245,13 @@ static void debugWrite(GSHTTPURLHandle *handle, NSData *data)
int d;
[debugLock lock];
#if defined(__MINGW__)
d = _wopen((const unichar*)[debugFile fileSystemRepresentation],
O_WRONLY|O_CREAT|O_APPEND, 0644);
#else
d = open([debugFile fileSystemRepresentation],
O_WRONLY|O_CREAT|O_APPEND, 0644);
O_WRONLY|O_CREAT|O_APPEND, 0644);
#endif
if (d >= 0)
{
s = [NSString stringWithFormat: @"\nWrite for %x at %@ %u bytes - '",

View file

@ -49,7 +49,6 @@
#include <stdio.h>
#include <stdlib.h>
#if !defined(__MINGW32__)
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for gethostname() */
#endif
@ -90,7 +89,6 @@
#if defined(__svr4__)
#include <sys/stropts.h>
#endif
#endif /* !__MINGW32__ */
/*
* Largest chunk of data possible in DO
@ -192,9 +190,6 @@ newDataWithEncodedPort(NSMessagePort *port)
return data;
}
#if !defined(__MINGW32__)
/* Older systems (Solaris) compatibility */
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
@ -1962,290 +1957,3 @@ static unsigned wordAlign;
@end
#else
@implementation NSMessagePort
static NSRecursiveLock *messagePortLock = nil;
/*
* Maps port name to NSMessagePort objects.
*/
static NSMapTable *messagePortMap = 0;
static Class messagePortClass;
typedef struct {
NSString *_name;
NSRecursiveLock *_lock;
HANDLE *_handle;
} internal;
#define myName(P) ((internal*)(P)->_internal)->_name
#define myLock(P) ((internal*)(P)->_internal)->_lock
#define myHandle(P) ((internal*)(P)->_internal)->_handle
#if NEED_WORD_ALIGNMENT
static unsigned wordAlign;
#endif
+ (void) initialize
{
if (self == [NSMessagePort class])
{
#if NEED_WORD_ALIGNMENT
wordAlign = objc_alignof_type(@encode(gsu32));
#endif
messagePortClass = self;
messagePortMap = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0);
messagePortLock = [GSLazyRecursiveLock new];
}
}
+ (id) new
{
static int unique_index = 0;
unsigned char path[BUFSIZ];
M_LOCK(messagePortLock);
sprintf(path, "\\\\.\\mailslot\\NSMessagePort\\%i.%i",
[[NSProcessInfo processInfo] processIdentifier], unique_index++);
M_UNLOCK(messagePortLock);
return RETAIN([self _portWithName: path listener: YES]);
}
/*
* This is the preferred initialisation method for NSMessagePort
*
* 'mailslotName' is the name of the mailslot to use.
*/
+ (NSMessagePort*) _portWithName: (const unsigned char*)mailslotName
listener: (BOOL)shouldListen
{
NSMessagePort *port = nil;
M_LOCK(messagePortLock);
/*
* First try to find a pre-existing port.
*/
port = (NSMessagePort*)NSMapGet(messagePortMap, mailslotName);
if (port == nil)
{
port = (NSMessagePort*)NSAllocateObject(self, 0, NSDefaultMallocZone());
myName(port) = [[NSString alloc] initWithUTF8String: mailslotName];
myHandle(port) = INVALID_HANDLE_VALUE;
myLock(port) = [GSLazyRecursiveLock new];
port->_is_valid = YES;
if (shouldListen == YES)
{
myHandle(port) = CreateMailslot([myName(port) UTF8String], 0, 0, 0);
if (myHandle(port) == INVALID_HANDLE_VALUE)
{
NSLog(@"unable to create mailslot - %s", GSLastErrorStr(errno));
DESTROY(port);
}
else
{
/*
* Make sure we have the map table for this port.
*/
NSMapInsert(messagePortMap, (void*)myName(port), (void*)port);
NSDebugMLLog(@"NSMessagePort", @"Created listening port: %@",
port);
}
}
else
{
/*
* Make sure we have the map table for this port.
*/
NSMapInsert(messagePortMap, (void*)myName(port), (void*)port);
NSDebugMLLog(@"NSMessagePort", @"Created speaking port: %@", port);
}
}
else
{
RETAIN(port);
NSDebugMLLog(@"NSMessagePort", @"Using pre-existing port: %@", port);
}
IF_NO_GC(AUTORELEASE(port));
M_UNLOCK(messagePortLock);
return port;
}
- (id) copyWithZone: (NSZone*)zone
{
return RETAIN(self);
}
- (void) dealloc
{
[self gcFinalize];
DESTROY(myName(self));
[super dealloc];
}
- (NSString*) description
{
NSString *desc;
desc = [NSString stringWithFormat: @"<NSMessagePort %p with name %@>",
self, myName(self)];
return desc;
}
- (void) gcFinalize
{
NSDebugMLLog(@"NSMessagePort", @"NSMessagePort 0x%x finalized", self);
[self invalidate];
}
- (id) conversation: (NSPort*)recvPort
{
return nil;
}
- (void) handlePortMessage: (NSPortMessage*)m
{
id d = [self delegate];
if (d == nil)
{
NSDebugMLLog(@"NSMessagePort",
@"No delegate to handle incoming message", 0);
return;
}
if ([d respondsToSelector: @selector(handlePortMessage:)] == NO)
{
NSDebugMLLog(@"NSMessagePort", @"delegate doesn't handle messages", 0);
return;
}
[d handlePortMessage: m];
}
- (unsigned) hash
{
return [myName(self) hash];
}
- (id) init
{
RELEASE(self);
self = [messagePortClass new];
return self;
}
- (void) invalidate
{
if ([self isValid] == YES)
{
M_LOCK(myLock(self));
if ([self isValid] == YES)
{
M_LOCK(messagePortLock);
if (myHandle(self) != INVALID_HANDLE_VALUE)
{
(void) CloseHandle(myHandle(self));
myHandle(self) = INVALID_HANDLE_VALUE;
}
NSMapRemove(messagePortMap, (void*)myName(self));
M_UNLOCK(messagePortLock);
[[NSMessagePortNameServer sharedInstance] removePort: self];
[super invalidate];
}
M_UNLOCK(myLock(self));
}
}
- (BOOL) isEqual: (id)anObject
{
if (anObject == self)
{
return YES;
}
if ([anObject class] == [self class])
{
NSMessagePort *o = (NSMessagePort*)anObject;
return [myName(o) isEqual: myName(self)];
}
return NO;
}
- (void) receivedEvent: (void*)data
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode
{
HANDLE h = (HANDLE)(gsaddr)extra;
}
/*
* This returns the amount of space that a port coder should reserve at the
* start of its encoded data so that the NSMessagePort can insert header info
* into the data.
* The idea is that a message consisting of a single data item with space at
* the start can be written directly without having to copy data to another
* buffer etc.
*/
- (unsigned int) reservedSpaceLength
{
return sizeof(GSPortItemHeader) + sizeof(GSPortMsgHeader);
}
- (BOOL) sendBeforeDate: (NSDate*)when
msgid: (int)msgId
components: (NSMutableArray*)components
from: (NSPort*)receivingPort
reserved: (unsigned)length
{
BOOL sent = NO;
unsigned rl;
if ([self isValid] == NO)
{
return NO;
}
if ([components count] == 0)
{
NSLog(@"empty components sent");
return NO;
}
/*
* If the reserved length in the first data object is wrong - we have to
* fail, unless it's zero, in which case we can insert a data object for
* the header.
*/
rl = [self reservedSpaceLength];
if (length != 0 && length != rl)
{
NSLog(@"bad reserved length - %u", length);
return NO;
}
if ([receivingPort isKindOfClass: messagePortClass] == NO)
{
NSLog(@"woah there - receiving port is not the correct type");
return NO;
}
return sent;
}
- (NSDate*) timedOutEvent: (void*)data
type: (RunLoopEventType)type
forMode: (NSString*)mode
{
return nil;
}
@end
#endif /* __MINGW32__ */

View file

@ -32,9 +32,11 @@ SUBPROJECT_NAME = win32
win32_OBJC_FILES =\
GSRunLoopCtxt.m \
GSRunLoopWatcher.m \
NSMessagePortWin32.m \
NSRunLoopWin32.m \
NSUserDefaultsWin32.m \
Win32Support.m \
Win32_Utilities.m NSUserDefaultsWin32.m
Win32_Utilities.m
-include Makefile.preamble

View file

@ -0,0 +1,404 @@
/* Implementation of message port subclass of NSPortNameServer
Copyright (C) 2005 Free Software Foundation, Inc.
This file is part of the GNUstep Base Library.
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.
<title>NSMessagePortNameServer class reference</title>
$Date$ $Revision$
*/
#include "Foundation/NSPortNameServer.h"
#include "Foundation/NSAutoreleasePool.h"
#include "Foundation/NSDebug.h"
#include "Foundation/NSException.h"
#include "Foundation/NSLock.h"
#include "Foundation/NSMapTable.h"
#include "Foundation/NSPathUtilities.h"
#include "Foundation/NSPort.h"
#include "Foundation/NSFileManager.h"
#include "Foundation/NSValue.h"
#include "Foundation/NSThread.h"
#include "GSPortPrivate.h"
#if defined(__MINGW32__)
#else /* __MINGW32__ */
static NSRecursiveLock *serverLock = nil;
static NSMessagePortNameServer *defaultServer = nil;
/*
Maps NSMessagePort objects to NSMutableArray:s of NSString:s. The array
is an array of names the port has been registered under by _us_.
Note that this map holds the names the port has been registered under at
some time. If the name is been unregistered by some other program, we can't
update the table, so we have to deal with the case where the array contains
names that the port isn't registered under.
Since we _have_to_ deal with this anyway, we handle it in -removePort: and
-removePort:forName:, and we don't bother removing entries in the map when
unregistering a name not for a specific port.
*/
static NSMapTable portToNamesMap;
@interface NSMessagePortNameServer (private)
+(NSString *) _pathForName: (NSString *)name;
@end
static void clean_up_names(void)
{
NSMapEnumerator mEnum;
NSMessagePort *port;
NSString *name;
BOOL unknownThread = GSRegisterCurrentThread();
CREATE_AUTORELEASE_POOL(arp);
mEnum = NSEnumerateMapTable(portToNamesMap);
while (NSNextMapEnumeratorPair(&mEnum, (void *)&port, (void *)&name))
{
[defaultServer removePort: port];
}
NSEndMapTableEnumeration(&mEnum);
DESTROY(arp);
if (unknownThread == YES)
{
GSUnregisterCurrentThread();
}
}
/**
* Subclass of [NSPortNameServer] taking/returning instances of [NSMessagePort].
* Port removal functionality is not supported; if you want to cancel a service,
* you have to destroy the port (invalidate the [NSMessagePort] given to
* [NSPortNameServer-registerPort:forName:]).
*/
@implementation NSMessagePortNameServer
+ (void) initialize
{
if (self == [NSMessagePortNameServer class])
{
serverLock = [NSRecursiveLock new];
portToNamesMap = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
NSObjectMapValueCallBacks, 0);
atexit(clean_up_names);
}
}
/**
* Obtain single instance for this host.
*/
+ (id) sharedInstance
{
if (defaultServer == nil)
{
[serverLock lock];
if (defaultServer == nil)
{
defaultServer = (NSMessagePortNameServer *)NSAllocateObject(self,
0, NSDefaultMallocZone());
}
[serverLock unlock];
}
return defaultServer;
}
+ (NSString *) _pathForName: (NSString *)name
{
static NSString *base_path = nil;
NSString *path;
[serverLock lock];
if (!base_path)
{
NSNumber *p = [NSNumber numberWithInt: 0700];
NSDictionary *attr;
path = NSTemporaryDirectory();
attr = [NSDictionary dictionaryWithObject: p
forKey: NSFilePosixPermissions];
path = [path stringByAppendingPathComponent: @"NSMessagePort"];
[[NSFileManager defaultManager] createDirectoryAtPath: path
attributes: attr];
path = [path stringByAppendingPathComponent: @"names"];
[[NSFileManager defaultManager] createDirectoryAtPath: path
attributes: attr];
base_path = RETAIN(path);
}
else
{
path = base_path;
}
[serverLock unlock];
path = [path stringByAppendingPathComponent: name];
return path;
}
+ (BOOL) _livePort: (NSString *)path
{
FILE *f;
char socket_path[512];
int pid;
struct stat sb;
NSDebugLLog(@"NSMessagePort", @"_livePort: %@", path);
f = fopen([path fileSystemRepresentation], "rt");
if (!f)
{
NSDebugLLog(@"NSMessagePort", @"not live, couldn't open file (%m)");
return NO;
}
fgets(socket_path, sizeof(socket_path), f);
if (strlen(socket_path) > 0) socket_path[strlen(socket_path) - 1] = 0;
fscanf(f, "%i", &pid);
fclose(f);
if (stat(socket_path, &sb) < 0)
{
unlink([path fileSystemRepresentation]);
NSDebugLLog(@"NSMessagePort", @"not live, couldn't stat socket (%m)");
return NO;
}
if (kill(pid, 0) < 0)
{
unlink([path fileSystemRepresentation]);
unlink(socket_path);
NSDebugLLog(@"NSMessagePort", @"not live, no such process (%m)");
return NO;
}
else
{
struct sockaddr_un sockAddr;
int desc;
memset(&sockAddr, '\0', sizeof(sockAddr));
sockAddr.sun_family = AF_LOCAL;
strncpy(sockAddr.sun_path, socket_path, sizeof(sockAddr.sun_path));
if ((desc = socket(PF_LOCAL, SOCK_STREAM, PF_UNSPEC)) < 0)
{
unlink([path fileSystemRepresentation]);
unlink(socket_path);
NSDebugLLog(@"NSMessagePort",
@"couldn't create socket, assuming not live (%m)");
return NO;
}
if (connect(desc, (struct sockaddr*)&sockAddr, SUN_LEN(&sockAddr)) < 0)
{
unlink([path fileSystemRepresentation]);
unlink(socket_path);
NSDebugLLog(@"NSMessagePort", @"not live, can't connect (%m)");
return NO;
}
close(desc);
NSDebugLLog(@"NSMessagePort", @"port is live");
return YES;
}
}
- (NSPort*) portForName: (NSString *)name
onHost: (NSString *)host
{
NSString *path;
FILE *f;
char socket_path[512];
NSDebugLLog(@"NSMessagePort", @"portForName: %@ host: %@", name, host);
if ([host length] && ![host isEqual: @"*"])
{
NSDebugLLog(@"NSMessagePort", @"non-local host");
return nil;
}
path = [[self class] _pathForName: name];
if (![[self class] _livePort: path])
{
NSDebugLLog(@"NSMessagePort", @"not a live port");
return nil;
}
f = fopen([path fileSystemRepresentation], "rt");
if (!f)
{
NSDebugLLog(@"NSMessagePort", @"can't open file (%m)");
return nil;
}
fgets(socket_path, sizeof(socket_path), f);
if (strlen(socket_path) > 0) socket_path[strlen(socket_path) - 1] = 0;
fclose(f);
NSDebugLLog(@"NSMessagePort", @"got %s", socket_path);
return [NSMessagePort _portWithName: (unsigned char*)socket_path
listener: NO];
}
- (BOOL) registerPort: (NSPort *)port
forName: (NSString *)name
{
int fd;
unsigned char buf[32];
NSString *path;
const unsigned char *socket_name;
NSMutableArray *a;
NSDebugLLog(@"NSMessagePort", @"register %@ as %@\n", port, name);
if ([port isKindOfClass: [NSMessagePort class]] == NO)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempted to register a non-NSMessagePort (%@)",
port];
return NO;
}
path = [[self class] _pathForName: name];
if ([[self class] _livePort: path])
{
NSDebugLLog(@"NSMessagePort", @"fail, is a live port");
return NO;
}
fd = open([path fileSystemRepresentation], O_CREAT|O_EXCL|O_WRONLY, 0600);
if (fd < 0)
{
NSDebugLLog(@"NSMessagePort", @"fail, can't open file (%m)");
return NO;
}
socket_name = [(NSMessagePort *)port _name];
write(fd, (char*)socket_name, strlen((char*)socket_name));
write(fd, "\n", 1);
sprintf((char*)buf, "%i\n", getpid());
write(fd, (char*)buf, strlen((char*)buf));
close(fd);
[serverLock lock];
a = NSMapGet(portToNamesMap, port);
if (!a)
{
a = [[NSMutableArray alloc] init];
NSMapInsert(portToNamesMap, port, a);
RELEASE(a);
}
[a addObject: [name copy]];
[serverLock unlock];
return YES;
}
- (BOOL) removePortForName: (NSString *)name
{
NSString *path;
NSDebugLLog(@"NSMessagePort", @"removePortForName: %@", name);
path = [[self class] _pathForName: name];
unlink([path fileSystemRepresentation]);
return YES;
}
- (NSArray *) namesForPort: (NSPort *)port
{
NSMutableArray *a;
[serverLock lock];
a = NSMapGet(portToNamesMap, port);
a = [a copy];
[serverLock unlock];
return a;
}
- (BOOL) removePort: (NSPort *)port
{
NSMutableArray *a;
int i;
NSDebugLLog(@"NSMessagePort", @"removePort: %@", port);
[serverLock lock];
a = NSMapGet(portToNamesMap, port);
for (i = 0; i < [a count]; i++)
{
[self removePort: port forName: [a objectAtIndex: i]];
}
NSMapRemove(portToNamesMap, port);
[serverLock unlock];
return YES;
}
- (BOOL) removePort: (NSPort*)port forName: (NSString*)name
{
FILE *f;
char socket_path[512];
NSString *path;
const unsigned char *port_path;
NSDebugLLog(@"NSMessagePort", @"removePort: %@ forName: %@", port, name);
path = [[self class] _pathForName: name];
f = fopen([path fileSystemRepresentation], "rt");
if (!f)
return YES;
fgets(socket_path, sizeof(socket_path), f);
if (strlen(socket_path) > 0) socket_path[strlen(socket_path) - 1] = 0;
fclose(f);
port_path = [(NSMessagePort *)port _name];
if (!strcmp((char*)socket_path, (char*)port_path))
{
unlink([path fileSystemRepresentation]);
}
return YES;
}
@end
#endif /* __MINGW32__ */

View file

@ -0,0 +1,489 @@
/** Implementation of network port object based on unix domain sockets
Copyright (C) 2000 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Based on code by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
This file is part of the GNUstep Base Library.
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 "config.h"
#include "GNUstepBase/preface.h"
#include "GNUstepBase/GSLock.h"
#include "Foundation/NSArray.h"
#include "Foundation/NSNotification.h"
#include "Foundation/NSException.h"
#include "Foundation/NSRunLoop.h"
#include "Foundation/NSByteOrder.h"
#include "Foundation/NSData.h"
#include "Foundation/NSDate.h"
#include "Foundation/NSMapTable.h"
#include "Foundation/NSPortMessage.h"
#include "Foundation/NSPortNameServer.h"
#include "Foundation/NSLock.h"
#include "Foundation/NSThread.h"
#include "Foundation/NSConnection.h"
#include "Foundation/NSDebug.h"
#include "Foundation/NSPathUtilities.h"
#include "Foundation/NSValue.h"
#include "Foundation/NSFileManager.h"
#include "Foundation/NSProcessInfo.h"
#include "GSPortPrivate.h"
#include <stdio.h>
#include <stdlib.h>
/*
* Largest chunk of data possible in DO
*/
static gsu32 maxDataLength = 10 * 1024 * 1024;
#if 0
#define M_LOCK(X) {NSDebugMLLog(@"NSMessagePort",@"lock %@",X); [X lock];}
#define M_UNLOCK(X) {NSDebugMLLog(@"NSMessagePort",@"unlock %@",X); [X unlock];}
#else
#define M_LOCK(X) {[X lock];}
#define M_UNLOCK(X) {[X unlock];}
#endif
/*
* The GSPortItemType constant is used to identify the type of data in
* each packet read. All data transmitted is in a packet, each packet
* has an initial packet type and packet length.
*/
typedef enum {
GSP_NONE,
GSP_PORT, /* Simple port item. */
GSP_DATA, /* Simple data item. */
GSP_HEAD /* Port message header + initial data. */
} GSPortItemType;
/*
* The GSPortItemHeader structure defines the header for each item transmitted.
* Its contents are transmitted in network byte order.
*/
typedef struct {
gsu32 type; /* A GSPortItemType as a 4-byte number. */
gsu32 length; /* The length of the item (excluding header). */
} GSPortItemHeader;
/*
* The GSPortMsgHeader structure is at the start of any item of type GSP_HEAD.
* Its contents are transmitted in network byte order.
* Any additional data in the item is an NSData object.
* NB. additional data counts as part of the same item.
*/
typedef struct {
gsu32 mId; /* The ID for the message starting with this. */
gsu32 nItems; /* Number of items (including this one). */
} GSPortMsgHeader;
typedef struct {
unsigned char version;
unsigned char addr[0]; /* name of the port on the local host */
} GSPortInfo;
/*
* Utility functions for encoding and decoding ports.
*/
static NSMessagePort*
decodePort(NSData *data)
{
GSPortItemHeader *pih;
GSPortInfo *pi;
pih = (GSPortItemHeader*)[data bytes];
NSCAssert(GSSwapBigI32ToHost(pih->type) == GSP_PORT,
NSInternalInconsistencyException);
pi = (GSPortInfo*)&pih[1];
if (pi->version != 0)
{
NSLog(@"Remote version of GNUstep is more recent than this one (%i)",
pi->version);
return nil;
}
NSDebugFLLog(@"NSMessagePort", @"Decoded port as '%s'", pi->addr);
return [NSMessagePort _portWithName: pi->addr
listener: NO];
}
static NSData*
newDataWithEncodedPort(NSMessagePort *port)
{
GSPortItemHeader *pih;
GSPortInfo *pi;
NSMutableData *data;
unsigned plen;
const unsigned char *name = [port _name];
plen = 2 + strlen((char*)name);
data = [[NSMutableData alloc] initWithLength: sizeof(GSPortItemHeader)+plen];
pih = (GSPortItemHeader*)[data mutableBytes];
pih->type = GSSwapHostI32ToBig(GSP_PORT);
pih->length = GSSwapHostI32ToBig(plen);
pi = (GSPortInfo*)&pih[1];
strcpy((char*)pi->addr, (char*)name);
NSDebugFLLog(@"NSMessagePort", @"Encoded port as '%s'", pi->addr);
return data;
}
@implementation NSMessagePort
static NSRecursiveLock *messagePortLock = nil;
/*
* Maps port name to NSMessagePort objects.
*/
static NSMapTable *messagePortMap = 0;
static Class messagePortClass;
#define HDR (sizeof(GSPortItemHeader) + sizeof(GSPortMsgHeader))
typedef struct {
NSString *_name;
NSRecursiveLock *_lock;
HANDLE _handle;
HANDLE _event;
OVERLAPPED _ov;
DWORD _size;
BOOL _listen;
NSMutableData *_data;
unsigned _offset;
unsigned _target;
} internal;
#define myName(P) ((internal*)(P)->_internal)->_name
#define myLock(P) ((internal*)(P)->_internal)->_lock
#define myHandle(P) ((internal*)(P)->_internal)->_handle
#define myEvent(P) ((internal*)(P)->_internal)->_event
#define myOv(P) ((internal*)(P)->_internal)->_ov
#define myListen(P) ((internal*)(P)->_internal)->_listen
#define mySize(P) ((internal*)(P)->_internal)->_size
#define myData(P) ((internal*)(P)->_internal)->_data
#define myOffset(P) ((internal*)(P)->_internal)->_offset
#define myTarget(P) ((internal*)(P)->_internal)->_target
#if NEED_WORD_ALIGNMENT
static unsigned wordAlign;
#endif
+ (void) initialize
{
if (self == [NSMessagePort class])
{
#if NEED_WORD_ALIGNMENT
wordAlign = objc_alignof_type(@encode(gsu32));
#endif
messagePortClass = self;
messagePortMap = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0);
messagePortLock = [GSLazyRecursiveLock new];
}
}
+ (id) new
{
static int unique_index = 0;
unsigned char path[BUFSIZ];
M_LOCK(messagePortLock);
sprintf(path, "\\\\.\\mailslot\\NSMessagePort\\%i.%i",
[[NSProcessInfo processInfo] processIdentifier], unique_index++);
M_UNLOCK(messagePortLock);
return RETAIN([self _portWithName: path listener: YES]);
}
/*
* This is the preferred initialisation method for NSMessagePort
*
* 'mailslotName' is the name of the mailslot to use.
*/
+ (NSMessagePort*) _portWithName: (const unsigned char*)mailslotName
listener: (BOOL)shouldListen
{
NSMessagePort *port = nil;
M_LOCK(messagePortLock);
/*
* First try to find a pre-existing port.
*/
port = (NSMessagePort*)NSMapGet(messagePortMap, mailslotName);
if (port == nil)
{
port = (NSMessagePort*)NSAllocateObject(self, 0, NSDefaultMallocZone());
myName(port) = [[NSString alloc] initWithUTF8String: mailslotName];
myEvent(port) = CreateEvent(NULL, FALSE, FALSE, NULL);
myOv(port).Offset = 0;
myOv(port).OffsetHigh = 0;
myOv(port).hEvent = myEvent(port);
myHandle(port) = INVALID_HANDLE_VALUE;
myLock(port) = [GSLazyRecursiveLock new];
port->_is_valid = YES;
if (shouldListen == YES)
{
myHandle(port) = CreateMailslot([myName(port) UTF8String],
0, /* No max message size. */
MAILSLOT_WAIT_FOREVER, /* No read/write timeout. */
(LPSECURITY_ATTRIBUTES)0);
if (myHandle(port) == INVALID_HANDLE_VALUE)
{
NSLog(@"unable to create mailslot - %s", GSLastErrorStr(errno));
DESTROY(port);
}
else
{
myListen(port) = YES;
NSMapInsert(messagePortMap, (void*)myName(port), (void*)port);
NSDebugMLLog(@"NSMessagePort", @"Created listening port: %@",
port);
/* Set off an asynchronous read operation to get the header
* of an incoming message.
*/
myOffset(port) = 0;
myTarget(port) = HDR;
[myData(port) setLength: HDR];
ReadFile(myHandle(port),
[myData(port) mutableBytes], // Store results here
myTarget(port), // Read a header size.
&mySize(port), // Store number of bytes read
&myOv(port));
}
}
else
{
myHandle(port) = CreateFile([myName(port) UTF8String],
GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)0);
if (myHandle(port) == INVALID_HANDLE_VALUE)
{
NSLog(@"unable to access mailslot - %s", GSLastErrorStr(errno));
DESTROY(port);
}
else
{
myListen(port) = NO;
NSMapInsert(messagePortMap, (void*)myName(port), (void*)port);
NSDebugMLLog(@"NSMessagePort", @"Created speaking port: %@",
port);
}
}
}
else
{
RETAIN(port);
NSDebugMLLog(@"NSMessagePort", @"Using pre-existing port: %@", port);
}
IF_NO_GC(AUTORELEASE(port));
M_UNLOCK(messagePortLock);
return port;
}
- (id) copyWithZone: (NSZone*)zone
{
return RETAIN(self);
}
- (void) dealloc
{
[self gcFinalize];
DESTROY(myName(self));
[super dealloc];
}
- (NSString*) description
{
NSString *desc;
desc = [NSString stringWithFormat: @"<NSMessagePort %p with name %@>",
self, myName(self)];
return desc;
}
- (void) gcFinalize
{
NSDebugMLLog(@"NSMessagePort", @"NSMessagePort 0x%x finalized", self);
[self invalidate];
}
- (id) conversation: (NSPort*)recvPort
{
return nil;
}
- (void) handlePortMessage: (NSPortMessage*)m
{
id d = [self delegate];
if (d == nil)
{
NSDebugMLLog(@"NSMessagePort",
@"No delegate to handle incoming message", 0);
return;
}
if ([d respondsToSelector: @selector(handlePortMessage:)] == NO)
{
NSDebugMLLog(@"NSMessagePort", @"delegate doesn't handle messages", 0);
return;
}
[d handlePortMessage: m];
}
- (unsigned) hash
{
return [myName(self) hash];
}
- (id) init
{
RELEASE(self);
self = [messagePortClass new];
return self;
}
- (void) invalidate
{
if ([self isValid] == YES)
{
M_LOCK(myLock(self));
if ([self isValid] == YES)
{
M_LOCK(messagePortLock);
if (myEvent(self) != INVALID_HANDLE_VALUE)
{
(void) CloseHandle(myEvent(self));
myEvent(self) = INVALID_HANDLE_VALUE;
}
if (myHandle(self) != INVALID_HANDLE_VALUE)
{
(void) CloseHandle(myHandle(self));
myHandle(self) = INVALID_HANDLE_VALUE;
}
NSMapRemove(messagePortMap, (void*)myName(self));
M_UNLOCK(messagePortLock);
// FIXME [[NSMessagePortNameServer sharedInstance] removePort: self];
[super invalidate];
}
M_UNLOCK(myLock(self));
}
}
- (BOOL) isEqual: (id)anObject
{
if (anObject == self)
{
return YES;
}
if ([anObject class] == [self class])
{
NSMessagePort *o = (NSMessagePort*)anObject;
return [myName(o) isEqual: myName(self)];
}
return NO;
}
- (void) receivedEvent: (void*)data
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode
{
HANDLE h = (HANDLE)(gsaddr)extra;
}
/*
* This returns the amount of space that a port coder should reserve at the
* start of its encoded data so that the NSMessagePort can insert header info
* into the data.
* The idea is that a message consisting of a single data item with space at
* the start can be written directly without having to copy data to another
* buffer etc.
*/
- (unsigned int) reservedSpaceLength
{
return sizeof(GSPortItemHeader) + sizeof(GSPortMsgHeader);
}
- (BOOL) sendBeforeDate: (NSDate*)when
msgid: (int)msgId
components: (NSMutableArray*)components
from: (NSPort*)receivingPort
reserved: (unsigned)length
{
BOOL sent = NO;
unsigned rl;
if ([self isValid] == NO)
{
return NO;
}
if ([components count] == 0)
{
NSLog(@"empty components sent");
return NO;
}
/*
* If the reserved length in the first data object is wrong - we have to
* fail, unless it's zero, in which case we can insert a data object for
* the header.
*/
rl = [self reservedSpaceLength];
if (length != 0 && length != rl)
{
NSLog(@"bad reserved length - %u", length);
return NO;
}
if ([receivingPort isKindOfClass: messagePortClass] == NO)
{
NSLog(@"woah there - receiving port is not the correct type");
return NO;
}
return sent;
}
- (NSDate*) timedOutEvent: (void*)data
type: (RunLoopEventType)type
forMode: (NSString*)mode
{
return nil;
}
@end