From 5fc7ef02bfe0305e14c658d6c884d1fdcf676a34 Mon Sep 17 00:00:00 2001 From: Pavlos Touboulidis Date: Wed, 7 May 2014 20:32:54 +0300 Subject: [PATCH] Fixes #4615: OpenRA.Utility should expand wildcards in paths The path to be expanded. It can be a relative or an absolute path. Wildcards can appear as part of the path and as part of the file name/extension. Example: Expand("./mods/*/mod.?aml"); --- OpenRA.Utility/Command.cs | 12 ++- OpenRA.Utility/Glob.cs | 129 +++++++++++++++++++++++++++ OpenRA.Utility/OpenRA.Utility.csproj | 1 + 3 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 OpenRA.Utility/Glob.cs diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index e3d0aa52c1..e8c05c9362 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -31,6 +31,13 @@ namespace OpenRA.Utility { public static class Command { + static IEnumerable GlobArgs(string[] args, int startIndex = 1) + { + for (var i = startIndex; i < args.Length; i++) + foreach (var path in Glob.Expand(args[i])) + yield return path; + } + [Desc("KEY", "Get value of KEY from settings.yaml")] public static void Settings(string[] args) { @@ -50,8 +57,9 @@ namespace OpenRA.Utility [Desc("PNGFILE [PNGFILE ...]", "Combine a list of PNG images into a SHP")] public static void ConvertPngToShp(string[] args) { - var dest = args[1].Split('-').First() + ".shp"; - var frames = args.Skip(1).Select(a => PngLoader.Load(a)); + var inputFiles = GlobArgs(args).OrderBy(a => a).ToList(); + var dest = inputFiles[0].Split('-').First() + ".shp"; + var frames = inputFiles.Select(a => PngLoader.Load(a)); var size = frames.First().Size; if (frames.Any(f => f.Size != size)) diff --git a/OpenRA.Utility/Glob.cs b/OpenRA.Utility/Glob.cs new file mode 100644 index 0000000000..9fd112d269 --- /dev/null +++ b/OpenRA.Utility/Glob.cs @@ -0,0 +1,129 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace OpenRA.Utility +{ + public static class Glob + { + public static bool Enabled = true; + + static readonly char[] GlobChars = new char[] { '*', '?' }; + static readonly char[] DirectorySeparators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + static bool NeedsExpansion(string filePath) + { + if (!Enabled) + return false; + + return filePath.IndexOfAny(GlobChars) >= 0; + } + + public static IEnumerable Expand(string filePath) + { + if (!NeedsExpansion(filePath)) + { + yield return filePath; + yield break; + } + + // + // Split using DirectorySeparators but keep the separators + // + var parts = new List(); + + for (var startIndex = 0; startIndex < filePath.Length;) + { + var index = filePath.IndexOfAny(DirectorySeparators, startIndex); + if (index == -1) + { + parts.Add(filePath.Substring(startIndex)); + break; + } + + parts.Add(filePath.Substring(startIndex, index - startIndex + 1)); + startIndex = index + 1; + } + + if (parts.Count > 0 && (parts[0] == "." || parts[0] == "..")) + parts[0] += Path.DirectorySeparatorChar; + + // If it's empty + // or if + // it's not rooted + // and it doesn't start with "./" or "../" + // prepend "./" + if (parts.Count == 0 + || (parts[0][0] != Path.DirectorySeparatorChar + && parts[0][0] != Path.AltDirectorySeparatorChar + && parts[0].Contains(':') == false + && parts[0] != "." + Path.DirectorySeparatorChar + && parts[0] != "." + Path.AltDirectorySeparatorChar + && parts[0] != ".." + Path.DirectorySeparatorChar + && parts[0] != ".." + Path.AltDirectorySeparatorChar)) + parts.Insert(0, "." + Path.DirectorySeparatorChar); + + // If the last entry ends with a directory separator, append a '*' + if (parts[parts.Count - 1][parts[parts.Count - 1].Length - 1] == Path.DirectorySeparatorChar + || parts[parts.Count - 1][parts[parts.Count - 1].Length - 1] == Path.AltDirectorySeparatorChar) + parts.Add("*"); + + var root = parts[0]; + var dirs = parts.Skip(1).Take(parts.Count - 2).ToList(); + var file = parts[parts.Count - 1]; + + foreach (var path in Expand(root, dirs, 0, file)) + yield return path; + } + + static IEnumerable Expand(string basePath, IList dirs, int dirIndex, string file) + { + if (dirIndex < dirs.Count) + { + var dir = dirs[dirIndex]; + + if (!NeedsExpansion(dir)) + { + var path = Path.Combine(basePath, dir); + + if (!Directory.Exists(path)) + yield break; + else + { + foreach (var s in Expand(path, dirs, dirIndex + 1, file)) + yield return s; + } + } + else + { + if (dir[dir.Length - 1] == Path.DirectorySeparatorChar || dir[dir.Length - 1] == Path.AltDirectorySeparatorChar) + dir = dir.Substring(0, dir.Length - 1); + foreach (var subDir in Directory.EnumerateDirectories(basePath, dir, SearchOption.TopDirectoryOnly)) + foreach (var s in Expand(subDir, dirs, dirIndex + 1, file)) + yield return s; + } + } + else + { + if (!NeedsExpansion(file)) + yield return Path.Combine(basePath, file); + else + { + foreach (var s in Directory.GetFiles(basePath, file, SearchOption.TopDirectoryOnly)) + yield return s; + } + } + } + } +} diff --git a/OpenRA.Utility/OpenRA.Utility.csproj b/OpenRA.Utility/OpenRA.Utility.csproj index c61fdd7e08..c06279d850 100644 --- a/OpenRA.Utility/OpenRA.Utility.csproj +++ b/OpenRA.Utility/OpenRA.Utility.csproj @@ -79,6 +79,7 @@ +