<commit_before>/*
 * @file	CNFileManager.swift
 * @brief	Extend FileManager class
 * @par Copyright
 *   Copyright (C) 2018 Steel Wheels Project
 */

import Foundation

public enum CNFileType: Int32 {
	case NotExist		= 0
	case File		= 1
	case Directory		= 2

	public var description: String {
		get {
			var result: String
			switch self {
			case .NotExist:		result = "Not exist"
			case .File:		result = "File"
			case .Directory:	result = "Directory"
			}
			return result
		}
	}
}

public enum CNFileOpenResult {
	case 	ok(_ file: CNTextFile)
	case	error(_ error: NSError)
}

public enum CNFileAccessType: Int32 {
	case ReadAccess		= 0
	case WriteAccess	= 1
	case AppendAccess	= 2
}

public extension FileManager
{
	/* If you give full path to "relpath", "base" directory will be ignored */
	func fullPath(pathString path: String, baseURL base: URL) -> URL {
		let fullpath: String
		if self.isAbsolutePath(pathString: path) {
			fullpath   = path
		} else {
			let newurl = URL(fileURLWithPath: path, relativeTo: base)
			fullpath   = newurl.path
		}
		if let url = CNPreference.shared.bookmarkPreference.search(pathString: fullpath) {
			return url
		} else {
			return URL(fileURLWithPath: fullpath)
		}
	}

	func fileExists(atURL url: URL) -> Bool {
		return fileExists(atPath: url.path)
	}

	func createFile(atURL url: URL, contents data: Data?, attributes attr: [FileAttributeKey:Any]?) -> Bool {
		return createFile(atPath: url.absoluteString, contents: data, attributes: attr)
	}

	func checkFileType(pathString pathstr: String) -> CNFileType {
		var isdir    = ObjCBool(false)
		if fileExists(atPath: pathstr, isDirectory: &isdir) {
			if isdir.boolValue {
				return .Directory
			} else {
				return .File
			}
		} else {
			return .NotExist
		}
	}
	
	func openFile(URL url: URL, accessType acctyp: CNFileAccessType) -> CNFileOpenResult {
		do {
			var file: CNTextFile
			switch acctyp {
			case .ReadAccess:
				let handle = try FileHandle(forReadingFrom: url)
				file = CNTextFile(fileHandle: handle)
			case .WriteAccess:
				let handle = try FileHandle(forReadingFrom: url)
				file = CNTextFile(fileHandle: handle)
			case .AppendAccess:
				let handle = try FileHandle(forWritingTo: url)
				file = CNTextFile(fileHandle: handle)
			}
			return .ok(file)
		} catch let err as NSError {
			return .error(err)
		} catch {
			let err = NSError.fileError(message: "Failed to open URL \"\(url.absoluteString)\"")
			return .error(err)
		}
	}

	func schemeInPath(pathString str: String) -> String? {
		if let lastidx = str.firstIndex(of: ":") {
			var i:String.Index = str.startIndex
			var result: String = ""
			while i < lastidx {
				let c = str[i]
				if c.isLetterOrNumber || c == "." || c == "_" {
					result.append(c)
				} else {
					return nil
				}
				i = str.index(after: i)
			}
			return result
		} else {
			return nil
		}
	}

	/* The absolute path is
	 *  1. Started by '/'
	 *  2. Started by scheme, such as file:
	 */
	func isAbsolutePath(pathString path: String) -> Bool {
		if let c = path.first {
			if c == "/" || schemeInPath(pathString: path) != nil {
				return true
			} else {
				return false
			}
		} else {
			return false
		}
	}

	/* Reference: https://stackoverflow.com/questions/10512920/mac-sandbox-testing-whether-a-file-is-accessible*/
	func isAccessible(pathString path: String, accessType type: CNFileAccessType) -> Bool {
		let modes: Array<Int32>
		switch type {
		case .ReadAccess:	modes = [R_OK]
		case .WriteAccess:	modes = [W_OK]
		case .AppendAccess:	modes = [R_OK, W_OK]
		}

		let result: Bool
		let pref = CNPreference.shared.bookmarkPreference
		if let url = pref.search(pathString: path) {
			let issecure = url.startAccessingSecurityScopedResource()
			result = isAccessible(pathString: url.path, accessModes: modes)
			if issecure {
				url.stopAccessingSecurityScopedResource()
			}
		} else {
			result = isAccessible(pathString: path, accessModes: modes)
		}
		return result
	}

	private func isAccessible(pathString path: String, accessModes modes: Array<Int32>) -> Bool {
		for mode in modes {
			if access(path, mode) != 0 {
				return false
			}
		}
		return true
	}

