diff --git a/CHANGELOG.md b/CHANGELOG.md index 07b28ba..4e79419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -439,11 +439,16 @@ The format is based on - Fix [Get-ChildItem2](Docs/Cmdlets/Get-ChildItem2.md) with `-Hidden`, which omitted the first hidden item unless `-Force` was also supplied -- Fix the comparison of an identity with a name, such as - `$entry.Account -eq 'nt authority\system'`, in the Turkish culture +- Fix the comparison of an identity with a name by its `Equals` method, such + as `$entry.Account.Equals('nt authority\system')`, in the Turkish culture (`tr-TR`): it changed the case of both names with the culture of the thread, which turns `I` into a dotless `ı`, so an account whose name holds an uppercase `I`, such as `NT AUTHORITY\SYSTEM` or `BUILTIN\Administrators`, - did not match the same name in another case + did not match the same name in another case. The operators `-eq`, + `-contains`, and `-in` were not affected +- Fix [Get-ChildItem2](Docs/Cmdlets/Get-ChildItem2.md) with `-Filter` in the + Turkish culture (`tr-TR`), which dropped the items whose names differ from + the filter in the case of an `I`, such as `Index.txt` for `-Filter 'index*'` + or `SETTINGS.INI` for `-Filter '*.ini'` [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs index 7e8b05c..8a31073 100644 --- a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs +++ b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs @@ -150,8 +150,10 @@ namespace NTFSSecurity // for itself. Before 5.0.0, [1] was read as a character class, so a file with brackets in its name was not // returned for its name. The enumeration returns every item for *.* as Windows does, so the comparison does // too; with the dot as an ordinary character, it would drop the items without a dot, most folders. + // CultureInvariant: the enumeration ignores case without the culture of the thread, and with tr-TR the + // comparison would otherwise treat I and i as different letters and drop items that the enumeration matched. var pattern = filter == "*.*" ? "*" : filter; - wildcard = new WildcardPattern(pattern.Replace("`", "``").Replace("[", "`[").Replace("]", "`]"), WildcardOptions.Compiled | WildcardOptions.IgnoreCase); + wildcard = new WildcardPattern(pattern.Replace("`", "``").Replace("[", "`[").Replace("]", "`]"), WildcardOptions.Compiled | WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); modeMethodInfo = typeof(FileSystemCodeMembers).GetMethod("Mode"); diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index ea1c99f..0d907db 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -159,6 +159,36 @@ Describe 'Get-ChildItem2' { ($result.Name | Sort-Object) -join ',' | Should -Be (($Expected | Sort-Object) -join ',') } + # With the culture of the thread tr-TR, a case-insensitive comparison that is not culture invariant treats I and i as + # different letters: the cmdlet dropped the items whose names differ in the case of an I from the pattern, which + # the enumeration had matched without regard to the culture. INDEX* is the control: both sides turn I into the + # dotless letter. + It 'Should match -Filter ignoring case in Turkish, where I and i are different letters' -ForEach @( + @{ Filter = 'index*'; Expected = 'Index.txt' } + @{ Filter = 'INDEX*'; Expected = 'Index.txt' } + @{ Filter = '*.ini'; Expected = 'desktop.ini,SETTINGS.INI' } + @{ Filter = '*.INI'; Expected = 'desktop.ini,SETTINGS.INI' } + ) { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'FilterTurkish' -Directory + foreach ($name in 'Index.txt', 'desktop.ini', 'SETTINGS.INI', 'Other.txt') { + $file = Join-Path -Path $folder -ChildPath $name + Assert-TestSandboxPath -Sandbox $sandbox -Path $file + Set-Content -LiteralPath $file -Value $name + } + $original = [System.Threading.Thread]::CurrentThread.CurrentCulture + try { + [System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo]::GetCultureInfo('tr-TR') + 'I'.ToLower() | Should -Be ([string][char]0x131) -Because 'the culture of the thread has to be Turkish for the test to mean anything' + + $result = @(Get-ChildItem2 -Path $folder -Filter $Filter -ErrorAction Stop) + } + finally { + [System.Threading.Thread]::CurrentThread.CurrentCulture = $original + } + + ($result.Name | Sort-Object) -join ',' | Should -BeExactly $Expected + } + # Only * and ? are wildcards in -Filter. A bracket stands for itself, so a file with brackets in its name is found # by its name, as Get-ChildItem finds it, and the file that the brackets would select as a character class is not. # Before 5.0.0, the cmdlet read [1] as a character class and returned nothing.