blob: 2f701dcf5f49d31fd110eafefb814c5e510e1f6b [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
import CoreFoundation
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif
public class NSFileHandle : NSObject, NSSecureCoding {
internal var _fd: Int32
internal var _closeOnDealloc: Bool
internal var _closed: Bool = false
/*@NSCopying*/ public var availableData: NSData {
NSUnimplemented()
}
public func readDataToEndOfFile() -> NSData {
return readDataOfLength(Int.max)
}
public func readDataOfLength(_ length: Int) -> NSData {
var statbuf = stat()
var dynamicBuffer: UnsafeMutablePointer<UInt8>? = nil
var total = 0
if _closed || fstat(_fd, &statbuf) < 0 {
fatalError("Unable to read file")
}
if statbuf.st_mode & S_IFMT != S_IFREG {
/* We get here on sockets, character special files, FIFOs ... */
var currentAllocationSize: size_t = 1024 * 8
dynamicBuffer = UnsafeMutablePointer<UInt8>(malloc(currentAllocationSize))
var remaining = length
while remaining > 0 {
let amountToRead = min(1024 * 8, remaining)
// Make sure there is always at least amountToRead bytes available in the buffer.
if (currentAllocationSize - total) < amountToRead {
currentAllocationSize *= 2
dynamicBuffer = UnsafeMutablePointer<UInt8>(_CFReallocf(UnsafeMutablePointer<Void>(dynamicBuffer!), currentAllocationSize))
if dynamicBuffer == nil {
fatalError("unable to allocate backing buffer")
}
let amtRead = read(_fd, dynamicBuffer!.advanced(by: total), amountToRead)
if 0 > amtRead {
free(dynamicBuffer)
fatalError("read failure")
}
if 0 == amtRead {
break // EOF
}
total += amtRead
remaining -= amtRead
if total == length {
break // We read everything the client asked for.
}
}
}
} else {
let offset = lseek(_fd, 0, L_INCR)
if offset < 0 {
fatalError("Unable to fetch current file offset")
}
if statbuf.st_size > offset {
var remaining = size_t(statbuf.st_size - offset)
remaining = min(remaining, size_t(length))
dynamicBuffer = UnsafeMutablePointer<UInt8>(malloc(remaining))
if dynamicBuffer == nil {
fatalError("Malloc failure")
}
while remaining > 0 {
let count = read(_fd, dynamicBuffer!.advanced(by: total), remaining)
if count < 0 {
free(dynamicBuffer)
fatalError("Unable to read from fd")
}
if count == 0 {
break
}
total += count
remaining -= count
}
}
}
if length == Int.max && total > 0 {
dynamicBuffer = UnsafeMutablePointer<UInt8>(_CFReallocf(UnsafeMutablePointer<Void>(dynamicBuffer!), total))
}
if (0 == total) {
free(dynamicBuffer)
}
if total > 0 {
return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(dynamicBuffer!), length: total)
}
return NSData()
}
public func writeData(_ data: NSData) {
data.enumerateByteRangesUsingBlock() { (bytes, range, stop) in
do {
try NSData.writeToFileDescriptor(self._fd, path: nil, buf: bytes, length: range.length)
} catch {
fatalError("Write failure")
}
}
}
// TODO: Error handling.
public var offsetInFile: UInt64 {
return UInt64(lseek(_fd, 0, L_INCR))
}
public func seekToEndOfFile() -> UInt64 {
return UInt64(lseek(_fd, 0, L_XTND))
}
public func seekToFileOffset(_ offset: UInt64) {
lseek(_fd, off_t(offset), L_SET)
}
public func truncateFileAtOffset(_ offset: UInt64) {
if lseek(_fd, off_t(offset), L_SET) == 0 {
ftruncate(_fd, off_t(offset))
}
}
public func synchronizeFile() {
fsync(_fd)
}
public func closeFile() {
if !_closed {
close(_fd)
_closed = true
}
}
public init(fileDescriptor fd: Int32, closeOnDealloc closeopt: Bool) {
_fd = fd
_closeOnDealloc = closeopt
}
internal init?(path: String, flags: Int32, createMode: Int) {
_fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
_closeOnDealloc = true
super.init()
if _fd < 0 {
return nil
}
}
deinit {
if _fd >= 0 && _closeOnDealloc && !_closed {
close(_fd)
}
}
public required init?(coder: NSCoder) {
NSUnimplemented()
}
public func encodeWithCoder(_ aCoder: NSCoder) {
NSUnimplemented()
}
public static func supportsSecureCoding() -> Bool {
return true
}
}
extension NSFileHandle {
internal static var _stdinFileHandle: NSFileHandle = {
return NSFileHandle(fileDescriptor: STDIN_FILENO, closeOnDealloc: false)
}()
public class func fileHandleWithStandardInput() -> NSFileHandle {
return _stdinFileHandle
}
internal static var _stdoutFileHandle: NSFileHandle = {
return NSFileHandle(fileDescriptor: STDOUT_FILENO, closeOnDealloc: false)
}()
public class func fileHandleWithStandardOutput() -> NSFileHandle {
return _stdoutFileHandle
}
internal static var _stderrFileHandle: NSFileHandle = {
return NSFileHandle(fileDescriptor: STDERR_FILENO, closeOnDealloc: false)
}()
public class func fileHandleWithStandardError() -> NSFileHandle {
return _stderrFileHandle
}
public class func fileHandleWithNullDevice() -> NSFileHandle {
NSUnimplemented()
}
public convenience init?(forReadingAtPath path: String) {
self.init(path: path, flags: O_RDONLY, createMode: 0)
}
public convenience init?(forWritingAtPath path: String) {
self.init(path: path, flags: O_WRONLY, createMode: 0)
}
public convenience init?(forUpdatingAtPath path: String) {
self.init(path: path, flags: O_RDWR, createMode: 0)
}
internal static func _openFileDescriptorForURL(_ url : NSURL, flags: Int32, reading: Bool) throws -> Int32 {
if let path = url.path {
let fd = _CFOpenFile(path, flags)
if fd < 0 {
throw _NSErrorWithErrno(errno, reading: reading, url: url)
}
return fd
} else {
throw _NSErrorWithErrno(ENOENT, reading: reading, url: url)
}
}
public convenience init(forReadingFromURL url: NSURL) throws {
let fd = try NSFileHandle._openFileDescriptorForURL(url, flags: O_RDONLY, reading: true)
self.init(fileDescriptor: fd, closeOnDealloc: true)
}
public convenience init(forWritingToURL url: NSURL) throws {
let fd = try NSFileHandle._openFileDescriptorForURL(url, flags: O_WRONLY, reading: false)
self.init(fileDescriptor: fd, closeOnDealloc: true)
}
public convenience init(forUpdatingURL url: NSURL) throws {
let fd = try NSFileHandle._openFileDescriptorForURL(url, flags: O_RDWR, reading: false)
self.init(fileDescriptor: fd, closeOnDealloc: true)
}
}
public let NSFileHandleOperationException: String = "" // NSUnimplemented
public let NSFileHandleReadCompletionNotification: String = "" // NSUnimplemented
public let NSFileHandleReadToEndOfFileCompletionNotification: String = "" // NSUnimplemented
public let NSFileHandleConnectionAcceptedNotification: String = "" // NSUnimplemented
public let NSFileHandleDataAvailableNotification: String = "" // NSUnimplemented
public let NSFileHandleNotificationDataItem: String = "" // NSUnimplemented
public let NSFileHandleNotificationFileHandleItem: String = "" // NSUnimplemented
extension NSFileHandle {
public func readInBackgroundAndNotifyForModes(_ modes: [String]?) {
NSUnimplemented()
}
public func readInBackgroundAndNotify() {
NSUnimplemented()
}
public func readToEndOfFileInBackgroundAndNotifyForModes(_ modes: [String]?) {
NSUnimplemented()
}
public func readToEndOfFileInBackgroundAndNotify() {
NSUnimplemented()
}
public func acceptConnectionInBackgroundAndNotifyForModes(_ modes: [String]?) {
NSUnimplemented()
}
public func acceptConnectionInBackgroundAndNotify() {
NSUnimplemented()
}
public func waitForDataInBackgroundAndNotifyForModes(_ modes: [String]?) {
NSUnimplemented()
}
public func waitForDataInBackgroundAndNotify() {
NSUnimplemented()
}
public var readabilityHandler: ((NSFileHandle) -> Void)? {
NSUnimplemented()
}
public var writeabilityHandler: ((NSFileHandle) -> Void)? {
NSUnimplemented()
}
}
extension NSFileHandle {
public convenience init(fileDescriptor fd: Int32) {
self.init(fileDescriptor: fd, closeOnDealloc: false)
}
public var fileDescriptor: Int32 {
return _fd
}
}
public class NSPipe : NSObject {
private let readHandle: NSFileHandle
private let writeHandle: NSFileHandle
public override init() {
/// the `pipe` system call creates two `fd` in a malloc'ed area
var fds = UnsafeMutablePointer<Int32>(allocatingCapacity: 2)
defer {
free(fds)
}
/// If the operating system prevents us from creating file handles, stop
guard pipe(fds) == 0 else { fatalError("Could not open pipe file handles") }
/// The handles below auto-close when the `NSFileHandle` is deallocated, so we
/// don't need to add a `deinit` to this class
/// Create the read handle from the first fd in `fds`
self.readHandle = NSFileHandle(fileDescriptor: fds.pointee, closeOnDealloc: true)
/// Advance `fds` by one to create the write handle from the second fd
self.writeHandle = NSFileHandle(fileDescriptor: fds.successor().pointee, closeOnDealloc: true)
super.init()
}
public var fileHandleForReading: NSFileHandle {
return self.readHandle
}
public var fileHandleForWriting: NSFileHandle {
return self.writeHandle
}
}