	func setupFileSystem(console cons: CNConsole) -> NSError? {
		do {
			/* The base path is home directory */
			let homedir = CNPreference.shared.userPreference.homeDirectory

			/* Make default directories */
			let targetdirs: Array<String> = ["Library", "Sample", "Game"]
			for target in targetdirs {
				let dstdir = homedir.appendingPathComponent(target)
				switch checkFileType(pathString: dstdir.path) {
				case .Directory, .File:<commit_msg>CoconutData: CNFileManager update "setup" method<commit_after>/*
 * @file	CNFileManager.swift
 * @brief	Extend FileManager class
 * @par Copyright
 *   Copyright (C) 2018 Steel Wheels Project
 */

import Foundation

public enum CNFileType: Int32 {
	case NotExist		= 0
	case File		= 1
	case Directory		= 2

	public var description: String {
		get {
			var result: String
			switch self {
			case .NotExist:		result = "Not exist"
			case .File:		result = "File"
			case .Directory:	result = "Directory"
			}
			return result
		}
	}
}

public enum CNFileOpenResult {
	case 	ok(_ file: CNTextFile)
	case	error(_ error: NSError)
}

public enum CNFileAccessType: Int32 {
	case ReadAccess		= 0
	case WriteAccess	= 1
	case AppendAccess	= 2
}

public extension FileManager
{
	/* If you give full path to "relpath", "base" directory will be ignored */
	func fullPath(pathString path: String, baseURL base: URL) -> URL {
		let fullpath: String
		if self.isAbsolutePath(pathString: path) {
			fullpath   = path
		} else {
			let newurl = URL(fileURLWithPath: path, relativeTo: base)
			fullpath   = newurl.path
		}
		if let url = CNPreference.shared.bookmarkPreference.search(pathString: fullpath) {
			return url
		} else {
			return URL(fileURLWithPath: fullpath)
		}
	}

	func fileExists(atURL url: URL) -> Bool {
		return fileExists(atPath: url.path)
	}

	func createFile(atURL url: URL, contents data: Data?, attributes attr: [FileAttributeKey:Any]?) -> Bool {
		return createFile(atPath: url.absoluteString, contents: data, attributes: attr)
	}

	func checkFileType(pathString pathstr: String) -> CNFileType {
		var isdir    = ObjCBool(false)
		if fileExists(atPath: pathstr, isDirectory: &isdir) {
			if isdir.boolValue {
				return .Directory
			} else {
				return .File
			}
		} else {
			return .NotExist
		}
	}
	
	func openFile(URL url: URL, accessType acctyp: CNFileAccessType) -> CNFileOpenResult {
		do {
			var file: CNTextFile
			switch acctyp {
			case .ReadAccess:
				let handle = try FileHandle(forReadingFrom: url)
				file = CNTextFile(fileHandle: handle)
			case .WriteAccess:
				let handle = try FileHandle(forReadingFrom: url)
				file = CNTextFile(fileHandle: handle)
			case .AppendAccess:
				let handle = try FileHandle(forWritingTo: url)
				file = CNTextFile(fileHandle: handle)
			}
			return .ok(file)
		} catch let err as NSError {
			return .error(err)
		} catch {
			let err = NSError.fileError(message: "Failed to open URL \"\(url.absoluteString)\"")
			return .error(err)
		}
	}

	func schemeInPath(pathString str: String) -> String? {
		if let lastidx = str.firstIndex(of: ":") {
			var i:String.Index = str.startIndex
			var result: String = ""
			while i < lastidx {
				let c = str[i]
				if c.isLetterOrNumber || c == "." || c == "_" {
					result.append(c)
				} else {
					return nil
				}
				i = str.index(after: i)
			}
			return result
		} else {
			return nil
		}
	}

	/* The absolute path is
	 *  1. Started by '/'
	 *  2. Started by scheme, such as file:
	 */
	func isAbsolutePath(pathString path: String) -> Bool {
		if let c = path.first {
			if c == "/" || schemeInPath(pathString: path) != nil {
				return true
			} else {
				return false
			}
		} else {
			return false
		}
	}

	/* Reference: https://stackoverflow.com/questions/10512920/mac-sandbox-testing-whether-a-file-is-accessible*/
	func isAccessible(pathString path: String, accessType type: CNFileAccessType) -> Bool {
		let modes: Array<Int32>
		switch type {
		case .ReadAccess:	modes = [R_OK]
		case .WriteAccess:	modes = [W_OK]
		case .AppendAccess:	modes = [R_OK, W_OK]
		}

		let result: Bool
		let pref = CNPreference.shared.bookmarkPreference
		if let url = pref.search(pathString: path) {
			let issecure = url.startAccessingSecurityScopedResource()
			result = isAccessible(pathString: url.path, accessModes: modes)
			if issecure {
				url.stopAccessingSecurityScopedResource()
			}
		} else {
			result = isAccessible(pathString: path, accessModes: modes)
		}
		return result
	}

	private func isAccessible(pathString path: String, accessModes modes: Array<Int32>) -> Bool {
		for mode in modes {
			if access(path, mode) != 0 {
				return false
			}
		}
		return true
	}

	func setupFileSystem(console cons: CNConsole) -> NSError? {
		do {
			/* The base path is home directory */
			let homedir = CNPreference.shared.userPreference.homeDirectory

			/* Make default directories */
			let targetdirs: Array<String> = ["Sample", "Game"]
			for target in targetdirs {
				let dstdir = homedir.appendingPathComponent(target)
				switch checkFileType(pathString: dstdir.path) {
				case .Directory, .File:
{'fraction_non_alphanumeric': 0.10843634786380395, 'fraction_numerical': 0.006289308176100629, 'mean_word_length': 3.250230414746544, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 0, '>': 29, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1777714', 'n_tokens_mistral': 3643, 'n_tokens_neox': 3073, 'n_words': 866}