Skip to content

Commit

Permalink
Finish NSFileHandle's stubbed methods (#2477)
Browse files Browse the repository at this point in the history
This commit marks roughly half of NSFileHandle's APIs NotInPlan.
WinSock deals in opaque handles which cannot be used with
open/close/seek/tell/read/write, so most of NSFileHandle's APIs cannot
be supported with the POSIX File Descriptor interface.

The background APIs are better suited to use with a socket than with a
file, so until somebody asks for socket support, we will mark them
unplanned.

File handles cannot be coded except through IPC coders, but WinObjC does
not have any IPC coders.

Fixes #2367.
  • Loading branch information
DHowett authored Apr 11, 2017
1 parent 5f1c496 commit 4c0ff52
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 103 deletions.
261 changes: 180 additions & 81 deletions Frameworks/Foundation/NSFileHandle.mm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//******************************************************************************
//
// Copyright (c) 2016 Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
Expand Down Expand Up @@ -28,6 +28,10 @@
NSString* const NSFileHandleNotificationDataItem = @"NSFileHandleNotificationDataItem";
NSString* const NSFileHandleOperationException = @"NSFileHandleOperationException";
NSString* const NSFileHandleNotificationMonitorModes = @"NSFileHandleNotificationMonitorModes";
NSString* const NSFileHandleConnectionAcceptedNotification = @"NSFileHandleConnectionAcceptedNotification";
NSString* const NSFileHandleDataAvailableNotification = @"NSFileHandleDataAvailableNotification";
NSString* const NSFileHandleReadCompletionNotification = @"NSFileHandleReadCompletionNotification";
NSString* const NSFileHandleReadToEndOfFileCompletionNotification = @"NSFileHandleReadToEndOfFileCompletionNotification";

typedef NS_ENUM(NSUInteger, _NSFileOpenMode) { _NSFileOpenModeRead, _NSFileOpenModeWrite, _NSFileOpenModeUpdate };

Expand All @@ -36,70 +40,12 @@ @implementation NSFileHandle {
int _fileDescriptor;
}

/**
@Status Interoperable
*/
- (instancetype)initWithFileDescriptor:(int)fileDescriptor closeOnDealloc:(BOOL)flag {
if (self = [super init]) {
_fileDescriptor = fileDescriptor;
_closeOnDealloc = flag;
}

return self;
}

/**
@Status Interoperable
*/
- (instancetype)initWithFileDescriptor:(int)fileDescriptor {
return [self initWithFileDescriptor:fileDescriptor closeOnDealloc:NO];
}

- (instancetype)_initWithFileAtPath:(NSString*)file openType:(_NSFileOpenMode)type {
if (file == nil) {
[self release];
return nil;
}
if (self = [self initWithFileDescriptor:-1 closeOnDealloc:YES]) {
_fileDescriptor = [self _openFile:file openType:type];

if (_fileDescriptor == -1) {
TraceError(L"NSFileHandle", L"Unable to open file, errno:%d", errno);
[self release];
return nil;
}

if (type == _NSFileOpenModeUpdate) {
[self seekToFileOffset:0];
}
}

return self;
}

- (int)_openFile:(NSString*)file openType:(_NSFileOpenMode)type {
switch (type) {
case _NSFileOpenModeRead:
return EbrOpenWithPermission((char*)([file UTF8String]), O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD);
case _NSFileOpenModeWrite:
return EbrOpenWithPermission((char*)([file UTF8String]), _O_WRONLY | _O_BINARY | _O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE);
case _NSFileOpenModeUpdate:
return EbrOpenWithPermission((char*)([file UTF8String]),
O_RDWR | _O_CREAT | O_APPEND | _O_BINARY,
_SH_DENYNO,
_S_IREAD | _S_IWRITE);
default:
break;
}
return -1;
}

/**
@Status Caveat
@Notes NSFileHandle only supports file URLs.
*/
+ (instancetype)fileHandleForReadingFromURL:(NSURL*)url error:(NSError* _Nullable*)error {
return [NSFileHandle _fileHandleForURl:url error:error openType:_NSFileOpenModeRead];
return [NSFileHandle _fileHandleForURL:url error:error openType:_NSFileOpenModeRead];
}

/**
Expand All @@ -114,7 +60,7 @@ + (instancetype)fileHandleForReadingAtPath:(NSString*)file {
@Notes NSFileHandle only supports file URLs.
*/
+ (instancetype)fileHandleForWritingToURL:(NSURL*)url error:(NSError* _Nullable*)error {
return [NSFileHandle _fileHandleForURl:url error:error openType:_NSFileOpenModeWrite];
return [NSFileHandle _fileHandleForURL:url error:error openType:_NSFileOpenModeWrite];
}

/**
Expand All @@ -129,10 +75,10 @@ + (instancetype)fileHandleForWritingAtPath:(NSString*)file {
@Notes NSFileHandle only supports file URLs.
*/
+ (instancetype)fileHandleForUpdatingURL:(NSURL*)url error:(NSError* _Nullable*)error {
return [NSFileHandle _fileHandleForURl:url error:error openType:_NSFileOpenModeUpdate];
return [NSFileHandle _fileHandleForURL:url error:error openType:_NSFileOpenModeUpdate];
}

