Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
156 lines (132 sloc) 5.71 KB
/// This module contains a file pattern globbing implementation.
[<System.Obsolete "Please use Fake.IO.Globbing.Glob instead">]
module Fake.Globbing
#nowarn "44"
open System
open System.Collections.Generic
open System.IO
open System.Text.RegularExpressions
// Normalizes path for different OS
[<System.Obsolete "Please use Fake.IO.Globbing.Glob instead">]
let inline normalizePath (path : string) =
path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)
type private SearchOption =
| Directory of string
| Drive of string
| Recursive
| FilePattern of string
let private checkSubDirs absolute (dir : string) root =
if dir.Contains "*" then Directory.EnumerateDirectories(root, dir, SearchOption.TopDirectoryOnly) |> Seq.toList
else
let path = Path.Combine(root, dir)
let di =
if absolute then new DirectoryInfo(dir)
else new DirectoryInfo(path)
if di.Exists then [ di.FullName ]
else []
let rec private buildPaths acc (input : SearchOption list) =
match input with
| [] -> acc
| Directory(name) :: t ->
let subDirs =
acc
|> List.map (checkSubDirs false name)
|> List.concat
buildPaths subDirs t
| Drive(name) :: t ->
let subDirs =
acc
|> List.map (checkSubDirs true name)
|> List.concat
buildPaths subDirs t
| Recursive :: [] ->
let dirs =
Seq.collect (fun dir -> Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories)) acc
|> Seq.toList
buildPaths (acc @ dirs) []
| Recursive :: t ->
let dirs =
Seq.collect (fun dir -> Directory.EnumerateDirectories(dir, "*", SearchOption.AllDirectories)) acc
|> Seq.toList
buildPaths (acc @ dirs) t
| FilePattern(pattern) :: t ->
Seq.collect (fun dir ->
if Directory.Exists(Path.Combine(dir, pattern))
then seq { yield Path.Combine(dir, pattern) }
else
try
Directory.EnumerateFiles(dir, pattern)
with
| :? System.IO.PathTooLongException as ex ->
Array.toSeq [| |]
) acc |> Seq.toList
let private driveRegex = Regex(@"^[A-Za-z]:$", RegexOptions.Compiled)
let inline private normalizeOutputPath (p : string) =
p.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)
.TrimEnd(Path.DirectorySeparatorChar)
let internal getRoot (baseDirectory : string) (pattern : string) =
let baseDirectory = normalizePath baseDirectory
let normPattern = normalizePath pattern
let patternParts = normPattern.Split([| '/'; '\\' |], StringSplitOptions.RemoveEmptyEntries)
let patternPathParts =
patternParts
|> Seq.takeWhile(fun p -> not (p.Contains("*")))
|> Seq.toArray
let globRoot =
// If we did not find any "*", then drop the last bit (it is a file name, not a pattern)
( if patternPathParts.Length = patternParts.Length then
patternPathParts.[0 .. patternPathParts.Length-2]
else patternPathParts )
|> String.concat (Path.DirectorySeparatorChar.ToString())
let globRoot =
// If we dropped "/" from the beginning of the path in the 'Split' call, put it back!
if normPattern.StartsWith("/") then "/" + globRoot
else globRoot
if Path.IsPathRooted globRoot then globRoot
else Path.Combine(baseDirectory, globRoot)
let internal search (baseDir : string) (input : string) =
let baseDir = normalizePath baseDir
let input = normalizePath input
let input = input.Replace(baseDir, "")
let filePattern = Path.GetFileName(input)
input.Split([| '/'; '\\' |], StringSplitOptions.RemoveEmptyEntries)
|> Seq.map (function
| "**" -> Recursive
| a when a = filePattern -> FilePattern(a)
| a when driveRegex.IsMatch a -> Directory(a + "\\")
| a -> Directory(a))
|> Seq.toList
|> buildPaths [ baseDir ]
|> List.map normalizeOutputPath
let internal compileGlobToRegex pattern =
let pattern = normalizePath pattern
let escapedPattern = (Regex.Escape pattern)
let regexPattern =
let xTOy =
[
"dirwildcard", (@"\\\*\\\*(/|\\\\)", @"(.*(/|\\))?")
"stardotstar", (@"\\\*\\.\\\*", @"([^\\/]*)")
"wildcard", (@"\\\*", @"([^\\/]*)")
] |> List.map(fun (key, reg) ->
let pattern, replace = reg
let pattern = sprintf "(?<%s>%s)" key pattern
key, (pattern, replace)
)
let xTOyMap = xTOy |> Map.ofList
let replacePattern = xTOy |> List.map(fun x -> x |> snd |> fst) |> String.concat("|")
let replaced = Regex(replacePattern).Replace(escapedPattern, fun m ->
let matched = xTOy |> Seq.map(fst) |> Seq.find(fun n ->
m.Groups.Item(n).Success
)
(xTOyMap |> Map.tryFind matched).Value |> snd
)
"^" + replaced + "$"
Regex(regexPattern)
[<System.Obsolete "This is no longer supported, please open an issue if you had a need for it.">]
let globRegexCache = System.Collections.Concurrent.ConcurrentDictionary<string, Regex>()
[<System.Obsolete "Please use Fake.IO.Globbing.Glob instead">]
let isMatch pattern path : bool =
let path = normalizePath path
let regex =
globRegexCache.GetOrAdd(pattern, compileGlobToRegex)
regex.IsMatch(path)
You can’t perform that action at this time.