Skip to content

SR-1005: [Foundation] Use NSFileHandle instead of fopen #292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions Sources/Build/PackageVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import POSIX
import PackageType
import class Utility.Git
import struct Utility.Path
import func libc.fclose
import func Utility.fopen
import func Utility.fputs

public func generateVersionData(_ rootDir: String, rootPackage: Package, externalPackages: [Package]) throws {
let dirPath = Path.join(rootDir, ".build/versionData")
Expand Down Expand Up @@ -73,9 +74,7 @@ func versionData(package: Package) -> String {

private func saveVersionData(_ dirPath: String, packageName: String, data: String) throws {
let filePath = Path.join(dirPath, "\(packageName).swift")
let file = try fopen(filePath, mode: .Write)
defer {
libc.fclose(file)
}
let file = try Utility.fopen(filePath, mode: .Write)
defer { file.closeFile() }
try fputs(data, file)
}
2 changes: 0 additions & 2 deletions Sources/Build/describe().swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

import func POSIX.getenv
import func POSIX.mkdir
import func POSIX.fopen
import func libc.fclose
import PackageType
import Utility

Expand Down
4 changes: 1 addition & 3 deletions Sources/Build/misc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import func POSIX.getenv
import func POSIX.popen
import func POSIX.mkdir
import func POSIX.fopen
import func libc.fclose
import PackageType
import Utility

Expand Down Expand Up @@ -119,7 +117,7 @@ extension ClangModule {
try POSIX.mkdir(wd)
let moduleMapFile = Path.join(wd, self.moduleMap)
let moduleMap = try fopen(moduleMapFile, mode: .Write)
defer { fclose(moduleMap) }
defer { moduleMap.closeFile() }

var output = "module \(c99name) {\n"
switch type {
Expand Down
14 changes: 6 additions & 8 deletions Sources/ManifestSerializer/Manifest+parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
*/

import func POSIX.realpath
import func POSIX.unlink
import PackageDescription
import PackageType
import Utility
import func POSIX.fopen
import func libc.fileno
import func libc.unlink
import func libc.fclose
import func Utility.fopen

extension Manifest {
public init(path pathComponents: String..., baseURL: String, swiftc: String, libdir: String) throws {
Expand Down Expand Up @@ -71,14 +69,14 @@ private func parse(path manifestPath: String, swiftc: String, libdir: String) th
//Create and open a temporary file to write toml to
let filePath = Path.join(manifestPath.parentDirectory, ".Package.toml")
let fp = try fopen(filePath, mode: .Write)
defer { fclose(fp) }
defer { fp.closeFile() }

//Pass the fd in arguments
cmd += ["-fileno", "\(fileno(fp))"]
cmd += ["-fileno", "\(fp.fileDescriptor)"]
try system(cmd)

let toml = try File(path: filePath).enumerate().reduce("") { $0 + "\n" + $1 }
unlink(filePath) //Delete the temp file after reading it
let toml = try fopen(filePath).reduce("") { $0 + "\n" + $1 }
try unlink(filePath) //Delete the temp file after reading it

return toml != "" ? toml : nil
}
6 changes: 0 additions & 6 deletions Sources/POSIX/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ public enum SystemError: ErrorProtocol {
case chdir(Int32)
case close(Int32)
case dirfd(Int32, String)
case fopen(Int32, String)
case fputs
case fgetc(Int32)
case fread(Int32)
case getcwd(Int32)
Expand Down Expand Up @@ -58,10 +56,6 @@ extension SystemError: CustomStringConvertible {
return "close error: \(strerror(errno))"
case .dirfd(let errno, _):
return "dirfd error: \(strerror(errno))"
case .fopen(let errno, let path):
return "fopen error: \(strerror(errno)), \(path)"
case .fputs:
return "fputs error"
case .fgetc(let errno):
return "fgetc error: \(strerror(errno))"
case .fread(let errno):
Expand Down
25 changes: 0 additions & 25 deletions Sources/POSIX/fopen.swift

This file was deleted.

26 changes: 0 additions & 26 deletions Sources/POSIX/fputs.swift

This file was deleted.

3 changes: 2 additions & 1 deletion Sources/POSIX/getcwd.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import func libc.getcwd
import func libc.free
import func libc.exit
import func libc.fputs
import var libc.PATH_MAX
import var libc.stderr
import var libc.errno
Expand All @@ -28,7 +29,7 @@ import var libc.errno
public func getcwd() -> String {

@noreturn func error() {
try! fputs("error: no current directory\n", libc.stderr)
fputs("error: no current directory\n", libc.stderr)
exit(2)
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/PkgConfig/PkgConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ struct PkgConfigParser {
return line
}

let file = File(path: self.pcFile)
for line in try file.enumerate() {
let file = try fopen(self.pcFile, mode: .Read)
for line in file {
// Remove commented or any trailing comment from the line.
let uncommentedLine = removeComment(line: line)
// Ignore any empty or whitespace line.
Expand Down
8 changes: 8 additions & 0 deletions Sources/Utility/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
public enum Error: ErrorProtocol {
case ObsoleteGitVersion
case UnknownGitError
case UnicodeDecodingError
case UnicodeEncodingError
case CouldNotCreateFile(path: String)
case FileDoesNotExist(path: String)
}

extension Error: CustomStringConvertible {
Expand All @@ -20,6 +24,10 @@ extension Error: CustomStringConvertible {
return "Git 2.0 or higher is required. Please update git and retry."
case .UnknownGitError:
return "Failed to invoke git command. Please try updating git"
case .UnicodeDecodingError: return "Could not decode input file into unicode"
case .UnicodeEncodingError: return "Could not encode string into unicode"
case .CouldNotCreateFile(let path): return "Could not create file: \(path)"
case .FileDoesNotExist(let path): return "File does not exist: \(path)"
}
}
}
123 changes: 65 additions & 58 deletions Sources/Utility/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,75 +8,82 @@
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import struct libc.FILE
import func POSIX.fopen
import func libc.fclose
import func libc.ferror
import func libc.fgetc
import var libc.EOF
import Foundation

/**
An instance of `File` represents a file with a defined path on
the filesystem that may or may not exist.
*/
public struct File {
let path: String
public enum FopenMode: String {
case Read = "r"
case Write = "w"
}

public init(path: String...) {
self.path = Path.join(path)
public func fopen(_ path: String, mode: FopenMode = .Read) throws -> NSFileHandle {
let handle: NSFileHandle!
switch mode {
case .Read: handle = NSFileHandle(forReadingAtPath: path)
case .Write:
#if os(OSX) || os(iOS)
guard NSFileManager.`default`().createFile(atPath: path, contents: nil) else {
throw Error.CouldNotCreateFile(path: path)
}
#else
guard NSFileManager.defaultManager().createFile(atPath: path, contents: nil) else {
throw Error.CouldNotCreateFile(path: path)
}
#endif
handle = NSFileHandle(forWritingAtPath: path)
}

/**
Returns a generator for the file contents separated by the
provided character.

Character must be representable as a single 8 bit integer.

Generator ends at EOF or on read error, we cannot report the
read error, so to detect this query `ferror`.

In the event of read-error we do not feed a partially generated
line before ending iteration.
*/
public func enumerate(_ separator: UnicodeScalar = "\n") throws -> FileLineGenerator {
return try FileLineGenerator(path: path, separator: separator)
guard handle != nil else {
throw Error.FileDoesNotExist(path: path)
}
return handle
}

/**
- See: File.enumerate
*/
public class FileLineGenerator: IteratorProtocol, Sequence {
private let fp: UnsafeMutablePointer<FILE>
private let separator: Int32
public func fopen(_ path: String..., mode: FopenMode = .Read, body: (NSFileHandle) throws -> Void) throws {
let fp = try fopen(Path.join(path), mode: mode)
defer { fp.closeFile() }
try body(fp)
}

init(path: String, separator c: UnicodeScalar) throws {
separator = Int32(UInt8(ascii: c))
fp = try fopen(path)
public func fputs(_ string: String, _ handle: NSFileHandle) throws {
guard let data = string.data(using: NSUTF8StringEncoding) else {
throw Error.UnicodeEncodingError
}

deinit {
fclose(fp)
}
#if os(OSX) || os(iOS)
handle.write(data)
#else
handle.writeData(data)
#endif
}

public func next() -> String? {
var out = ""
while true {
let c = fgetc(fp)
if c == EOF {
let err = ferror(fp)
if err == 0 {
// if we have some string, return it, then next next() we will
// immediately EOF and stop generating
return out.isEmpty ? nil : out
} else {
return nil
}
}
if c == separator { return out }
public func fputs(_ bytes: [UInt8], _ handle: NSFileHandle) throws {
var bytes = bytes
let data = NSData(bytes: &bytes, length: bytes.count)

#if os(OSX) || os(iOS)
handle.write(data)
#else
handle.writeData(data)
#endif
}


extension NSFileHandle: Sequence {
public func enumerate(separatedBy separator: String = "\n") throws -> IndexingIterator<[String]> {
guard let contents = String(data: readDataToEndOfFile(), encoding: NSUTF8StringEncoding) else {
throw Error.UnicodeDecodingError
}

if contents == "" {
return [].makeIterator()
}

return contents.components(separatedBy: separator).makeIterator()
}

// fgetc is documented to return unsigned char converted to an int
out.append(UnicodeScalar(UInt8(c)))
public func makeIterator() -> IndexingIterator<[String]> {
guard let iterator = try? enumerate() else {
return [].makeIterator()
}
return iterator
}
}
23 changes: 0 additions & 23 deletions Sources/Utility/POSIXConvenience.swift

This file was deleted.

Loading