From 9b3553e43f2c9c47e8da9542bec53709af2d162f Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 8 Nov 2023 01:01:15 -0500 Subject: [PATCH] Support ancient .NET --- IOExtensions.cs | 22 ++- IniFile.cs | 15 +- NaturalSort/NaturalComparer.cs | 9 +- NaturalSort/NaturalReversedComparer.cs | 9 +- OldDotNet.cs | 194 +++++++++++++++++++++++++ ParentablePath.cs | 16 +- PathTool.cs | 28 ++-- Readers/ClrMameProReader.cs | 8 +- Readers/IniReader.cs | 6 +- Readers/SeparatedValueReader.cs | 10 +- SabreTools.IO.csproj | 2 +- StreamExtensions.cs | 2 + Writers/IniWriter.cs | 14 +- 13 files changed, 284 insertions(+), 51 deletions(-) create mode 100644 OldDotNet.cs diff --git a/IOExtensions.cs b/IOExtensions.cs index 0d8e6a3..b899d00 100644 --- a/IOExtensions.cs +++ b/IOExtensions.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using System.Text; namespace SabreTools.IO @@ -19,7 +21,7 @@ namespace SabreTools.IO public static string Ensure(this string dir, bool create = false) { // If the output directory is invalid - if (string.IsNullOrWhiteSpace(dir)) + if (string.IsNullOrEmpty(dir)) dir = PathTool.GetRuntimeDirectory(); // Get the full path for the output directory @@ -60,7 +62,7 @@ namespace SabreTools.IO file.Dispose(); // Disable warning about UTF7 usage - #pragma warning disable SYSLIB0001 +#pragma warning disable SYSLIB0001 // Analyze the BOM if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7; @@ -70,7 +72,7 @@ namespace SabreTools.IO if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32; return Encoding.Default; - #pragma warning restore SYSLIB0001 +#pragma warning restore SYSLIB0001 } catch { @@ -86,14 +88,14 @@ namespace SabreTools.IO public static string? GetNormalizedExtension(this string? path) { // Check null or empty first - if (string.IsNullOrWhiteSpace(path)) + if (string.IsNullOrEmpty(path)) return null; // Get the extension from the path, if possible string? ext = Path.GetExtension(path)?.ToLowerInvariant(); // Check if the extension is null or empty - if (string.IsNullOrWhiteSpace(ext)) + if (string.IsNullOrEmpty(ext)) return null; // Make sure that extensions are valid @@ -118,13 +120,23 @@ namespace SabreTools.IO return null; // If it does and it is empty, return a blank enumerable +#if NET20 || NET35 + if (Directory.GetFileSystemEntries(root, "*").Length == 0) +#else if (!Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Any()) +#endif return new List(); // Otherwise, get the complete list +#if NET20 || NET35 + return Directory.GetDirectories(root, "*") + .Where(dir => Directory.GetFileSystemEntries(dir, "*").Length != 0) + .ToList(); +#else return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories) .Where(dir => !Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Any()) .ToList(); +#endif } } } diff --git a/IniFile.cs b/IniFile.cs index 5983d97..1660a3b 100644 --- a/IniFile.cs +++ b/IniFile.cs @@ -2,7 +2,9 @@ using System.Collections; using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using System.Text; using SabreTools.IO.Readers; using SabreTools.IO.Writers; @@ -198,14 +200,14 @@ namespace SabreTools.IO string? value = keyValuePair.Value; // We assume '.' is a section name separator - if (key.Contains('.')) + if (key.Contains(".")) { // Split the key by '.' string[] data = keyValuePair.Key.Split('.'); // If the key contains an '.', we need to put them back in string newSection = data[0].Trim(); - key = string.Join(".", data.Skip(1)).Trim(); + key = string.Join(".", data.Skip(1).ToArray()).Trim(); // If we have a new section, write it out if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase)) @@ -231,9 +233,18 @@ namespace SabreTools.IO #region IDictionary Impelementations +#if NET20 || NET35 || NET40 || NET452 + public ICollection Keys => _keyValuePairs?.Keys?.ToArray() ?? new string[0]; +#else public ICollection Keys => _keyValuePairs?.Keys?.ToArray() ?? Array.Empty(); +#endif + +#if NET20 || NET35 || NET40 || NET452 + public ICollection Values => _keyValuePairs?.Values?.ToArray() ?? new string[0]; +#else public ICollection Values => _keyValuePairs?.Values?.ToArray() ?? Array.Empty(); +#endif public int Count => (_keyValuePairs as ICollection>)?.Count ?? 0; diff --git a/NaturalSort/NaturalComparer.cs b/NaturalSort/NaturalComparer.cs index 10c013e..a1bf03f 100644 --- a/NaturalSort/NaturalComparer.cs +++ b/NaturalSort/NaturalComparer.cs @@ -11,8 +11,13 @@ using System; using System.Collections.Generic; +#if !NET20 using System.Linq; +#endif using System.Text.RegularExpressions; +#if NET20 +using SabreTools.IO; +#endif /// TODO: Make this namespace a separate library namespace NaturalSort @@ -49,13 +54,13 @@ namespace NaturalSort if (!table.TryGetValue(x, out string[]? x1)) { //x1 = Regex.Split(x.Replace(" ", string.Empty), "([0-9]+)"); - x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); + x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrEmpty(s)).ToArray(); table.Add(x, x1); } if (!table.TryGetValue(y, out string[]? y1)) { //y1 = Regex.Split(y.Replace(" ", string.Empty), "([0-9]+)"); - y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); + y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrEmpty(s)).ToArray(); table.Add(y, y1); } diff --git a/NaturalSort/NaturalReversedComparer.cs b/NaturalSort/NaturalReversedComparer.cs index d689c88..f1bc1fc 100644 --- a/NaturalSort/NaturalReversedComparer.cs +++ b/NaturalSort/NaturalReversedComparer.cs @@ -11,8 +11,13 @@ using System; using System.Collections.Generic; +#if !NET20 using System.Linq; +#endif using System.Text.RegularExpressions; +#if NET20 +using SabreTools.IO; +#endif /// TODO: Make this namespace a separate library namespace NaturalSort @@ -49,13 +54,13 @@ namespace NaturalSort if (!table.TryGetValue(x, out string[]? x1)) { //x1 = Regex.Split(x.Replace(" ", string.Empty), "([0-9]+)"); - x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); + x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrEmpty(s)).ToArray(); table.Add(x, x1); } if (!table.TryGetValue(y, out string[]? y1)) { //y1 = Regex.Split(y.Replace(" ", string.Empty), "([0-9]+)"); - y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); + y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrEmpty(s)).ToArray(); table.Add(y, y1); } diff --git a/OldDotNet.cs b/OldDotNet.cs new file mode 100644 index 0000000..c5dcdff --- /dev/null +++ b/OldDotNet.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +#if NET20 + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] + internal sealed class ExtensionAttribute : Attribute {} +} + +namespace SabreTools.IO +{ + internal delegate U LinqSelectDelegate(T str); + + internal delegate bool LinqWhereDelegate(T str); + + internal static partial class EnumerationExtensions + { + public static IEnumerable Cast(this MatchCollection arr) + { + var output = new List(); + foreach (var val in arr) + { + output.Add((T)val); + } + return output; + } + + public static IEnumerable Select(this T[] arr, LinqSelectDelegate func) + { + var output = new List(); + for (int i = 0; i < arr.Length; i++) + { + output.Add(func(arr[i])); + } + return output; + } + + public static IEnumerable Select(this IEnumerable arr, LinqSelectDelegate func) + { + var output = new List(); + foreach (var s in arr) + { + output.Add(func(s)); + } + return output; + } + + public static IEnumerable Skip(this T[] arr, int skip) + { + var output = new List(); + for (int i = 0; i < arr.Length; i++) + { + if (i < skip) + continue; + output.Add(arr[i]); + } + return output; + } + + public static IEnumerable Skip(this IEnumerable arr, int skip) + { + var output = new List(); + int index = 0; + foreach (var s in arr) + { + if (index++ < skip) + continue; + output.Add(s); + } + return output; + } + + public static T[] ToArray(this IEnumerable arr) + { + var list = arr.ToList(); + var output = new T[list.Count]; + int index = 0; + foreach (var s in list) + { + output[index++] = s; + } + return output; + } + + public static U[] ToArray(this Dictionary.ValueCollection arr) + { + var list = arr.ToList(); + var output = new U[list.Count]; + int index = 0; + foreach (var s in list) + { + output[index++] = s; + } + return output; + } + + public static List ToList(this IEnumerable arr) + { + var output = new List(); + foreach (var s in arr) + { + output.Add(s); + } + return output; + } + + public static List ToList(this Dictionary.ValueCollection arr) + { + var output = new List(); + foreach (var s in arr) + { + output.Add(s); + } + return output; + } + + public static IEnumerable Where(this T[] arr, LinqWhereDelegate func) + { + var output = new List(); + for (int i = 0; i < arr.Length; i++) + { + if (func(arr[i])) + output.Add(arr[i]); + } + return output; + } + + public static IEnumerable Where(this IEnumerable arr, LinqWhereDelegate func) + { + var output = new List(); + foreach (var s in arr) + { + if (func(s)) + output.Add(s); + } + return output; + } + } +} + +#endif + +#if NET20 || NET35 + +namespace SabreTools.IO +{ + internal static partial class EnumerationExtensions + { + public static bool SequenceEqual(this T[] arr1, T[] arr2) + { + if (arr1 == null || arr2 == null) + return false; + + if (arr1.Length != arr2.Length) + return false; + + for (int i = 0; i < arr1.Length; i++) + { + if (arr1[i] == null || !arr1![i]!.Equals(arr2[i])) + return false; + } + + return true; + } + } +} + +#endif + +#if NET20 || NET35 || NET40 + +namespace SabreTools.IO +{ + internal delegate U LinqOrderByDelegate(T str); + + internal static partial class EnumerationExtensions + { + public static IEnumerable OrderBy(this IEnumerable arr, LinqOrderByDelegate func) + { + // TODO: Implement ordering + return arr; + } + } + + internal static partial class StringExtensions + { + + } +} + +#endif \ No newline at end of file diff --git a/ParentablePath.cs b/ParentablePath.cs index 972d1f3..7fc6c9b 100644 --- a/ParentablePath.cs +++ b/ParentablePath.cs @@ -32,14 +32,14 @@ namespace SabreTools.IO public string? GetNormalizedFileName(bool sanitize) { // If the current path is empty, we can't do anything - if (string.IsNullOrWhiteSpace(CurrentPath)) + if (string.IsNullOrEmpty(CurrentPath)) return null; // Assume the current path is the filename string filename = Path.GetFileName(CurrentPath); // If we have a true ParentPath, remove it from CurrentPath and return the remainder - if (!string.IsNullOrWhiteSpace(ParentPath) && !string.Equals(CurrentPath, ParentPath, StringComparison.Ordinal)) + if (string.IsNullOrEmpty(ParentPath) && !string.Equals(CurrentPath, ParentPath, StringComparison.Ordinal)) filename = CurrentPath.Remove(0, ParentPath!.Length + 1); // If we're sanitizing the path after, do so @@ -58,15 +58,15 @@ namespace SabreTools.IO public string? GetOutputPath(string outDir, bool inplace) { // If the current path is empty, we can't do anything - if (string.IsNullOrWhiteSpace(CurrentPath)) + if (string.IsNullOrEmpty(CurrentPath)) return null; // If the output dir is empty (and we're not inplace), we can't do anything - if (string.IsNullOrWhiteSpace(outDir) && !inplace) + if (string.IsNullOrEmpty(outDir) && !inplace) return null; // Check if we have a split path or not - bool splitpath = !string.IsNullOrWhiteSpace(ParentPath); + bool splitpath = !string.IsNullOrEmpty(ParentPath); // If we have an inplace output, use the directory name from the input path if (inplace) @@ -85,15 +85,9 @@ namespace SabreTools.IO workingParent = Path.GetDirectoryName(ParentPath ?? string.Empty) ?? string.Empty; // Determine the correct subfolder based on the working parent directory -#if NETFRAMEWORK int extraLength = workingParent.EndsWith(":") || workingParent.EndsWith(Path.DirectorySeparatorChar.ToString()) || workingParent.EndsWith(Path.AltDirectorySeparatorChar.ToString()) ? 0 : 1; -#else - int extraLength = workingParent.EndsWith(':') - || workingParent.EndsWith(Path.DirectorySeparatorChar) - || workingParent.EndsWith(Path.AltDirectorySeparatorChar) ? 0 : 1; -#endif return Path.GetDirectoryName(Path.Combine(outDir, CurrentPath.Remove(0, workingParent.Length + extraLength))); } diff --git a/PathTool.cs b/PathTool.cs index 097dd88..792e7c6 100644 --- a/PathTool.cs +++ b/PathTool.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using NaturalSort; namespace SabreTools.IO @@ -29,14 +31,10 @@ namespace SabreTools.IO // If we have a wildcard string pattern = "*"; - if (input.Contains('*') || input.Contains('?')) + if (input.Contains("*") || input.Contains("?")) { pattern = Path.GetFileName(input); -#if NETFRAMEWORK input = input.Substring(0, input.Length - pattern.Length); -#else - input = input[..^pattern.Length]; -#endif } // Get the parent path in case of appending @@ -75,7 +73,11 @@ namespace SabreTools.IO private static List GetDirectoriesOrderedHelper(string dir, List infiles, string pattern) { // Take care of the files in the top directory +#if NET20 || NET35 + List toadd = Directory.GetDirectories(dir, pattern).ToList(); +#else List toadd = Directory.EnumerateDirectories(dir, pattern, SearchOption.TopDirectoryOnly).ToList(); +#endif toadd.Sort(new NaturalComparer()); infiles.AddRange(toadd); @@ -108,14 +110,10 @@ namespace SabreTools.IO // If we have a wildcard string pattern = "*"; - if (input.Contains('*') || input.Contains('?')) + if (input.Contains("*") || input.Contains("?")) { pattern = Path.GetFileName(input); -#if NETFRAMEWORK input = input.Substring(0, input.Length - pattern.Length); -#else - input = input[..^pattern.Length]; -#endif } // Get the parent path in case of appending @@ -158,12 +156,20 @@ namespace SabreTools.IO private static List GetFilesOrderedHelper(string dir, List infiles, string pattern) { // Take care of the files in the top directory +#if NET20 || NET35 + List toadd = Directory.GetFiles(dir, pattern).ToList(); +#else List toadd = Directory.EnumerateFiles(dir, pattern, SearchOption.TopDirectoryOnly).ToList(); +#endif toadd.Sort(new NaturalComparer()); infiles.AddRange(toadd); // Then recurse through and add from the directories +#if NET20 || NET35 + List subDirs = Directory.GetDirectories(dir, pattern).ToList(); +#else List subDirs = Directory.EnumerateDirectories(dir, pattern, SearchOption.TopDirectoryOnly).ToList(); +#endif subDirs.Sort(new NaturalComparer()); foreach (string subdir in subDirs) { @@ -173,7 +179,7 @@ namespace SabreTools.IO // Return the new list return infiles; } - + /// /// Get the current runtime directory /// diff --git a/Readers/ClrMameProReader.cs b/Readers/ClrMameProReader.cs index 0f67e7c..83501ea 100644 --- a/Readers/ClrMameProReader.cs +++ b/Readers/ClrMameProReader.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using System.Text; using System.Text.RegularExpressions; @@ -131,11 +133,7 @@ namespace SabreTools.IO.Readers // Standalone (special case for DC dats) if (CurrentLine.StartsWith("Name:")) { -#if NETFRAMEWORK string temp = CurrentLine.Substring("Name:".Length).Trim(); -#else - string temp = CurrentLine["Name:".Length..].Trim(); -#endif CurrentLine = $"Name: {temp}"; } @@ -172,7 +170,7 @@ namespace SabreTools.IO.Readers for (int i = 0; i < linegc.Length; i++) { string key = linegc[i].Replace("\"", string.Empty); - if (string.IsNullOrWhiteSpace(key)) + if (string.IsNullOrEmpty(key)) continue; string value = string.Empty; diff --git a/Readers/IniReader.cs b/Readers/IniReader.cs index 0d1e84a..1658898 100644 --- a/Readers/IniReader.cs +++ b/Readers/IniReader.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using System.Text; namespace SabreTools.IO.Readers @@ -111,14 +113,14 @@ namespace SabreTools.IO.Readers } // KeyValuePair - else if (CurrentLine.Contains('=')) + else if (CurrentLine.Contains("=")) { // Split the line by '=' for key-value pairs string[] data = CurrentLine.Split('='); // If the value field contains an '=', we need to put them back in string key = data[0].Trim(); - string value = string.Join("=", data.Skip(1)).Trim(); + string value = string.Join("=", data.Skip(1).ToArray()).Trim(); KeyValuePair = new KeyValuePair(key, value); RowType = IniRowType.KeyValue; diff --git a/Readers/SeparatedValueReader.cs b/Readers/SeparatedValueReader.cs index 86a2d0f..0cce17a 100644 --- a/Readers/SeparatedValueReader.cs +++ b/Readers/SeparatedValueReader.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using System.Text; using System.Text.RegularExpressions; @@ -124,9 +126,11 @@ namespace SabreTools.IO.Readers // https://stackoverflow.com/questions/3776458/split-a-comma-separated-string-with-both-quoted-and-unquoted-strings var lineSplitRegex = new Regex($"(?:^|{Separator})(\"(?:[^\"]+|\"\")*\"|[^{Separator}]*)"); var temp = new List(); - foreach (Match match in lineSplitRegex.Matches(fullLine)) + foreach (Match? match in lineSplitRegex.Matches(fullLine)) { - string curr = match.Value; + string? curr = match?.Value; + if (curr == null) + continue; if (curr.Length == 0) temp.Add(""); @@ -137,7 +141,7 @@ namespace SabreTools.IO.Readers Line = temp; } - + // Otherwise, just split on the delimiter else { diff --git a/SabreTools.IO.csproj b/SabreTools.IO.csproj index 4a7aed9..02f3d48 100644 --- a/SabreTools.IO.csproj +++ b/SabreTools.IO.csproj @@ -2,7 +2,7 @@ - net48;net6.0;net7.0;net8.0 + net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64 latest enable diff --git a/StreamExtensions.cs b/StreamExtensions.cs index 9bc2b2f..c25c372 100644 --- a/StreamExtensions.cs +++ b/StreamExtensions.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +#if !NET20 using System.Linq; +#endif using System.Text; namespace SabreTools.IO diff --git a/Writers/IniWriter.cs b/Writers/IniWriter.cs index 01e230b..2ce151f 100644 --- a/Writers/IniWriter.cs +++ b/Writers/IniWriter.cs @@ -34,8 +34,8 @@ namespace SabreTools.IO.Writers { if (sw?.BaseStream == null) return; - - if (string.IsNullOrWhiteSpace(value)) + + if (string.IsNullOrEmpty(value)) throw new ArgumentException("Section tag cannot be null or empty", nameof(value)); sw.WriteLine($"[{value!.TrimStart('[').TrimEnd(']')}]"); @@ -48,8 +48,8 @@ namespace SabreTools.IO.Writers { if (sw?.BaseStream == null) return; - - if (string.IsNullOrWhiteSpace(key)) + + if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key cannot be null or empty", nameof(key)); value ??= string.Empty; @@ -63,7 +63,7 @@ namespace SabreTools.IO.Writers { if (sw?.BaseStream == null) return; - + value ??= string.Empty; sw.WriteLine($";{value}"); } @@ -75,7 +75,7 @@ namespace SabreTools.IO.Writers { if (sw?.BaseStream == null) return; - + value ??= string.Empty; sw.Write(value); } @@ -87,7 +87,7 @@ namespace SabreTools.IO.Writers { if (sw?.BaseStream == null) return; - + sw.WriteLine(); }