Skip to content

Instantly share code, notes, and snippets.

@listrophy
Created June 25, 2015 17:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save listrophy/45b10cf2c958028ca57e to your computer and use it in GitHub Desktop.
Save listrophy/45b10cf2c958028ca57e to your computer and use it in GitHub Desktop.
Wrap glob(3) in Swift
//
// Glob.swift
//
// Created by Brad Grzesiak on 6/25/15.
// Copyright © 2015 Bendyworks Inc.
// Released under the Apache v2 License.
//
import Foundation
class Glob: CollectionType {
private var globFlags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK
var paths = [String]()
var startIndex: Int {
return paths.startIndex
}
var endIndex: Int {
return paths.endIndex
}
subscript(i: Int) -> String {
return paths[i]
}
init(pattern: String) {
var gt = glob_t()
if let cPatt = cPattern(pattern) {
if executeGlob(cPatt, gt: &gt) {
populateFiles(gt)
}
}
globfree(&gt)
}
private
func executeGlob(pattern: [CChar], gt: UnsafeMutablePointer<glob_t>) -> Bool {
return 0 == glob(pattern, globFlags, nil, gt)
}
private
func cPattern(pattern: String) -> [CChar]? {
return pattern.cStringUsingEncoding(NSUTF8StringEncoding)
}
private func populateFiles(gt: glob_t) {
for var i = 0; i < Int(gt.gl_matchc); i++ {
if let path = String.fromCString(gt.gl_pathv[i]) {
paths.append(path)
}
}
}
}
//
// GlobTester.swift
//
// Created by Brad Grzesiak on 6/25/15.
// Copyright © 2015 Bendyworks Inc.
// Released under the Apache v2 License.
//
import XCTest
class GlobTester: XCTestCase {
let tmpFiles = ["foo", "bar", "baz"]
var tmpDir: String?
override func setUp() {
super.setUp()
var tmpDirTmpl = "/tmp/glob-test.XXXXX".cStringUsingEncoding(NSUTF8StringEncoding)!
self.tmpDir = String.fromCString(mkdtemp(&tmpDirTmpl))
for file in tmpFiles {
close(open("\(tmpDir!)/\(file)", O_CREAT))
}
}
override func tearDown() {
for file in tmpFiles {
unlink("\(tmpDir!)/\(file)")
}
rmdir(self.tmpDir!)
super.tearDown()
}
func testBraces() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
var contents = [String]()
for file in glob {
contents.append(file)
}
XCTAssertEqual(contents, ["\(tmpDir!)/bar", "\(tmpDir!)/baz"], "matching with braces failed")
}
func testNothingMatches() {
let pattern = "\(tmpDir!)/nothing"
let glob = Glob(pattern: pattern)
var contents = [String]()
for file in glob {
contents.append(file)
}
XCTAssertEqual(contents, [], "expected empty list of files")
}
func testDirectAccess() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
XCTAssertEqual(glob.paths, ["\(tmpDir!)/bar", "\(tmpDir!)/baz"], "matching with braces failed")
}
func testIterateTwice() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
var contents1 = [String]()
var contents2 = [String]()
for file in glob {
contents1.append(file)
}
let filesAfterOnce = glob.paths
for file in glob {
contents2.append(file)
}
XCTAssertEqual(contents1, contents2, "results for calling for-in twice are the same")
XCTAssertEqual(glob.paths, filesAfterOnce, "calling for-in twice doesn't only memoizes once")
}
func testIndexing() {
let pattern = "\(tmpDir!)/ba{r,y,z}"
let glob = Glob(pattern: pattern)
XCTAssertEqual(glob[0], "\(tmpDir!)/bar", "indexing")
}
}
@blakemerryman
Copy link

I forked and updated this gist. Please consider incorporating my suggestions:

  • Remove dependency upon Foundation (which Linux won't have); replaced with Darwin (You were mostly making Darwin calls anyway)
  • Updated to Swift 2.0

I left the public API unchanged but I did not run any of your tests to ensure compatibility.

@psequel
Copy link

psequel commented Sep 11, 2016

func cPattern(pattern: String) -> [CChar]? {
    return pattern.cStringUsingEncoding(NSUTF8StringEncoding)
}

This is redundant, since Swift automatically converts strings into pointer to UTF8 buffers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment