Ponyboy47/Pathman

View on GitHub
Sources/Pathman/Glob/Glob.swift

Summary

Maintainability
A
1 hr
Test Coverage
import Cglob

/// The construct used and returned by globbing
public final class Glob {
    // swiftlint:disable identifier_name
    /// A pointer to the underlying glob_t struct used for the C glob(3) API calls
    var _glob: UnsafeMutablePointer<glob_t>
    // swiftlint:enable identifier_name
    /// Whether or not this library owns the underlying glob_t struct and can
    /// safely free it's memory on deinitialization
    private let owned: Bool

    /// The number of matches that should be found in the matches collection
    public var trueCount: Int { return Int(count - offset) }
    /// The number of matches + the offset
    public var count: Int { return Int(_glob.pointee.gl_pathc) }
    /** The paths that matched the globbing pattern

     NOTE: Since this is a computed variable, it would be more efficient to
     store this into a variable for use in your program if you intend to use
     it more than once. (Getting the files and then getting the directories
     constitutes multiple accesses and therefore multiple computations)
     */
    public var matches: DirectoryChildren {
        // Create the children collection
        var children = DirectoryChildren()

        // Array of Strings of the matched paths. (char **)
        var item = _glob.pointee.gl_pathv

        // Skip the offset number of matches since those are reserved and will be nil
        for _ in 0..<offset {
            item = item?.successor()
        }

        // Make sure the item pointed to is not nil or else we've hit the
        // end. The glob(3) docs say the array is null-terminated.
        while let pointee = item?.pointee {
            // Cast the char * pointee to a swift String
            children.append(String(cString: pointee))

            // Advance to the next item in the array
            item = item?.successor()
        }

        return children
    }

    /** The number of reserved items at the beginning of the matches in the
     underlying glob_t struct. Reserved items are ignored/skipped in the
     matches variable of this Glob object.
     */
    public var offset: Int { return Int(_glob.pointee.gl_offs) }

    #if os(macOS)
    /** The limit on the number of matches to return. Intended to prevent DoS
     attacks. Only honored if the .limit GlobFlag is included.
     */
    public var limit: Int {
        get { return Int(_glob.pointee.gl_matchc) }
        set { _glob.pointee.gl_matchc = OptionInt(newValue) }
    }
    #endif

    /// The C function to use to close directories (default is closedir(2))
    public var closedir: @convention(c) (UnsafeMutableRawPointer?) -> Void {
        get { return _glob.pointee.gl_closedir }
        set {
            _glob.pointee.gl_closedir = newValue
        }
    }

    #if os(Linux)
    public typealias GlobReadDirectoryReturnType = UnsafeMutableRawPointer
    #else
    public typealias GlobReadDirectoryReturnType = UnsafeMutablePointer<dirent>
    #endif
    /// The C function used to read directories (default is readdir(2))
    public var readdir: @convention(c) (UnsafeMutableRawPointer?) -> GlobReadDirectoryReturnType? {
        get { return _glob.pointee.gl_readdir }
        set {
            _glob.pointee.gl_readdir = newValue
        }
    }

    /// The C function used to open directories (default is opendir(2))
    public var opendir: @convention(c) (UnsafePointer<CChar>?) -> UnsafeMutableRawPointer? {
        get { return _glob.pointee.gl_opendir }
        set {
            _glob.pointee.gl_opendir = newValue
        }
    }

    #if os(Linux)
    public typealias GlobStatType = UnsafeMutableRawPointer
    #else
    public typealias GlobStatType = UnsafeMutablePointer<stat>
    #endif
    /// The C function used to lstat directories (default is lstat(2))
    public var lstat: @convention(c) (UnsafePointer<CChar>?, GlobStatType?) -> FileDescriptor {
        get { return _glob.pointee.gl_lstat }
        set {
            _glob.pointee.gl_lstat = newValue
        }
    }

    /// The C function used to stat directories (default is stat(2))
    public var stat: @convention(c) (UnsafePointer<CChar>?, GlobStatType?) -> FileDescriptor {
        get { return _glob.pointee.gl_stat }
        set {
            _glob.pointee.gl_stat = newValue
        }
    }

    /// The flags used by the glob (if it was previously used before)
    public var flags: GlobFlags { return GlobFlags(rawValue: _glob.pointee.gl_flags) }

    /// Initializes with an empty glob_t struct
    public init() {
        _glob = UnsafeMutablePointer.allocate(capacity: 1)
        _glob.initialize(to: glob_t())

        // Since we have sole control over the pointer, it's safe to deallocate
        // it at deinitialization
        owned = true
    }

    /** Initializes with a pointer to the specified glob_t struct. Only use
     this initializer if you have a specific reason to own the management of
     your glob_t struct. You MUST call globfree(3) (or cGlobFree) with your
     glob_t struct when you are done or it will be a memory leak
     */
    public init(glob: UnsafeMutablePointer<glob_t>) {
        _glob = glob

        // If the user is passing their own glob_t pointer, then we do not own
        // the management of it and they will have to free it themselves
        owned = false
    }

    // When this object is deconstructed, be sure to free the glob pointer (or
    // you'll have a memory leak)
    deinit {
        if owned {
            cGlobFree(_glob)
        }
    }
}