From 6901b8be3569f4cba890fcf2d42677cbb2a95d9b Mon Sep 17 00:00:00 2001 From: = Date: Thu, 29 Dec 2022 14:12:46 -0700 Subject: [PATCH] Fix file naming template on unix systems --- Source/FileManager/FileNamingTemplate.cs | 22 ++++++++++++++++++++-- Source/FileManager/LongPath.cs | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Source/FileManager/FileNamingTemplate.cs b/Source/FileManager/FileNamingTemplate.cs index 203b9acf..ee8446ed 100644 --- a/Source/FileManager/FileNamingTemplate.cs +++ b/Source/FileManager/FileNamingTemplate.cs @@ -44,16 +44,34 @@ namespace FileManager var fileNamePart = pathParts[^1]; pathParts.Remove(fileNamePart); + var fileExtension = Path.GetExtension(fileNamePart); + fileNamePart = fileNamePart[..^fileExtension.Length]; + LongPath directory = Path.Join(pathParts.Select(p => replaceFileName(p, paramReplacements, LongPath.MaxFilenameLength)).ToArray()); //If file already exists, GetValidFilename will append " (n)" to the filename. //This could cause the filename length to exceed MaxFilenameLength, so reduce //allowable filename length by 5 chars, allowing for up to 99 duplicates. - return FileUtility.GetValidFilename(Path.Join(directory, replaceFileName(fileNamePart, paramReplacements, LongPath.MaxFilenameLength - 5)), replacements, returnFirstExisting); + return FileUtility + .GetValidFilename( + Path.Join(directory, replaceFileName(fileNamePart, paramReplacements, LongPath.MaxFilenameLength - fileExtension.Length - 5)) + fileExtension, + replacements, + returnFirstExisting + ); } private static string replaceFileName(string filename, Dictionary 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.PlatformID is PlatformID.Win32NT ? + str.Length + : Encoding.UTF8.GetByteCount(str.ToString()); + List filenameParts = new(); //Build the filename in parts, replacing replacement parameters with //their values, and storing the parts in a list. @@ -88,7 +106,7 @@ namespace FileManager //Remove 1 character from the end of the longest filename part until //the total filename is less than max filename length - while (filenameParts.Sum(p => p.Length) > maxFilenameLength) + while (filenameParts.Sum(p => getFilesystemStringLength(p)) > maxFilenameLength) { int maxLength = filenameParts.Max(p => p.Length); var maxEntry = filenameParts.First(p => p.Length == maxLength); diff --git a/Source/FileManager/LongPath.cs b/Source/FileManager/LongPath.cs index a4a467d2..a5302fe0 100644 --- a/Source/FileManager/LongPath.cs +++ b/Source/FileManager/LongPath.cs @@ -20,7 +20,7 @@ namespace FileManager public string Path { get; init; } public override string ToString() => Path; - private static readonly PlatformID PlatformID = Environment.OSVersion.Platform; + internal static readonly PlatformID PlatformID = Environment.OSVersion.Platform; public static implicit operator LongPath(string path)