// // File.swift // Swifter // // Copyright (c) 2014-2016 Damian KoĊ‚akowski. All rights reserved. // #if os(Linux) import Glibc #else import Foundation #endif public enum FileError: Error { case openFailed(String) case writeFailed(String) case readFailed(String) case seekFailed(String) case getCurrentWorkingDirectoryFailed(String) case isDirectoryFailed(String) case openDirFailed(String) } public class File { public static func openNewForWriting(_ path: String) throws -> File { return try openFileForMode(path, "wb") } public static func openForReading(_ path: String) throws -> File { return try openFileForMode(path, "rb") } public static func openForWritingAndReading(_ path: String) throws -> File { return try openFileForMode(path, "r+b") } public static func openFileForMode(_ path: String, _ mode: String) throws -> File { guard let file = path.withCString({ pathPointer in mode.withCString({ fopen(pathPointer, $0) }) }) else { throw FileError.openFailed(Errno.description()) } return File(file) } public static func isDirectory(_ path: String) throws -> Bool { var s = stat() guard path.withCString({ stat($0, &s) }) == 0 else { throw FileError.isDirectoryFailed(Errno.description()) } return s.st_mode & S_IFMT == S_IFDIR } public static func currentWorkingDirectory() throws -> String { guard let path = getcwd(nil, 0) else { throw FileError.getCurrentWorkingDirectoryFailed(Errno.description()) } return String(cString: path) } public static func exists(_ path: String) throws -> Bool { var buffer = stat() return path.withCString({ stat($0, &buffer) == 0 }) } public static func list(_ path: String) throws -> [String] { guard let dir = path.withCString({ opendir($0) }) else { throw FileError.openDirFailed(Errno.description()) } defer { closedir(dir) } var results = [String]() while let ent = readdir(dir) { var name = ent.pointee.d_name let fileName = withUnsafePointer(to: &name) { (ptr) -> String? in #if os(Linux) return String.fromCString([CChar](UnsafeBufferPointer(start: UnsafePointer(unsafeBitCast(ptr, UnsafePointer.self)), count: 256))) #else var buffer = [CChar](UnsafeBufferPointer(start: unsafeBitCast(ptr, to: UnsafePointer.self), count: Int(ent.pointee.d_namlen))) buffer.append(0) return String(validatingUTF8: buffer) #endif } if let fileName = fileName { results.append(fileName) } } return results } let pointer: UnsafeMutablePointer public init(_ pointer: UnsafeMutablePointer) { self.pointer = pointer } public func close() -> Void { fclose(pointer) } public func read( data: inout [UInt8]) throws -> Int { if data.count <= 0 { return data.count } let count = fread(&data, 1, data.count, self.pointer) if count == data.count { return count } if feof(self.pointer) != 0 { return count } if ferror(self.pointer) != 0 { throw FileError.readFailed(Errno.description()) } throw FileError.readFailed("Unknown file read error occured.") } public func write(data: [UInt8]) throws -> Void { if data.count <= 0 { return } try data.withUnsafeBufferPointer { if fwrite($0.baseAddress, 1, data.count, self.pointer) != data.count { throw FileError.writeFailed(Errno.description()) } } } public func seek(offset: Int) throws -> Void { if fseek(self.pointer, offset, SEEK_SET) != 0 { throw FileError.seekFailed(Errno.description()) } } } public func withNewFileOpenedForWriting(_ path: String, _ f: (File) throws -> Result) throws -> Result { return try withFileOpenedForMode(path, mode: "wb", f) } public func withFileOpenedForReading(_ path: String, _ f: (File) throws -> Result) throws -> Result { return try withFileOpenedForMode(path, mode: "rb", f) } public func withFileOpenedForWritingAndReading(_ path: String, _ f: (File) throws -> Result) throws -> Result { return try withFileOpenedForMode(path, mode: "r+b", f) } public func withFileOpenedForMode(_ path: String, mode: String, _ f: (File) throws -> Result) throws -> Result { let file = try File.openFileForMode(path, mode) defer { file.close() } return try f(file) }