mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Add 64bit specific files.
This commit is contained in:
parent
532b85c7a9
commit
7f3fe7f1f5
10 changed files with 6839 additions and 1 deletions
|
@ -40,6 +40,7 @@ ifeq ($(GNUSTEP_MAKEFILES),)
|
|||
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
|
||||
endif
|
||||
|
||||
# export GNUSTEP_TARGET_OS=mingw64
|
||||
GNUSTEP_CORE_SOFTWARE = YES
|
||||
export GNUSTEP_CORE_SOFTWARE
|
||||
RPM_DISABLE_RELOCATABLE = YES
|
||||
|
|
|
@ -84,7 +84,7 @@ ifeq ($(GNUSTEP_TARGET_OS), mingw32)
|
|||
libgnustep-base_SUBPROJECTS += win32
|
||||
else
|
||||
ifeq ($(GNUSTEP_TARGET_OS), mingw64)
|
||||
libgnustep-base_SUBPROJECTS += win32
|
||||
libgnustep-base_SUBPROJECTS += win64
|
||||
else
|
||||
libgnustep-base_SUBPROJECTS += unix
|
||||
endif
|
||||
|
|
45
Source/win64/GNUmakefile
Normal file
45
Source/win64/GNUmakefile
Normal file
|
@ -0,0 +1,45 @@
|
|||
#
|
||||
# src makefile for the GNUstep Base Library
|
||||
#
|
||||
# Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
#
|
||||
# Written by: Scott Christley <scottc@net-community.com>
|
||||
#
|
||||
# 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 Lesser 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 Lesser 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.
|
||||
#
|
||||
|
||||
PACKAGE_NAME = gnustep-base
|
||||
GNUSTEP_LOCAL_ADDITIONAL_MAKEFILES=../../base.make
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include ../../config.mak
|
||||
|
||||
SUBPROJECT_NAME = win32
|
||||
|
||||
win32_OBJC_FILES =\
|
||||
GSFileHandle.m \
|
||||
GSRunLoopCtxt.m \
|
||||
NSMessagePort.m \
|
||||
NSMessagePortNameServer.m \
|
||||
NSStream.m \
|
||||
NSUserDefaults.m \
|
||||
|
||||
-include Makefile.preamble
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/subproject.make
|
||||
|
||||
-include Makefile.postamble
|
2409
Source/win64/GSFileHandle.m
Normal file
2409
Source/win64/GSFileHandle.m
Normal file
File diff suppressed because it is too large
Load diff
617
Source/win64/GSRunLoopCtxt.m
Normal file
617
Source/win64/GSRunLoopCtxt.m
Normal file
|
@ -0,0 +1,617 @@
|
|||
/**
|
||||
* The GSRunLoopCtxt stores context information to handle polling for
|
||||
* events. This information is associated with a particular runloop
|
||||
* mode, and persists throughout the life of the runloop instance.
|
||||
*
|
||||
* NB. This class is private to NSRunLoop and must not be subclassed.
|
||||
*/
|
||||
|
||||
#import "common.h"
|
||||
|
||||
#import "Foundation/NSError.h"
|
||||
#import "Foundation/NSNotification.h"
|
||||
#import "Foundation/NSNotificationQueue.h"
|
||||
#import "Foundation/NSPort.h"
|
||||
#import "Foundation/NSStream.h"
|
||||
#import "../GSRunLoopCtxt.h"
|
||||
#import "../GSRunLoopWatcher.h"
|
||||
#import "../GSPrivate.h"
|
||||
|
||||
#define FDCOUNT 1024
|
||||
|
||||
static SEL wRelSel;
|
||||
static SEL wRetSel;
|
||||
static IMP wRelImp;
|
||||
static IMP wRetImp;
|
||||
|
||||
static void
|
||||
wRelease(NSMapTable* t, void* w)
|
||||
{
|
||||
(*wRelImp)((id)w, wRelSel);
|
||||
}
|
||||
|
||||
static void
|
||||
wRetain(NSMapTable* t, const void* w)
|
||||
{
|
||||
(*wRetImp)((id)w, wRetSel);
|
||||
}
|
||||
|
||||
static const NSMapTableValueCallBacks WatcherMapValueCallBacks =
|
||||
{
|
||||
wRetain,
|
||||
wRelease,
|
||||
0
|
||||
};
|
||||
|
||||
@implementation GSRunLoopCtxt
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
wRelSel = @selector(release);
|
||||
wRetSel = @selector(retain);
|
||||
wRelImp = [[GSRunLoopWatcher class] instanceMethodForSelector: wRelSel];
|
||||
wRetImp = [[GSRunLoopWatcher class] instanceMethodForSelector: wRetSel];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(mode);
|
||||
GSIArrayEmpty(performers);
|
||||
NSZoneFree(performers->zone, (void*)performers);
|
||||
GSIArrayEmpty(timers);
|
||||
NSZoneFree(timers->zone, (void*)timers);
|
||||
GSIArrayEmpty(watchers);
|
||||
NSZoneFree(watchers->zone, (void*)watchers);
|
||||
if (handleMap != 0)
|
||||
{
|
||||
NSFreeMapTable(handleMap);
|
||||
}
|
||||
if (winMsgMap != 0)
|
||||
{
|
||||
NSFreeMapTable(winMsgMap);
|
||||
}
|
||||
GSIArrayEmpty(_trigger);
|
||||
NSZoneFree(_trigger->zone, (void*)_trigger);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any callback for the specified event which is set for an
|
||||
* uncompleted poll operation.<br />
|
||||
* This is called by nested event loops on contexts in outer loops
|
||||
* when they handle an event ... removing the event from the outer
|
||||
* loop ensures that it won't get handled twice, once by the inner
|
||||
* loop and once by the outer one.
|
||||
*/
|
||||
- (void) endEvent: (void*)data
|
||||
for: (GSRunLoopWatcher*)watcher
|
||||
{
|
||||
if (completed == NO)
|
||||
{
|
||||
unsigned i = GSIArrayCount(_trigger);
|
||||
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSIArrayItem item = GSIArrayItemAtIndex(_trigger, i);
|
||||
|
||||
if (item.obj == (id)watcher)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex(_trigger, i);
|
||||
}
|
||||
}
|
||||
switch (watcher->type)
|
||||
{
|
||||
case ET_RPORT:
|
||||
case ET_HANDLE:
|
||||
NSMapRemove(handleMap, data);
|
||||
break;
|
||||
case ET_WINMSG:
|
||||
NSMapRemove(winMsgMap, data);
|
||||
break;
|
||||
case ET_TRIGGER:
|
||||
// Already handled
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Ending an event of unexpected type (%d)", watcher->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this poll context as having completed, so that if we are
|
||||
* executing a re-entrant poll, the enclosing poll operations
|
||||
* know they can stop what they are doing because an inner
|
||||
* operation has done the job.
|
||||
*/
|
||||
- (void) endPoll
|
||||
{
|
||||
completed = YES;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"-init may not be called for GSRunLoopCtxt"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id) initWithMode: (NSString*)theMode extra: (void*)e
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
NSZone *z;
|
||||
|
||||
mode = [theMode copy];
|
||||
extra = e;
|
||||
z = [self zone];
|
||||
performers = NSZoneMalloc(z, sizeof(GSIArray_t));
|
||||
timers = NSZoneMalloc(z, sizeof(GSIArray_t));
|
||||
watchers = NSZoneMalloc(z, sizeof(GSIArray_t));
|
||||
_trigger = NSZoneMalloc(z, sizeof(GSIArray_t));
|
||||
GSIArrayInitWithZoneAndCapacity(performers, z, 8);
|
||||
GSIArrayInitWithZoneAndCapacity(timers, z, 8);
|
||||
GSIArrayInitWithZoneAndCapacity(watchers, z, 8);
|
||||
GSIArrayInitWithZoneAndCapacity(_trigger, z, 8);
|
||||
|
||||
handleMap = NSCreateMapTable(NSIntegerMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
winMsgMap = NSCreateMapTable(NSIntegerMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a generic watcher (watching hwnd == 0),
|
||||
* loop through all events, and send them to the correct
|
||||
* watcher (if there are any) and then process the rest right here.
|
||||
* Return a flag to say whether any messages were handled.
|
||||
*/
|
||||
- (BOOL) processAllWindowsMessages:(int)num_winMsgs within: (NSArray*)contexts
|
||||
{
|
||||
MSG msg;
|
||||
GSRunLoopWatcher *generic = nil;
|
||||
unsigned i;
|
||||
BOOL handled = NO;
|
||||
|
||||
if (num_winMsgs > 0)
|
||||
{
|
||||
generic = NSMapGet(winMsgMap,0);
|
||||
}
|
||||
|
||||
if (generic != nil && generic->_invalidated == NO)
|
||||
{
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if (num_winMsgs > 0)
|
||||
{
|
||||
HANDLE handle;
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
handle = msg.hwnd;
|
||||
watcher = (GSRunLoopWatcher*)NSMapGet(winMsgMap,
|
||||
(void*)handle);
|
||||
if (watcher == nil || watcher->_invalidated == YES)
|
||||
{
|
||||
handle = 0; // Generic
|
||||
watcher
|
||||
= (GSRunLoopWatcher*)NSMapGet(winMsgMap, (void*)handle);
|
||||
}
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
{
|
||||
i = [contexts count];
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
||||
|
||||
if (c != self)
|
||||
{
|
||||
[c endEvent: (void*)handle for: watcher];
|
||||
}
|
||||
}
|
||||
handled = YES;
|
||||
/*
|
||||
* The watcher is still valid - so call the
|
||||
* receiver's event handling method.
|
||||
*/
|
||||
[watcher->receiver receivedEvent: watcher->data
|
||||
type: watcher->type
|
||||
extra: (void*)&msg
|
||||
forMode: mode];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num_winMsgs > 0)
|
||||
{
|
||||
unsigned num = num_winMsgs;
|
||||
NSMapEnumerator hEnum;
|
||||
HANDLE handle;
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
hEnum = NSEnumerateMapTable(winMsgMap);
|
||||
while (NSNextMapEnumeratorPair(&hEnum, &handle, (void**)&watcher))
|
||||
{
|
||||
if (watcher->_invalidated == NO)
|
||||
{
|
||||
while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE))
|
||||
{
|
||||
i = [contexts count];
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
||||
|
||||
if (c != self)
|
||||
{
|
||||
[c endEvent: (void*)handle for: watcher];
|
||||
}
|
||||
}
|
||||
handled = YES;
|
||||
[watcher->receiver receivedEvent: watcher->data
|
||||
type: watcher->type
|
||||
extra: (void*)&msg
|
||||
forMode: mode];
|
||||
}
|
||||
}
|
||||
num--;
|
||||
}
|
||||
NSEndMapTableEnumeration(&hEnum);
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
|
||||
{
|
||||
GSRunLoopThreadInfo *threadInfo = GSRunLoopInfoForThread(nil);
|
||||
NSMapEnumerator hEnum;
|
||||
GSRunLoopWatcher *watcher;
|
||||
HANDLE handleArray[MAXIMUM_WAIT_OBJECTS-1];
|
||||
int num_handles;
|
||||
int num_winMsgs;
|
||||
unsigned count;
|
||||
unsigned i;
|
||||
void *handle;
|
||||
int wait_timeout;
|
||||
DWORD wait_return;
|
||||
BOOL immediate = NO;
|
||||
BOOL existingMessages = NO;
|
||||
|
||||
// Set timeout how much time should wait
|
||||
if (milliseconds >= 0)
|
||||
{
|
||||
wait_timeout = milliseconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
wait_timeout = INFINITE;
|
||||
}
|
||||
|
||||
NSResetMapTable(handleMap);
|
||||
NSResetMapTable(winMsgMap);
|
||||
GSIArrayRemoveAllItems(_trigger);
|
||||
|
||||
i = GSIArrayCount(watchers);
|
||||
num_handles = 1; // One handle for signals from other threads
|
||||
num_winMsgs = 0;
|
||||
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSRunLoopWatcher *info;
|
||||
BOOL trigger;
|
||||
|
||||
info = GSIArrayItemAtIndex(watchers, i).obj;
|
||||
if (info->_invalidated == YES)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex(watchers, i);
|
||||
}
|
||||
else if ([info runLoopShouldBlock: &trigger] == NO)
|
||||
{
|
||||
if (trigger == YES)
|
||||
{
|
||||
immediate = YES;
|
||||
GSIArrayAddItem(_trigger, (GSIArrayItem)(id)info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HANDLE handle;
|
||||
|
||||
switch (info->type)
|
||||
{
|
||||
case ET_HANDLE:
|
||||
handle = (HANDLE)(size_t)info->data;
|
||||
NSMapInsert(handleMap, (void*)handle, info);
|
||||
num_handles++;
|
||||
break;
|
||||
case ET_RPORT:
|
||||
{
|
||||
id port = info->receiver;
|
||||
NSInteger port_hd_size = FDCOUNT;
|
||||
NSInteger port_hd_count = FDCOUNT;
|
||||
NSInteger port_hd_buffer[FDCOUNT];
|
||||
NSInteger *port_hd_array = port_hd_buffer;
|
||||
|
||||
[port getFds: port_hd_array count: &port_hd_count];
|
||||
while (port_hd_count > port_hd_size)
|
||||
{
|
||||
if (port_hd_array != port_hd_buffer) free(port_hd_array);
|
||||
port_hd_size = port_hd_count;
|
||||
port_hd_count = port_hd_size;
|
||||
port_hd_array = malloc(sizeof(NSInteger)*port_hd_size);
|
||||
[port getFds: port_hd_array count: &port_hd_count];
|
||||
}
|
||||
NSDebugMLLog(@"NSRunLoop", @"listening to %d port handles",
|
||||
port_hd_count);
|
||||
while (port_hd_count--)
|
||||
{
|
||||
NSMapInsert(handleMap,
|
||||
(void*)(size_t) port_hd_array[port_hd_count],
|
||||
info);
|
||||
num_handles++;
|
||||
}
|
||||
if (port_hd_array != port_hd_buffer) free(port_hd_array);
|
||||
}
|
||||
break;
|
||||
case ET_WINMSG:
|
||||
handle = (HANDLE)(size_t)info->data;
|
||||
NSMapInsert(winMsgMap, (void*)handle, info);
|
||||
num_winMsgs++;
|
||||
break;
|
||||
case ET_TRIGGER:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are notifications in the 'idle' queue, we try an
|
||||
* instantaneous select so that, if there is no input pending,
|
||||
* we can service the queue. Similarly, if a task has completed,
|
||||
* we need to deliver its notifications.
|
||||
*/
|
||||
if (GSPrivateCheckTasks() || GSPrivateNotifyMore(mode) || immediate == YES)
|
||||
{
|
||||
wait_timeout = 0;
|
||||
}
|
||||
|
||||
handleArray[0] = threadInfo->event; // Signal from other thread
|
||||
num_handles = NSCountMapTable(handleMap) + 1;
|
||||
if (num_handles >= MAXIMUM_WAIT_OBJECTS)
|
||||
{
|
||||
NSLog(@"Too many handles to wait for ... only using %d of %d",
|
||||
MAXIMUM_WAIT_OBJECTS-1, num_handles);
|
||||
num_handles = MAXIMUM_WAIT_OBJECTS-1;
|
||||
}
|
||||
count = num_handles - 1; // Count of handles excluding thread event
|
||||
if (count > 0)
|
||||
{
|
||||
i = 1 + (fairStart++ % count);
|
||||
hEnum = NSEnumerateMapTable(handleMap);
|
||||
while (count-- > 0
|
||||
&& NSNextMapEnumeratorPair(&hEnum, &handle, (void**)&watcher))
|
||||
{
|
||||
if (i >= num_handles)
|
||||
{
|
||||
i = 1;
|
||||
}
|
||||
handleArray[i++] = (HANDLE)handle;
|
||||
}
|
||||
NSEndMapTableEnumeration(&hEnum);
|
||||
}
|
||||
|
||||
completed = NO;
|
||||
|
||||
/* Clear all the windows messages first before we wait,
|
||||
* since MsgWaitForMultipleObjects only signals on NEW messages
|
||||
*/
|
||||
if ([self processAllWindowsMessages: num_winMsgs within: contexts] == YES)
|
||||
{
|
||||
// Processed something ... no need to wait.
|
||||
wait_timeout = 0;
|
||||
num_winMsgs = 0;
|
||||
existingMessages = YES;
|
||||
}
|
||||
|
||||
if (num_winMsgs > 0)
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop",
|
||||
@"wait for messages and %d handles for %d milliseconds",
|
||||
num_handles, wait_timeout);
|
||||
|
||||
/*
|
||||
* Wait for signalled events or window messages.
|
||||
*/
|
||||
wait_return = MsgWaitForMultipleObjects(num_handles, handleArray,
|
||||
NO, wait_timeout, QS_ALLINPUT);
|
||||
}
|
||||
else if (num_handles > 0)
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop",
|
||||
@"wait for %d handles for %d milliseconds", num_handles, wait_timeout);
|
||||
|
||||
/*
|
||||
* We are not interested in windows messages ... just wait for
|
||||
* signalled events.
|
||||
*/
|
||||
wait_return = WaitForMultipleObjects(num_handles, handleArray,
|
||||
NO, wait_timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop",
|
||||
@"wait for %d milliseconds", wait_timeout);
|
||||
SleepEx(wait_timeout, TRUE);
|
||||
wait_return = WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
// check wait errors
|
||||
if (WAIT_FAILED == wait_return
|
||||
|| (wait_return >= WAIT_ABANDONED_0
|
||||
&& wait_return < WAIT_ABANDONED_0 + num_handles))
|
||||
{
|
||||
int i;
|
||||
BOOL found = NO;
|
||||
|
||||
NSDebugMLLog(@"NSRunLoop", @"WaitForMultipleObjects() error in "
|
||||
@"-pollUntil:within: %@", [NSError _last]);
|
||||
/*
|
||||
* Check each handle in turn until either we find one which has an
|
||||
* event signalled, or we find the one which caused the original
|
||||
* wait to fail ... so the callback routine for that handle can
|
||||
* deal with the problem.
|
||||
*/
|
||||
for (i = 0; i < num_handles; i++)
|
||||
{
|
||||
handleArray[0] = handleArray[i];
|
||||
wait_return = WaitForMultipleObjects(1, handleArray, NO, 0);
|
||||
if (wait_return != WAIT_TIMEOUT)
|
||||
{
|
||||
wait_return = WAIT_OBJECT_0;
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == NO)
|
||||
{
|
||||
NSLog(@"WaitForMultipleObjects() error in "
|
||||
@"-pollUntil:within: %@", [NSError _last]);
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger any watchers which are set up to trigger for every runloop wait.
|
||||
*/
|
||||
count = GSIArrayCount(_trigger);
|
||||
completed = NO;
|
||||
while (count-- > 0)
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
watcher = (GSRunLoopWatcher*)GSIArrayItemAtIndex(_trigger, count).obj;
|
||||
if (watcher->_invalidated == NO)
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop", @"trigger watcher %@", watcher);
|
||||
i = [contexts count];
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
||||
|
||||
if (c != self)
|
||||
{
|
||||
[c endEvent: (void*)watcher for: watcher];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The watcher is still valid - so call its
|
||||
* receivers event handling method.
|
||||
*/
|
||||
[watcher->receiver receivedEvent: watcher->data
|
||||
type: watcher->type
|
||||
extra: watcher->data
|
||||
forMode: mode];
|
||||
}
|
||||
GSPrivateNotifyASAP(mode);
|
||||
}
|
||||
|
||||
if (WAIT_TIMEOUT == wait_return)
|
||||
{
|
||||
// there is no event to handle
|
||||
if (existingMessages)
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop", @"processed windows messages");
|
||||
}
|
||||
else
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop", @"timeout without events");
|
||||
completed = YES;
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else if (WAIT_OBJECT_0 + num_handles == wait_return)
|
||||
{
|
||||
// one or more windows message
|
||||
NSDebugMLLog(@"NSRunLoop", @"processing windows messages");
|
||||
[self processAllWindowsMessages: num_winMsgs within: contexts];
|
||||
}
|
||||
else if ((i = wait_return - WAIT_OBJECT_0) >= 0 && i < num_handles)
|
||||
{
|
||||
/* Look the event that WaitForMultipleObjects() says is ready;
|
||||
* get the corresponding fd for that handle event and notify
|
||||
* the corresponding object for the ready fd.
|
||||
*/
|
||||
NSDebugMLLog(@"NSRunLoop", @"Handle signalled %d", i);
|
||||
|
||||
handle = handleArray[i];
|
||||
|
||||
if (handle == threadInfo->event)
|
||||
{
|
||||
watcher = nil;
|
||||
NSDebugMLLog(@"NSRunLoop", @"Fire perform on thread");
|
||||
[threadInfo fire];
|
||||
}
|
||||
else
|
||||
{
|
||||
watcher = (GSRunLoopWatcher*)NSMapGet(handleMap, (void*)handle);
|
||||
NSDebugMLLog(@"NSRunLoop", @"Fire watcher %@", watcher);
|
||||
}
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
{
|
||||
i = [contexts count];
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
||||
|
||||
if (c != self)
|
||||
{
|
||||
[c endEvent: (void*)handle for: watcher];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The watcher is still valid - so call its receivers
|
||||
* event handling method.
|
||||
*/
|
||||
[watcher->receiver receivedEvent: watcher->data
|
||||
type: watcher->type
|
||||
extra: (void*)handle
|
||||
forMode: mode];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop", @"unexpected result %d", wait_return);
|
||||
GSPrivateNotifyASAP(mode);
|
||||
completed = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
GSPrivateNotifyASAP(mode);
|
||||
completed = YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL) awakenedBefore: (NSDate*)when
|
||||
{
|
||||
GSRunLoopThreadInfo *threadInfo = GSRunLoopInfoForThread(nil);
|
||||
NSTimeInterval ti = (when == nil) ? 0.0 : [when timeIntervalSinceNow];
|
||||
int milliseconds = (ti <= 0.0) ? 0 : (int)(ti*1000);
|
||||
HANDLE h = threadInfo->event;
|
||||
|
||||
if (WaitForMultipleObjects(1, &h, NO, milliseconds) != WAIT_TIMEOUT)
|
||||
{
|
||||
NSDebugMLLog(@"NSRunLoop", @"Fire perform on thread");
|
||||
[threadInfo fire];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
67
Source/win64/Makefile.preamble
Normal file
67
Source/win64/Makefile.preamble
Normal file
|
@ -0,0 +1,67 @@
|
|||
#
|
||||
# Makefile.preamble
|
||||
#
|
||||
# Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
#
|
||||
# Author: Scott Christley <scottc@net-community.com>
|
||||
#
|
||||
# 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 Lesser 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.
|
||||
#
|
||||
# If you are interested in a warranty or support for this source code,
|
||||
# contact Scott Christley at scottc@net-community.com
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; see the file COPYING.LIB.
|
||||
# If not, write to the Free Software Foundation,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#
|
||||
# Makefile.preamble
|
||||
#
|
||||
# Project specific makefile variables
|
||||
#
|
||||
# Do not put any Makefile rules in this file, instead they should
|
||||
# be put into Makefile.postamble.
|
||||
#
|
||||
|
||||
#
|
||||
# Flags dealing with compiling and linking
|
||||
#
|
||||
|
||||
# Additional flags to pass to the preprocessor
|
||||
ADDITIONAL_CPPFLAGS = $(DEFS) \
|
||||
$(WARN_FLAGS)
|
||||
|
||||
# Additional flags to pass to the Objective-C compiler
|
||||
#ADDITIONAL_OBJCFLAGS =
|
||||
|
||||
ifeq ($(GNUSTEP_TARGET_OS),mingw32)
|
||||
ADDITIONAL_OBJCFLAGS += -DBUILD_libgnustep_base_DLL=1
|
||||
else ifeq ($(GNUSTEP_TARGET_OS),mingw64)
|
||||
ADDITIONAL_OBJCFLAGS += -DBUILD_libgnustep_base_DLL=1
|
||||
else ifeq ($(GNUSTEP_TARGET_OS),cygwin)
|
||||
ADDITIONAL_OBJCFLAGS += -DBUILD_libgnustep_base_DLL=1
|
||||
endif
|
||||
|
||||
# Additional flags to pass to the C compiler
|
||||
ADDITIONAL_CFLAGS =
|
||||
|
||||
# Additional include directories the compiler should search
|
||||
ADDITIONAL_INCLUDE_DIRS = -I../$(GNUSTEP_TARGET_DIR) -I../
|
||||
|
||||
ifeq ($(FOUNDATION_LIB),gnu)
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../../Headers
|
||||
endif
|
||||
|
||||
# Additional LDFLAGS to pass to the linker
|
||||
ADDITIONAL_LDFLAGS =
|
1156
Source/win64/NSMessagePort.m
Normal file
1156
Source/win64/NSMessagePort.m
Normal file
File diff suppressed because it is too large
Load diff
433
Source/win64/NSMessagePortNameServer.m
Normal file
433
Source/win64/NSMessagePortNameServer.m
Normal file
|
@ -0,0 +1,433 @@
|
|||
/* 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 Lesser 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 Lesser 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 "common.h"
|
||||
|
||||
#include "Foundation/NSPortNameServer.h"
|
||||
|
||||
#include "Foundation/NSAutoreleasePool.h"
|
||||
#include "Foundation/NSDebug.h"
|
||||
#include "Foundation/NSError.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 "Foundation/NSUserDefaults.h"
|
||||
|
||||
#include "GNUstepBase/GSMime.h"
|
||||
|
||||
#include "../GSPrivate.h"
|
||||
#include "../GSPortPrivate.h"
|
||||
|
||||
#define UNISTR(X) \
|
||||
((const unichar*)[(X) cStringUsingEncoding: NSUnicodeStringEncoding])
|
||||
|
||||
|
||||
static NSRecursiveLock *serverLock = nil;
|
||||
static NSMessagePortNameServer *defaultServer = nil;
|
||||
static NSMapTable *portToNamesMap;
|
||||
static NSString *registry;
|
||||
static HKEY key;
|
||||
|
||||
static SECURITY_ATTRIBUTES security;
|
||||
|
||||
@interface NSMessagePortNameServer (private)
|
||||
+ (NSString *) _query: (NSString *)name;
|
||||
+ (NSString *) _translate: (NSString *)name;
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* 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) atExit
|
||||
{
|
||||
NSMapEnumerator mEnum;
|
||||
NSMessagePort *port;
|
||||
NSString *name;
|
||||
|
||||
mEnum = NSEnumerateMapTable(portToNamesMap);
|
||||
while (NSNextMapEnumeratorPair(&mEnum, (void *)&port, (void *)&name))
|
||||
{
|
||||
[defaultServer removePort: port];
|
||||
}
|
||||
NSEndMapTableEnumeration(&mEnum);
|
||||
DESTROY(portToNamesMap);
|
||||
DESTROY(serverLock);
|
||||
RegCloseKey(key);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [NSMessagePortNameServer class])
|
||||
{
|
||||
int rc;
|
||||
|
||||
serverLock = [NSRecursiveLock new];
|
||||
portToNamesMap = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
[self registerAtExit];
|
||||
|
||||
security.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security.lpSecurityDescriptor = 0; // Default
|
||||
security.bInheritHandle = FALSE;
|
||||
|
||||
registry = @"Software\\GNUstepNSMessagePort";
|
||||
rc = RegCreateKeyExW(
|
||||
HKEY_CURRENT_USER,
|
||||
UNISTR(registry),
|
||||
0,
|
||||
(LPWSTR) L"",
|
||||
REG_OPTION_VOLATILE,
|
||||
STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ|KEY_SET_VALUE
|
||||
|KEY_QUERY_VALUE|KEY_NOTIFY,
|
||||
&security,
|
||||
&key,
|
||||
NULL);
|
||||
if (rc == ERROR_SUCCESS)
|
||||
{
|
||||
rc = RegFlushKey(key);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to flush registry HKEY_CURRENT_USER\\%@ (%x)",
|
||||
registry, rc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Failed to create registry HKEY_CURRENT_USER\\%@ (%x)",
|
||||
registry, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 *) _query: (NSString *)name
|
||||
{
|
||||
NSString *mailslotName;
|
||||
NSString *translatedName;
|
||||
NSString *mailslotPath;
|
||||
unsigned char buf[1024];
|
||||
unsigned char *ptr = buf;
|
||||
DWORD max = 1024;
|
||||
DWORD len = 1024;
|
||||
DWORD type;
|
||||
HANDLE h;
|
||||
int rc;
|
||||
|
||||
translatedName = [[self class] _translate: name];
|
||||
|
||||
#if 1
|
||||
/* FIXME ... wierd hack.
|
||||
* It appears that RegQueryValueExW does not always read from the registry,
|
||||
* but will in fact return cached results (even if you close and re-open the
|
||||
* registry key between the calls to RegQueryValueExW). This is a problem
|
||||
* if we look up a server which is not running, and then try to look it up
|
||||
* again when it is running, or if we have one address recorded but the server
|
||||
* has been restarted and is using a new address.
|
||||
* I couldn't find any mention of this behavior ... but accidentally discovered
|
||||
* that a call to OutputDebugStringW stops it ... presumably something in the
|
||||
* debug system invalidates whatever registry caching is being done.
|
||||
* Anyway, on my XP SP2 system, this next line is needed to fix things.
|
||||
*
|
||||
* You can test this by running a GNUstep application without starting
|
||||
* gdnc beforehand. If the bug is occurring, the app will try to start gdnc
|
||||
* then poll to connect to it, and after 5 seconds will abort because it
|
||||
* hasn't seen the gdnc port registered even though gdnc did start.
|
||||
* If the hack has fixed the bug, the app will just pause briefly during
|
||||
* startup (as it starts gdnc) and then continue when it finds the server
|
||||
* port.
|
||||
*/
|
||||
OutputDebugStringW(L"");
|
||||
#endif
|
||||
|
||||
rc = RegQueryValueExW(
|
||||
key,
|
||||
UNISTR(translatedName),
|
||||
(LPDWORD)0,
|
||||
&type,
|
||||
(LPBYTE)ptr,
|
||||
&len);
|
||||
while (rc == ERROR_MORE_DATA)
|
||||
{
|
||||
if (ptr != buf)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
max += 1024;
|
||||
ptr = malloc(max);
|
||||
len = max;
|
||||
rc = RegQueryValueExW(
|
||||
key,
|
||||
UNISTR(translatedName),
|
||||
(LPDWORD)0,
|
||||
&type,
|
||||
(LPBYTE)ptr,
|
||||
&len);
|
||||
}
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
if (ptr != buf)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
mailslotName = [NSString stringWithUTF8String: (const char *) ptr];
|
||||
if (ptr != buf)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we can open the port mailslot ... if not, the query returned
|
||||
* an old name, and we can remove it.
|
||||
*/
|
||||
mailslotPath = [NSString stringWithFormat:
|
||||
@"\\\\.\\mailslot\\GNUstep\\NSMessagePort\\%@", mailslotName];
|
||||
h = CreateFileW(
|
||||
UNISTR(mailslotPath),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
&security,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
(HANDLE)0);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
NSDebugLLog(@"NSMessagePortNameServer",
|
||||
@"Failed to open mailslot (%@) for write on port named %@ - %@",
|
||||
mailslotPath, name, [NSError _last]);
|
||||
//RegDeleteValueW(key, UNISTR(n));
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseHandle(h); // OK
|
||||
return mailslotName;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *) _translate: (NSString *)name
|
||||
{
|
||||
NSData *data;
|
||||
|
||||
/*
|
||||
* Make sure name is representable in the registry ...
|
||||
* assume base64 encoded strings are valid.
|
||||
*/
|
||||
data = [name dataUsingEncoding: NSUTF8StringEncoding];
|
||||
data = [GSMimeDocument encodeBase64: data];
|
||||
name = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding];
|
||||
AUTORELEASE(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
- (NSPort*) portForName: (NSString *)name
|
||||
{
|
||||
return [self portForName: name onHost: @""];
|
||||
}
|
||||
|
||||
- (NSPort*) portForName: (NSString *)name
|
||||
onHost: (NSString *)host
|
||||
{
|
||||
NSString *n;
|
||||
|
||||
NSDebugLLog(@"NSMessagePortNameServer",
|
||||
@"portForName: %@ host: %@", name, host);
|
||||
|
||||
if ([host length] != 0)
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Attempt to contact a named host using a "
|
||||
@"message port name server. This name server can only be used "
|
||||
@"to contact processes owned by the same user on the local host "
|
||||
@"(host name must be an empty string). To contact processes "
|
||||
@"owned by other users or on other hosts you must use an instance "
|
||||
@"of the NSSocketPortNameServer class."];
|
||||
}
|
||||
|
||||
n = [[self class] _query: name];
|
||||
if (n == nil)
|
||||
{
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"got no port for %@", name);
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"got %@ for %@", n, name);
|
||||
return AUTORELEASE([NSMessagePort newWithName: n]);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) registerPort: (NSPort *)port
|
||||
forName: (NSString *)name
|
||||
{
|
||||
NSMutableArray *a;
|
||||
NSString *n;
|
||||
int rc;
|
||||
const unsigned char *str;
|
||||
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"register %@ as %@\n", port, name);
|
||||
if ([port isKindOfClass: [NSMessagePort class]] == NO)
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Attempted to register a non-NSMessagePort (%@)",
|
||||
port];
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([[self class] _query: name] != nil)
|
||||
{
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"fail, is a live port");
|
||||
return NO;
|
||||
}
|
||||
|
||||
n = [[self class] _translate: name];
|
||||
str = (const unsigned char *) [[(NSMessagePort*)port name] UTF8String];
|
||||
|
||||
rc = RegSetValueExW(
|
||||
key,
|
||||
UNISTR(n),
|
||||
0,
|
||||
REG_BINARY,
|
||||
str,
|
||||
strlen((const char*) str)+1);
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"Set port '%s' for %@", str, n);
|
||||
if (rc == ERROR_SUCCESS)
|
||||
{
|
||||
rc = RegFlushKey(key);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to flush registry HKEY_CURRENT_USER\\%@\\%@ (%x)",
|
||||
registry, n, rc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Failed to insert HKEY_CURRENT_USER\\%@\\%@ (%x) %@",
|
||||
registry, n, rc, [NSError _last]);
|
||||
return NO;
|
||||
}
|
||||
|
||||
[serverLock lock];
|
||||
a = NSMapGet(portToNamesMap, port);
|
||||
if (a != nil)
|
||||
{
|
||||
a = [[NSMutableArray alloc] init];
|
||||
NSMapInsert(portToNamesMap, port, a);
|
||||
RELEASE(a);
|
||||
}
|
||||
[a addObject: [name copy]];
|
||||
[serverLock unlock];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) removePortForName: (NSString *)name
|
||||
{
|
||||
NSString *n;
|
||||
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"removePortForName: %@", name);
|
||||
n = [[self class] _translate: name];
|
||||
(void)RegDeleteValueW(key, UNISTR(n));
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSArray *) namesForPort: (NSPort *)port
|
||||
{
|
||||
NSMutableArray *a;
|
||||
|
||||
[serverLock lock];
|
||||
a = NSMapGet(portToNamesMap, port);
|
||||
a = [a copy];
|
||||
[serverLock unlock];
|
||||
return AUTORELEASE(a);
|
||||
}
|
||||
|
||||
- (BOOL) removePort: (NSPort *)port
|
||||
{
|
||||
NSMutableArray *a;
|
||||
int i;
|
||||
|
||||
NSDebugLLog(@"NSMessagePortNameServer", @"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
|
||||
{
|
||||
NSDebugLLog(@"NSMessagePortNameServer",
|
||||
@"removePort: %@ forName: %@", port, name);
|
||||
|
||||
if ([self portForName: name onHost: @""] == port)
|
||||
{
|
||||
return [self removePortForName: name];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
1485
Source/win64/NSStream.m
Normal file
1485
Source/win64/NSStream.m
Normal file
File diff suppressed because it is too large
Load diff
625
Source/win64/NSUserDefaults.m
Normal file
625
Source/win64/NSUserDefaults.m
Normal file
|
@ -0,0 +1,625 @@
|
|||
#import "common.h"
|
||||
|
||||
#import "Foundation/NSUserDefaults.h"
|
||||
#import "Foundation/NSAutoreleasePool.h"
|
||||
#import "Foundation/NSException.h"
|
||||
#import "Foundation/NSDictionary.h"
|
||||
#import "Foundation/NSArray.h"
|
||||
#import "Foundation/NSLock.h"
|
||||
#import "Foundation/NSFileManager.h"
|
||||
#import "Foundation/NSMapTable.h"
|
||||
#import "Foundation/NSPathUtilities.h"
|
||||
#import "Foundation/NSProcessInfo.h"
|
||||
|
||||
#define UNISTR(X) \
|
||||
((const unichar*)[(X) cStringUsingEncoding: NSUnicodeStringEncoding])
|
||||
|
||||
extern void GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*);
|
||||
|
||||
@interface NSUserDefaults (Private)
|
||||
- (id) initWithContentsOfFile: (NSString*)fielName;
|
||||
@end
|
||||
|
||||
@interface NSUserDefaultsWin32 : NSUserDefaults
|
||||
{
|
||||
NSString *registryPrefix;
|
||||
NSMapTable *registryInfo;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface NSUserDefaults (Secrets)
|
||||
- (id) initWithContentsOfFile: (NSString*)aPath;
|
||||
- (BOOL) lockDefaultsFile: (BOOL*)wasLocked;
|
||||
- (void) unlockDefaultsFile;
|
||||
- (NSMutableDictionary*) readDefaults;
|
||||
- (BOOL) wantToReadDefaultsSince: (NSDate*)lastSyncDate;
|
||||
- (BOOL) writeDefaults: (NSDictionary*)defaults oldData: (NSDictionary*)oldData;
|
||||
@end
|
||||
|
||||
struct NSUserDefaultsWin32_DomainInfo
|
||||
{
|
||||
HKEY userKey;
|
||||
HKEY systemKey;
|
||||
};
|
||||
|
||||
@implementation NSUserDefaultsWin32
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
DESTROY(registryPrefix);
|
||||
if (registryInfo != 0)
|
||||
{
|
||||
NSMapEnumerator iter = NSEnumerateMapTable(registryInfo);
|
||||
NSString *domain;
|
||||
struct NSUserDefaultsWin32_DomainInfo *dinfo;
|
||||
|
||||
while (NSNextMapEnumeratorPair(&iter, (void**)&domain, (void**)&dinfo))
|
||||
{
|
||||
LONG rc;
|
||||
|
||||
if (dinfo->userKey)
|
||||
{
|
||||
rc = RegCloseKey(dinfo->userKey);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSString *dPath;
|
||||
|
||||
dPath = [registryPrefix stringByAppendingString: domain];
|
||||
NSLog(@"Failed to close registry HKEY_CURRENT_USER\\%@ (%x)",
|
||||
dPath, rc);
|
||||
}
|
||||
}
|
||||
if (dinfo->systemKey)
|
||||
{
|
||||
rc = RegCloseKey(dinfo->systemKey);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSString *dPath;
|
||||
|
||||
dPath = [registryPrefix stringByAppendingString: domain];
|
||||
NSLog(@"Failed to close registry HKEY_LOCAL_MACHINE\\%@ (%x)",
|
||||
dPath, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
NSResetMapTable(registryInfo);
|
||||
NSFreeMapTable(registryInfo);
|
||||
registryInfo = 0;
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) initWithUser: (NSString*)userName
|
||||
{
|
||||
NSString *path;
|
||||
NSRange r;
|
||||
|
||||
NSAssert([userName isEqual: NSUserName()],
|
||||
@"NSUserDefaultsWin32 doesn't support reading/writing to users other than the current user.");
|
||||
|
||||
path = GSDefaultsRootForUser(userName);
|
||||
r = [path rangeOfString: @":REGISTRY:"];
|
||||
NSAssert(r.length > 0,
|
||||
@"NSUserDefaultsWin32 should only be used if defaults directory is :REGISTRY:");
|
||||
|
||||
path = [path substringFromIndex: NSMaxRange(r)];
|
||||
if ([path length] == 0)
|
||||
{
|
||||
path = @"Software\\GNUstep\\";
|
||||
}
|
||||
else if ([path hasSuffix: @"\\"] == NO)
|
||||
{
|
||||
path = [path stringByAppendingString: @"\\"];
|
||||
}
|
||||
registryPrefix = RETAIN(path);
|
||||
self = [super initWithContentsOfFile: @":REGISTRY:"];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) lockDefaultsFile: (BOOL*)wasLocked
|
||||
{
|
||||
*wasLocked = NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary*) readDefaults
|
||||
{
|
||||
NSArray *allDomains;
|
||||
NSEnumerator *iter;
|
||||
NSString *persistentDomain;
|
||||
NSMutableDictionary *newDict = nil;
|
||||
|
||||
allDomains = [self persistentDomainNames];
|
||||
if ([allDomains count] == 0)
|
||||
{
|
||||
allDomains = [NSArray arrayWithObjects:
|
||||
[[NSProcessInfo processInfo] processName],
|
||||
NSGlobalDomain,
|
||||
nil];
|
||||
}
|
||||
|
||||
if (registryInfo == 0)
|
||||
{
|
||||
registryInfo = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||
NSOwnedPointerMapValueCallBacks, [allDomains count]);
|
||||
}
|
||||
|
||||
newDict = [NSMutableDictionary dictionary];
|
||||
|
||||
iter = [allDomains objectEnumerator];
|
||||
while ((persistentDomain = [iter nextObject]) != nil)
|
||||
{
|
||||
NSMutableDictionary *domainDict;
|
||||
struct NSUserDefaultsWin32_DomainInfo *dinfo;
|
||||
NSString *dPath;
|
||||
LONG rc;
|
||||
|
||||
dinfo = NSMapGet(registryInfo, persistentDomain);
|
||||
if (dinfo == 0)
|
||||
{
|
||||
dinfo = calloc(sizeof(struct NSUserDefaultsWin32_DomainInfo), 1);
|
||||
NSMapInsertKnownAbsent(registryInfo, persistentDomain, dinfo);
|
||||
}
|
||||
dPath = [registryPrefix stringByAppendingString: persistentDomain];
|
||||
|
||||
if (dinfo->userKey == 0)
|
||||
{
|
||||
rc = RegOpenKeyExW(HKEY_CURRENT_USER,
|
||||
UNISTR(dPath),
|
||||
0,
|
||||
STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ
|
||||
|KEY_SET_VALUE|KEY_QUERY_VALUE,
|
||||
&(dinfo->userKey));
|
||||
if (rc == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
dinfo->userKey = 0;
|
||||
}
|
||||
else if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to open registry HKEY_CURRENT_USER\\%@ (%x)",
|
||||
dPath, rc);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
if (dinfo->systemKey == 0)
|
||||
{
|
||||
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
UNISTR(dPath),
|
||||
0,
|
||||
STANDARD_RIGHTS_READ|KEY_QUERY_VALUE,
|
||||
&(dinfo->systemKey));
|
||||
if (rc == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
dinfo->systemKey = 0;
|
||||
}
|
||||
else if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to open registry HKEY_LOCAL_MACHINE\\%@ (%x)",
|
||||
dPath, rc);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
domainDict = [newDict objectForKey: persistentDomain];
|
||||
if (domainDict == nil)
|
||||
{
|
||||
domainDict = [NSMutableDictionary dictionary];
|
||||
[newDict setObject: domainDict forKey: persistentDomain];
|
||||
}
|
||||
|
||||
if (dinfo->systemKey)
|
||||
{
|
||||
DWORD i = 0;
|
||||
unichar *name = malloc(200);
|
||||
unichar *data = malloc(1000);
|
||||
DWORD namelenbuf = 100, datalenbuf = 1000;
|
||||
DWORD type;
|
||||
|
||||
do
|
||||
{
|
||||
DWORD namelen = namelenbuf, datalen = datalenbuf;
|
||||
|
||||
rc = RegEnumValueW(dinfo->systemKey,
|
||||
i,
|
||||
name,
|
||||
&namelen,
|
||||
NULL,
|
||||
&type,
|
||||
(void*)data,
|
||||
&datalen);
|
||||
if (rc == ERROR_SUCCESS)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
id v;
|
||||
NSString *k;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case REG_SZ:
|
||||
{
|
||||
int datacharlen = datalen / 2;
|
||||
if (datacharlen > 0 && data[datacharlen-1] == 0)
|
||||
datacharlen--;
|
||||
|
||||
v = [NSString stringWithCharacters: data
|
||||
length: datacharlen];
|
||||
}
|
||||
break;
|
||||
case REG_BINARY:
|
||||
{
|
||||
v = [NSString stringWithCString: (char*)data
|
||||
encoding: NSASCIIStringEncoding];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Bad registry type %d for '%S'", type, name);
|
||||
v = 0;
|
||||
}
|
||||
v = [v propertyList];
|
||||
if (v)
|
||||
{
|
||||
k = [NSString stringWithCharacters: name
|
||||
length: namelen];
|
||||
[domainDict setObject: v forKey: k];
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
NSLog(@"Bad registry value for '%S'", name);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
else if (rc == ERROR_MORE_DATA)
|
||||
{
|
||||
if (namelen >= namelenbuf)
|
||||
{
|
||||
namelenbuf = namelen + 1;
|
||||
name = realloc(name, namelenbuf * sizeof(unichar));
|
||||
}
|
||||
if (datalen >= datalenbuf)
|
||||
{
|
||||
datalenbuf = datalen + 1;
|
||||
data = realloc(data, datalenbuf);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (rc == ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"RegEnumValueW error %d", rc);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} while (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA);
|
||||
free(name);
|
||||
free(data);
|
||||
}
|
||||
|
||||
if (dinfo->userKey)
|
||||
{
|
||||
DWORD i = 0;
|
||||
unichar *name = malloc(200);
|
||||
unichar *data = malloc(1000);
|
||||
DWORD namelenbuf = 100, datalenbuf = 1000;
|
||||
DWORD type;
|
||||
|
||||
do
|
||||
{
|
||||
DWORD namelen = namelenbuf, datalen = datalenbuf;
|
||||
|
||||
// RegEnumValueW returns the data as a wide string
|
||||
// but returns the length in bytes.
|
||||
// To add insult to injury, datalen includes the terminating
|
||||
// NULL character, unless there isn't enough room, in which
|
||||
// case it doesn't.
|
||||
|
||||
rc = RegEnumValueW(dinfo->userKey,
|
||||
i,
|
||||
name,
|
||||
&namelen,
|
||||
NULL,
|
||||
&type,
|
||||
(void*)data,
|
||||
&datalen);
|
||||
if (rc == ERROR_SUCCESS)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
id v;
|
||||
NSString *k;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case REG_SZ:
|
||||
{
|
||||
int datacharlen = datalen / 2;
|
||||
if (datacharlen > 0 && data[datacharlen-1] == 0)
|
||||
datacharlen--;
|
||||
|
||||
v = [NSString stringWithCharacters: data
|
||||
length: datacharlen];
|
||||
}
|
||||
break;
|
||||
case REG_BINARY:
|
||||
{
|
||||
v = [NSString stringWithCString: (char*)data
|
||||
encoding: NSASCIIStringEncoding];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Bad registry type %d for '%S'", type, name);
|
||||
v = 0;
|
||||
}
|
||||
v = [v propertyList];
|
||||
if (v)
|
||||
{
|
||||
k = [NSString stringWithCharacters: name
|
||||
length: namelen];
|
||||
[domainDict setObject: v forKey: k];
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
NSLog(@"Bad registry value for '%S'", name);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
else if (rc == ERROR_MORE_DATA)
|
||||
{
|
||||
if (namelen >= namelenbuf)
|
||||
{
|
||||
namelenbuf = namelen + 1;
|
||||
name = realloc(name, namelenbuf * sizeof(unichar));
|
||||
}
|
||||
if (datalen >= datalenbuf)
|
||||
{
|
||||
datalenbuf = datalen + 1;
|
||||
data = realloc(data, datalenbuf);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (rc == ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"RegEnumValueW error %d", rc);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} while (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA);
|
||||
free(name);
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
return newDict;
|
||||
}
|
||||
|
||||
- (void) unlockDefaultsFile
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
- (BOOL) wantToReadDefaultsSince: (NSDate*)lastSyncDate
|
||||
{
|
||||
if (lastSyncDate != nil && registryInfo != 0)
|
||||
{
|
||||
// Detect changes in the registry
|
||||
NSMapEnumerator iter;
|
||||
NSString *domain;
|
||||
struct NSUserDefaultsWin32_DomainInfo *dinfo;
|
||||
|
||||
iter = NSEnumerateMapTable(registryInfo);
|
||||
while (NSNextMapEnumeratorPair(&iter, (void**)&domain, (void**)&dinfo))
|
||||
{
|
||||
ULARGE_INTEGER lasttime;
|
||||
LONG rc;
|
||||
NSTimeInterval ti;
|
||||
NSString *dPath;
|
||||
|
||||
dPath = [registryPrefix stringByAppendingString: domain];
|
||||
|
||||
if (dinfo->userKey)
|
||||
{
|
||||
rc = RegQueryInfoKey(dinfo->userKey,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL,NULL, NULL, (PFILETIME)&lasttime);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSString *dName = [@"HKEY_CURRENT_USER\\"
|
||||
stringByAppendingString: dPath];
|
||||
|
||||
NSLog(@"Failed to query modify time on registry %@ (%x)",
|
||||
dName, rc);
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return YES;
|
||||
}
|
||||
ti = -12622780800.0 + lasttime.QuadPart / 10000000.0;
|
||||
if ([lastSyncDate timeIntervalSinceReferenceDate] < ti)
|
||||
{
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the key didn't exist, but now it does, we want to read it.
|
||||
rc = RegOpenKeyExW(HKEY_CURRENT_USER,
|
||||
UNISTR(dPath),
|
||||
0,
|
||||
STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ
|
||||
|KEY_SET_VALUE|KEY_QUERY_VALUE,
|
||||
&(dinfo->userKey));
|
||||
if (rc == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
dinfo->userKey = 0;
|
||||
}
|
||||
else if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSString *dName = [@"HKEY_CURRENT_USER\\"
|
||||
stringByAppendingString: dPath];
|
||||
|
||||
NSLog(@"Failed to open registry %@ (%x)", dName, rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
if (dinfo->systemKey)
|
||||
{
|
||||
rc = RegQueryInfoKey(dinfo->systemKey,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, (PFILETIME)&lasttime);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to query time on HKEY_LOCAL_MACHINE\\%@ (%x)",
|
||||
dPath, rc);
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return YES;
|
||||
}
|
||||
ti = -12622780800.0 + lasttime.QuadPart / 10000000.0;
|
||||
if ([lastSyncDate timeIntervalSinceReferenceDate] < ti)
|
||||
{
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the key didn't exist, but now it does, we want to read it.
|
||||
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
UNISTR(dPath),
|
||||
0,
|
||||
STANDARD_RIGHTS_READ|KEY_QUERY_VALUE,
|
||||
&(dinfo->systemKey));
|
||||
if (rc == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
dinfo->systemKey = 0;
|
||||
}
|
||||
else if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to open registry HKEY_LOCAL_MACHINE\\%@ (%x)",
|
||||
dPath, rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
NSEndMapTableEnumeration(&iter);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) writeDefaults: (NSDictionary*)defaults oldData: (NSDictionary*)oldData
|
||||
{
|
||||
NSEnumerator *iter;
|
||||
NSString *persistentDomain;
|
||||
|
||||
if (registryInfo == 0)
|
||||
{
|
||||
registryInfo = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||
NSOwnedPointerMapValueCallBacks, [defaults count]);
|
||||
}
|
||||
|
||||
iter = [defaults keyEnumerator];
|
||||
while ((persistentDomain = [iter nextObject]) != nil)
|
||||
{
|
||||
struct NSUserDefaultsWin32_DomainInfo *dinfo;
|
||||
NSDictionary *domainDict;
|
||||
NSDictionary *oldDomainDict;
|
||||
NSString *dPath;
|
||||
LONG rc;
|
||||
NSEnumerator *valIter;
|
||||
NSString *valName;
|
||||
|
||||
dinfo = NSMapGet(registryInfo, persistentDomain);
|
||||
if (dinfo == 0)
|
||||
{
|
||||
dinfo = calloc(sizeof(struct NSUserDefaultsWin32_DomainInfo), 1);
|
||||
NSMapInsertKnownAbsent(registryInfo, persistentDomain, dinfo);
|
||||
}
|
||||
|
||||
domainDict = [defaults objectForKey: persistentDomain];
|
||||
oldDomainDict = [oldData objectForKey: persistentDomain];
|
||||
dPath = [registryPrefix stringByAppendingString: persistentDomain];
|
||||
|
||||
if ([domainDict count] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (dinfo->userKey == 0)
|
||||
{
|
||||
rc = RegCreateKeyExW(HKEY_CURRENT_USER,
|
||||
UNISTR(dPath),
|
||||
0,
|
||||
(LPWSTR) L"",
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ|KEY_SET_VALUE
|
||||
|KEY_QUERY_VALUE,
|
||||
NULL,
|
||||
&(dinfo->userKey),
|
||||
NULL);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to create registry HKEY_CURRENT_USER\\%@ (%x)",
|
||||
dPath, rc);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
valIter = [domainDict keyEnumerator];
|
||||
while ((valName = [valIter nextObject]))
|
||||
{
|
||||
id value = [domainDict objectForKey: valName];
|
||||
id oldvalue = [oldDomainDict objectForKey: valName];
|
||||
|
||||
if (oldvalue == nil || [value isEqual: oldvalue] == NO)
|
||||
{
|
||||
NSString *result = nil;
|
||||
const unichar *ptr;
|
||||
|
||||
GSPropertyListMake(value, nil, NO, NO, 0, &result);
|
||||
ptr = UNISTR(result);
|
||||
rc = RegSetValueExW(dinfo->userKey,
|
||||
UNISTR(valName),
|
||||
0,
|
||||
REG_SZ,
|
||||
(void*)ptr,
|
||||
2*(wcslen(ptr) + 1));
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to insert HKEY_CURRENT_USER\\%@\\%@ (%x)",
|
||||
dPath, valName, rc);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Enumerate over the oldvalues and delete the deleted keys.
|
||||
valIter = [oldDomainDict keyEnumerator];
|
||||
while ((valName = [valIter nextObject]) != nil)
|
||||
{
|
||||
if ([domainDict objectForKey: valName] == nil)
|
||||
{
|
||||
// Delete value from registry
|
||||
rc = RegDeleteValueW(dinfo->userKey, UNISTR(valName));
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
NSLog(@"Failed to delete HKEY_CURRENT_USER\\%@\\%@ (%x)",
|
||||
dPath, valName, rc);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in a new issue