Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
shiraji committed Oct 26, 2016
1 parent 9a32a54 commit 0650626
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 0 deletions.
6 changes: 6 additions & 0 deletions R.swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
3406F9501DB64E8C00351D0E /* IgnoreFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3406F94F1DB64E8C00351D0E /* IgnoreFile.swift */; };
3406F9511DB65FAB00351D0E /* IgnoreFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3406F94F1DB64E8C00351D0E /* IgnoreFile.swift */; };
3406F9581DBEA87500351D0E /* Glob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3406F9571DBEA87500351D0E /* Glob.swift */; };
3406F9591DBEA87500351D0E /* Glob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3406F9571DBEA87500351D0E /* Glob.swift */; };
5D45C2191CA12913000B8DC9 /* StringsStructGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D997C941C7C291900B2F376 /* StringsStructGenerator.swift */; };
5D997C951C7C291900B2F376 /* StringsStructGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D997C941C7C291900B2F376 /* StringsStructGenerator.swift */; };
D5214C1F1DA6AA9300212FF1 /* Struct+InternalProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5214C1E1DA6AA9300212FF1 /* Struct+InternalProperties.swift */; };
Expand Down Expand Up @@ -118,6 +120,7 @@

/* Begin PBXFileReference section */
3406F94F1DB64E8C00351D0E /* IgnoreFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoreFile.swift; sourceTree = "<group>"; };
3406F9571DBEA87500351D0E /* Glob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Glob.swift; sourceTree = "<group>"; };
5D997C941C7C291900B2F376 /* StringsStructGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringsStructGenerator.swift; sourceTree = "<group>"; };
D5214C1E1DA6AA9300212FF1 /* Struct+InternalProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Struct+InternalProperties.swift"; sourceTree = "<group>"; };
D5646DE11BE2016E0034F4D7 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -246,6 +249,7 @@
D5B7997C1C1B07C3009EA901 /* SwiftIdentifier.swift */,
D58672481C21FC9700A760EC /* TypeSequenceProvider.swift */,
D5EA0DFE1A3DF4E300FFEBC4 /* UtilExtensions.swift */,
3406F9571DBEA87500351D0E /* Glob.swift */,
);
path = Util;
sourceTree = "<group>";
Expand Down Expand Up @@ -498,6 +502,7 @@
D5B129B31C3BA75A00A1C5FC /* Let.swift in Sources */,
D5F97E491C1819160066D7C0 /* Font.swift in Sources */,
D5B799801C1B0943009EA901 /* SwiftIdentifier.swift in Sources */,
3406F9591DBEA87500351D0E /* Glob.swift in Sources */,
D5E890A71DA5A2550059D5ED /* AggregatedStructGenerator.swift in Sources */,
D5B799711C199755009EA901 /* FontStructGenerator.swift in Sources */,
D586D1C61BE20D8600F18FEC /* Extensions.swift in Sources */,
Expand Down Expand Up @@ -532,6 +537,7 @@
D5E890A51DA5A2290059D5ED /* AggregatedStructGenerator.swift in Sources */,
E2762AC71CCCEC2A0009BCAA /* Locale.swift in Sources */,
D5F97E3C1C1813A70066D7C0 /* Storyboard.swift in Sources */,
3406F9581DBEA87500351D0E /* Glob.swift in Sources */,
D59F723C1C1964B20089767C /* Typealias.swift in Sources */,
D5B799661C19939D009EA901 /* SegueGenerator.swift in Sources */,
D5DA249D1B9CB1BF00AAA43E /* StructGenerator.swift in Sources */,
Expand Down
197 changes: 197 additions & 0 deletions R.swift/Util/Glob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
//
// Created by Eric Firestone on 3/22/16.
// Copyright © 2016 Square, Inc. All rights reserved.
// Released under the Apache v2 License.
//
// Adapted from https://gist.github.com/blakemerryman/76312e1cbf8aec248167

import Foundation


let GlobBehaviorBashV3 = Glob.Behavior(
supportsGlobstar: false,
includesFilesFromRootOfGlobstar: false,
includesDirectoriesInResults: true,
includesFilesInResultsIfTrailingSlash: false
)
let GlobBehaviorBashV4 = Glob.Behavior(
supportsGlobstar: true, // Matches Bash v4 with "shopt -s globstar" option
includesFilesFromRootOfGlobstar: true,
includesDirectoriesInResults: true,
includesFilesInResultsIfTrailingSlash: false
)
let GlobBehaviorGradle = Glob.Behavior(
supportsGlobstar: true,
includesFilesFromRootOfGlobstar: true,
includesDirectoriesInResults: false,
includesFilesInResultsIfTrailingSlash: true
)


