This commit is contained in:
= 2022-12-29 15:39:48 -07:00
parent f822a23daa
commit e12f475850
2 changed files with 23 additions and 22 deletions

View File

@ -62,16 +62,6 @@ namespace FileManager
private static string replaceFileName(string filename, Dictionary<string,string> paramReplacements, int maxFilenameLength) private static string replaceFileName(string filename, Dictionary<string,string> paramReplacements, int maxFilenameLength)
{ {
//Filename limits on NTFS and FAT filesystems are based on characters,
//but on ext* filesystems they're based on bytes. The ext* filesystems
//don't care about encoding, so how unicode characters are encoded is
///a choice made by the linux kernel. As best as I can tell, pretty
//much everyone uses UTF-8.
int getFilesystemStringLength(StringBuilder str)
=> LongPath.IsWindows ?
str.Length
: Encoding.UTF8.GetByteCount(str.ToString());
List<StringBuilder> filenameParts = new(); List<StringBuilder> filenameParts = new();
//Build the filename in parts, replacing replacement parameters with //Build the filename in parts, replacing replacement parameters with
//their values, and storing the parts in a list. //their values, and storing the parts in a list.
@ -106,7 +96,7 @@ namespace FileManager
//Remove 1 character from the end of the longest filename part until //Remove 1 character from the end of the longest filename part until
//the total filename is less than max filename length //the total filename is less than max filename length
while (filenameParts.Sum(p => getFilesystemStringLength(p)) > maxFilenameLength) while (filenameParts.Sum(p => LongPath.GetFilesystemStringLength(p)) > maxFilenameLength)
{ {
int maxLength = filenameParts.Max(p => p.Length); int maxLength = filenameParts.Max(p => p.Length);
var maxEntry = filenameParts.First(p => p.Length == maxLength); var maxEntry = filenameParts.First(p => p.Length == maxLength);

View File

@ -13,6 +13,13 @@ namespace FileManager
public const int MaxFilenameLength = 255; public const int MaxFilenameLength = 255;
public static readonly int MaxDirectoryLength; public static readonly int MaxDirectoryLength;
public static readonly int MaxPathLength; public static readonly int MaxPathLength;
private const int WIN_MAX_PATH = 260;
private const string WIN_LONG_PATH_PREFIX = @"\\?\";
internal static readonly bool IsWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
internal static readonly bool IsLinux = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
internal static readonly bool IsOSX = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public string Path { get; }
static LongPath() static LongPath()
{ {
@ -33,24 +40,25 @@ namespace FileManager
} }
} }
private const int WIN_MAX_PATH = 260;
private const string WIN_LONG_PATH_PREFIX = @"\\?\";
private LongPath(string path) private LongPath(string path)
{ {
if (IsWindows && path.Length > MaxPathLength) if (IsWindows && path.Length > MaxPathLength)
throw new System.IO.PathTooLongException($"Path exceeds {MaxPathLength} character limit. ({path})"); throw new System.IO.PathTooLongException($"Path exceeds {MaxPathLength} character limit. ({path})");
if ((!IsWindows && Encoding.UTF8.GetByteCount(path) > MaxPathLength)) if (!IsWindows && Encoding.UTF8.GetByteCount(path) > MaxPathLength)
throw new System.IO.PathTooLongException($"Path exceeds {MaxPathLength} byte limit. ({path})"); throw new System.IO.PathTooLongException($"Path exceeds {MaxPathLength} byte limit. ({path})");
Path = path; Path = path;
} }
public string Path { get; }
public override string ToString() => Path;
internal static readonly bool IsWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows); //Filename limits on NTFS and FAT filesystems are based on characters,
internal static readonly bool IsLinux = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux); //but on ext* filesystems they're based on bytes. The ext* filesystems
internal static readonly bool IsOSX = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX); //don't care about encoding, so how unicode characters are encoded is
///a choice made by the linux kernel. As best as I can tell, pretty
//much everyone uses UTF-8.
public static int GetFilesystemStringLength(StringBuilder filename)
=> LongPath.IsWindows ?
filename.Length
: Encoding.UTF8.GetByteCount(filename.ToString());
public static implicit operator LongPath(string path) public static implicit operator LongPath(string path)
{ {
@ -153,6 +161,9 @@ namespace FileManager
} }
} }
public override string ToString() => Path;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern int GetShortPathName([MarshalAs(UnmanagedType.LPWStr)] string path, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder shortPath, int shortPathLength); private static extern int GetShortPathName([MarshalAs(UnmanagedType.LPWStr)] string path, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder shortPath, int shortPathLength);