147 lines
4.7 KiB
Swift
147 lines
4.7 KiB
Swift
/*
|
|
* Released under the MIT License (MIT), http://opensource.org/licenses/MIT
|
|
*
|
|
* Copyright (c) 2015 Kåre Morstøl, NotTooBad Software (nottoobadsoftware.com)
|
|
*
|
|
*/
|
|
|
|
import Foundation
|
|
|
|
public protocol ShellContextType {
|
|
var encoding: NSStringEncoding {get set}
|
|
var env: [String: String] {get set}
|
|
|
|
var stdin: ReadableStream {get set}
|
|
var stdout: WriteableStream {get set}
|
|
var stderror: WriteableStream {get set}
|
|
|
|
/**
|
|
The current working directory.
|
|
|
|
Must be used instead of `run("cd", "...")` because all the `run` commands are executed in a
|
|
separate process and changing the directory there will not affect the rest of the Swift script.
|
|
*/
|
|
var currentdirectory: String {get set}
|
|
}
|
|
|
|
|
|
public struct ShellContext: ShellContextType {
|
|
public var encoding: NSStringEncoding
|
|
public var env: [String: String]
|
|
|
|
public var stdin: ReadableStream
|
|
public var stdout: WriteableStream
|
|
public var stderror: WriteableStream
|
|
|
|
/**
|
|
The current working directory.
|
|
|
|
Must be used instead of `run("cd", "...")` because all the `run` commands are executed in a
|
|
separate process and changing the directory there will not affect the rest of the Swift script.
|
|
*/
|
|
public var currentdirectory: String
|
|
|
|
/** Creates a blank ShellContext. */
|
|
public init () {
|
|
encoding = NSUTF8StringEncoding
|
|
env = [String:String]()
|
|
|
|
stdin = ReadableStream(NSFileHandle.nullDevice(), encoding: encoding)
|
|
stdout = WriteableStream(NSFileHandle.nullDevice(), encoding: encoding)
|
|
stderror = WriteableStream(NSFileHandle.nullDevice(), encoding: encoding)
|
|
|
|
currentdirectory = main.currentdirectory
|
|
}
|
|
|
|
/** Creates a new ShellContext from another ShellContextType. */
|
|
public init (_ context: ShellContextType) {
|
|
encoding = context.encoding
|
|
env = context.env
|
|
|
|
stdin = context.stdin
|
|
stdout = context.stdout
|
|
stderror = context.stderror
|
|
|
|
currentdirectory = context.currentdirectory
|
|
}
|
|
}
|
|
|
|
extension ShellContext: ShellRunnable {
|
|
public var shellcontext: ShellContextType { return self }
|
|
}
|
|
|
|
|
|
private func createTempdirectory () -> String {
|
|
let name = NSURL(fileURLWithPath: main.path).lastPathComponent ?? "SwiftShell"
|
|
let tempdirectory = NSURL(fileURLWithPath:NSTemporaryDirectory()) + (name + "-" + NSProcessInfo.processInfo().globallyUniqueString)
|
|
do {
|
|
try Files.createDirectory(atPath: tempdirectory.path!, withIntermediateDirectories: true, attributes: nil)
|
|
return tempdirectory.path! + "/"
|
|
} catch let error as NSError {
|
|
exit(errormessage: "Could not create new temporary directory '\(tempdirectory)':\n\(error.localizedDescription)", errorcode: error.code)
|
|
} catch {
|
|
exit(errormessage: "Unexpected error: \(error)")
|
|
}
|
|
}
|
|
|
|
extension Process {
|
|
|
|
/** Workaround for nil crash in Process.arguments when run in Xcode. */
|
|
static var safeArguments: [String] {
|
|
return self.argc == 0 ? [] : self.arguments
|
|
}
|
|
}
|
|
|
|
public final class MainShellContext: ShellContextType {
|
|
|
|
/**
|
|
The default character encoding for SwiftShell.
|
|
|
|
TODO: get encoding from environmental variable LC_CTYPE.
|
|
*/
|
|
public var encoding = NSUTF8StringEncoding
|
|
public lazy var env = NSProcessInfo.processInfo().environment as [String: String]
|
|
|
|
public lazy var stdin: ReadableStream = { ReadableStream(NSFileHandle.standardInput(), encoding: self.encoding) }()
|
|
public lazy var stdout: WriteableStream = { WriteableStream(NSFileHandle.standardOutput(), encoding: self.encoding) }()
|
|
public lazy var stderror: WriteableStream = { WriteableStream(NSFileHandle.standardError(), encoding: self.encoding) }()
|
|
|
|
/**
|
|
The current working directory.
|
|
|
|
Must be used instead of `run("cd", "...")` because all the `run` commands are executed in
|
|
separate processes and changing the directory there will not affect the rest of the Swift script.
|
|
|
|
This directory is also used as the base for relative NSURLs.
|
|
*/
|
|
public var currentdirectory: String {
|
|
get { return Files.currentDirectoryPath + "/" }
|
|
set {
|
|
if !Files.changeCurrentDirectoryPath(newValue) {
|
|
exit(errormessage: "Could not change the working directory to \(newValue)")
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
The tempdirectory is unique each time a script is run and is created the first time it is used.
|
|
It lies in the user's temporary directory and will be automatically deleted at some point.
|
|
*/
|
|
public lazy var tempdirectory: String = createTempdirectory()
|
|
|
|
/** The arguments this executable was launched with. Use main.path to get the path. */
|
|
public lazy var arguments: [String] = Array(Process.safeArguments.dropFirst())
|
|
|
|
/** The path to the currently running executable. Will be empty in playgrounds. */
|
|
public lazy var path: String = Process.safeArguments.first ?? ""
|
|
|
|
private init() {
|
|
}
|
|
}
|
|
|
|
extension MainShellContext: ShellRunnable {
|
|
public var shellcontext: ShellContextType { return self }
|
|
}
|
|
|
|
public let main = MainShellContext()
|