/**
Finds files on the file system using pattern matching.
*/
class Glob: CollectionType {

/**
* Different glob implementations have different behaviors, so the behavior of this
* implementation is customizable.
*/
struct Behavior {
// If true then a globstar ("**") causes matching to be done recursively in subdirectories.
// If false then "**" is treated the same as "*"
let supportsGlobstar: Bool

// If true the results from the directory where the globstar is declared will be included as well.
// For example, with the pattern "dir/**/*.ext" the fie "dir/file.ext" would be included if this
// property is true, and would be omitted if it's false.
let includesFilesFromRootOfGlobstar: Bool

// If false then the results will not include directory entries. This does not affect recursion depth.
let includesDirectoriesInResults: Bool

// If false and the last characters of the pattern are "**/" then only directories are returned in the results.
let includesFilesInResultsIfTrailingSlash: Bool
}

static var defaultBehavior = GlobBehaviorBashV4

private var isDirectoryCache = [String: Bool]()

let behavior: Behavior
var paths = [String]()
var startIndex: Int { return paths.startIndex }
var endIndex: Int { return paths.endIndex }

init(pattern: String, behavior: Behavior = Glob.defaultBehavior) {

self.behavior = behavior

var adjustedPattern = pattern
let hasTrailingGlobstarSlash = pattern.hasSuffix("**/")
var includeFiles = !hasTrailingGlobstarSlash

if behavior.includesFilesInResultsIfTrailingSlash {
includeFiles = true
if hasTrailingGlobstarSlash {
// Grab the files too.
adjustedPattern += "*"
}
}

let patterns = behavior.supportsGlobstar ? expandGlobstar(adjustedPattern) : [adjustedPattern]

for pattern in patterns {
var gt = glob_t()
if executeGlob(pattern, gt: &gt) {
populateFiles(gt, includeFiles: includeFiles)
}

globfree(&gt)
}

paths = Array(Set(paths)).sort { lhs, rhs in
lhs.compare(rhs) != NSComparisonResult.OrderedDescending
}

clearCaches()
}

// MARK: Private

private var globalFlags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK

private func executeGlob(pattern: UnsafePointer<CChar>, gt: UnsafeMutablePointer<glob_t>) -> Bool {
return 0 == glob(pattern, globalFlags, nil, gt)
}

private func expandGlobstar(pattern: String) -> [String] {
guard pattern.containsString("**") else {
return [pattern]
}

var results = [String]()
var parts = pattern.componentsSeparatedByString("**")
let firstPart = parts.removeFirst()
var lastPart = parts.joinWithSeparator("**")

let fileManager = NSFileManager.defaultManager()

var directories: [String]

do {
directories = try fileManager.subpathsOfDirectoryAtPath(firstPart).flatMap { subpath in
let fullPath = NSString(string: firstPart).stringByAppendingPathComponent(subpath)
var isDirectory = ObjCBool(false)
if fileManager.fileExistsAtPath(fullPath, isDirectory: &isDirectory) && isDirectory {
return fullPath
} else {
return nil
}
}
} catch {
directories = []
print("Error parsing file system item: \(error)")
}

if behavior.includesFilesFromRootOfGlobstar {
// Check the base directory for the glob star as well.
directories.insert(firstPart, atIndex: 0)

// Include the globstar root directory ("dir/") in a pattern like "dir/**" or "dir/**/"
if lastPart.isEmpty {
results.append(firstPart)
}
}

if lastPart.isEmpty {
lastPart = "*"
}
for directory in directories {
let partiallyResolvedPattern = NSString(string: directory).stringByAppendingPathComponent(lastPart)
results.appendContentsOf(expandGlobstar(partiallyResolvedPattern))
}

return results
}

private func isDirectory(path: String) -> Bool {
var isDirectory = isDirectoryCache[path]
if let isDirectory = isDirectory {
return isDirectory
}

var isDirectoryBool = ObjCBool(false)
isDirectory = NSFileManager.defaultManager().fileExistsAtPath(path, isDirectory: &isDirectoryBool) && isDirectoryBool
isDirectoryCache[path] = isDirectory!

return isDirectory!
}

private func clearCaches() {
isDirectoryCache.removeAll()
}

private func populateFiles(gt: glob_t, includeFiles: Bool) {
let includeDirectories = behavior.includesDirectoriesInResults

for i in 0..<Int(gt.gl_matchc) {
if let path = String.fromCString(gt.gl_pathv[i]) {
if !includeFiles || !includeDirectories {
let isDirectory = self.isDirectory(path)
if (!includeFiles && !isDirectory) || (!includeDirectories && isDirectory) {
continue
}
}

paths.append(path)
}
}
}

// MARK: Subscript Support

subscript(i: Int) -> String {
return paths[i]
}
}

0 comments on commit 0650626

Please sign in to comment.