+ (instancetype)_fileHandleForURl:(NSURL*)url error:(NSError* _Nullable*)error openType:(_NSFileOpenMode)type {
+ (instancetype)_fileHandleForURL:(NSURL*)url error:(NSError* _Nullable*)error openType:(_NSFileOpenMode)type {
if (url == nil) {
return nil;
}
Expand Down Expand Up @@ -175,37 +121,103 @@ + (instancetype)fileHandleForUpdatingAtPath:(NSString*)file {
}

/**
@Status Stub
@Notes
@Status Caveat
@Notes UWPs do not have Standard I/O, so this API will return a null device.
*/
+ (NSFileHandle*)fileHandleWithStandardError {
UNIMPLEMENTED();
return StubReturn();
return [self fileHandleWithNullDevice];
}

/**
@Status Stub
@Notes
@Status Caveat
@Notes UWPs do not have Standard I/O, so this API will return a null device.
*/
+ (NSFileHandle*)fileHandleWithStandardInput {
UNIMPLEMENTED();
return StubReturn();
return [self fileHandleWithNullDevice];
}

/**
@Status Stub
@Notes
@Status Caveat
@Notes UWPs do not have Standard I/O, so this API will return a null device.
*/
+ (NSFileHandle*)fileHandleWithStandardOutput {
UNIMPLEMENTED();
return StubReturn();
return [self fileHandleWithNullDevice];
}

/**
@Status Interoperable
*/
+ (NSFileHandle*)fileHandleWithNullDevice {
return [[_NSFileHandleNullDevice new] autorelease];
static StrongId<NSFileHandle> nullDeviceHandle{ woc::TakeOwnership, [_NSFileHandleNullDevice new] };
return nullDeviceHandle;
}

/**
@Status Interoperable
*/
- (instancetype)initWithFileDescriptor:(int)fileDescriptor closeOnDealloc:(BOOL)flag {
if (self = [super init]) {
_fileDescriptor = fileDescriptor;
_closeOnDealloc = flag;
}

return self;
}

/**
@Status Interoperable
*/
- (instancetype)initWithFileDescriptor:(int)fileDescriptor {
return [self initWithFileDescriptor:fileDescriptor closeOnDealloc:NO];
}

- (instancetype)_initWithFileAtPath:(NSString*)file openType:(_NSFileOpenMode)type {
if (file == nil) {
[self release];
return nil;
}
if (self = [self initWithFileDescriptor:-1 closeOnDealloc:YES]) {
_fileDescriptor = [self _openFile:file openType:type];

if (_fileDescriptor == -1) {
TraceError(L"NSFileHandle", L"Unable to open file, errno:%d", errno);
[self release];
return nil;
}

if (type == _NSFileOpenModeUpdate) {
[self seekToFileOffset:0];
}
}

return self;
}

/**
@Status Interoperable
*/
- (void)dealloc {
if (_closeOnDealloc) {
[self closeFile];
}
[super dealloc];
}

- (int)_openFile:(NSString*)file openType:(_NSFileOpenMode)type {
switch (type) {
case _NSFileOpenModeRead:
return EbrOpenWithPermission((char*)([file UTF8String]), O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD);
case _NSFileOpenModeWrite:
return EbrOpenWithPermission((char*)([file UTF8String]), _O_WRONLY | _O_BINARY | _O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE);
case _NSFileOpenModeUpdate:
return EbrOpenWithPermission((char*)([file UTF8String]),
O_RDWR | _O_CREAT | O_APPEND | _O_BINARY,
_SH_DENYNO,
_S_IREAD | _S_IWRITE);
default:
break;
}
return -1;
}

/**
Expand Down Expand Up @@ -257,6 +269,7 @@ - (unsigned long long)offsetInFile {

/**
@Status Interoperable
@Notes Since NSFileHandle does not support communication channels, this will always read until EOF.
*/
- (NSData*)availableData {
return [self readDataToEndOfFile];
Expand Down Expand Up @@ -356,10 +369,96 @@ - (void)synchronizeFile {
/**
@Status Interoperable
*/
- (void)dealloc {
if (_closeOnDealloc) {
[self closeFile];
}
[super dealloc];
- (id)initWithCoder:(NSCoder*)coder {
[self release];
[NSException raise:NSInvalidArgumentException format:@"%hs: file handles cannot be unarchived", __PRETTY_FUNCTION__];
return nil;
}

/**
@Status Interoperable
*/
- (void)encodeWithCoder:(NSCoder*)coder {
[NSException raise:NSInvalidArgumentException format:@"%hs: file handles cannot be archived", __PRETTY_FUNCTION__];
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)acceptConnectionInBackgroundAndNotify {
[self acceptConnectionInBackgroundAndNotifyForModes:@[ NSDefaultRunLoopMode ]];
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)acceptConnectionInBackgroundAndNotifyForModes:(NSArray*)modes {
UNIMPLEMENTED();
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)readInBackgroundAndNotify {
[self readInBackgroundAndNotifyForModes:@[ NSDefaultRunLoopMode ]];
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)readInBackgroundAndNotifyForModes:(NSArray*)modes {
UNIMPLEMENTED();
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)readToEndOfFileInBackgroundAndNotify {
[self readToEndOfFileInBackgroundAndNotifyForModes:@[ NSDefaultRunLoopMode ]];
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)readToEndOfFileInBackgroundAndNotifyForModes:(NSArray*)modes {
UNIMPLEMENTED();
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)waitForDataInBackgroundAndNotify {
[self waitForDataInBackgroundAndNotifyForModes:@[ NSDefaultRunLoopMode ]];
}

/**
@Status NotInPlan
@Notes The background APIs are intended for use with sockets but WinSock does not provide
a unified File Descriptor and Socket interface. As such, NSFileHandle cannot, as
specified, work with sockets.
*/
- (void)waitForDataInBackgroundAndNotifyForModes:(NSArray*)modes {
UNIMPLEMENTED();
}
@end
Loading

0 comments on commit 4c0ff52

Please sign in to comment.