Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Added Get-FileEncoding cmdlet #42

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ project.lock.json
Microsoft.PowerShell.TextUtility.xml
# VSCode directories that are not at the repository root
/**/.vscode/
# Visual Studio IDE directory
.vs/
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Return a base64 encoded representation of a string.

This will convert tabular data and convert it to an object.

## Get-FileEncoding

This cmdlet returns encoding for a file.

## Code of Conduct

Please see our [Code of Conduct](.github/CODE_OF_CONDUCT.md) before participating in this project.
Expand Down
30 changes: 30 additions & 0 deletions TextUtility.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CBF81F7C-6E0A-4695-AA0F-35C61676B7EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.TextUtility", "src\code\Microsoft.PowerShell.TextUtility.csproj", "{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F} = {CBF81F7C-6E0A-4695-AA0F-35C61676B7EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EAC68D5E-4DA0-4F37-88B6-CAF32652C01F}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion src/Microsoft.PowerShell.TextUtility.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
PowerShellVersion = '5.1'
FormatsToProcess = @('Microsoft.PowerShell.TextUtility.format.ps1xml')
CmdletsToExport = @(
'Compare-Text','ConvertFrom-Base64','ConvertTo-Base64',"ConvertFrom-TextTable"
'Compare-Text','ConvertFrom-Base64','ConvertTo-Base64',"ConvertFrom-TextTable","Get-FileEncoding"
)
PrivateData = @{
PSData = @{
Expand Down
70 changes: 70 additions & 0 deletions src/code/GetFileEncodingCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.IO;
using System.Management.Automation;
using System.Text;

namespace Microsoft.PowerShell.TextUtility
{
/// <summary>
/// This class implements the Get-FileEncoding command.
/// </summary>
[Cmdlet(VerbsCommon.Get, "FileEncoding", DefaultParameterSetName = PathParameterSet)]
[OutputType(typeof(Encoding))]
public sealed class GetFileEncodingCommand : PSCmdlet
{
#region Parameter Sets

private const string PathParameterSet = "ByPath";
private const string LiteralPathParameterSet = "ByLiteralPath";

#endregion

#region Parameters

/// <summary>
/// Gets or sets path from from which to get encoding.
/// </summary>
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = PathParameterSet)]
public string Path { get; set; }

/// <summary>
/// Gets or sets literal path from which to get encoding.
/// </summary>
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = LiteralPathParameterSet)]
[Alias("PSPath", "LP")]
public string LiteralPath
{
get
{
return _isLiteralPath ? Path : null;
}

set
{
Path = value;
_isLiteralPath = true;
}
}

private bool _isLiteralPath;

#endregion

/// <summary>
/// Process paths to get file encoding.
/// </summary>
protected override void ProcessRecord()
{
string resolvedPath = PathUtils.ResolveFilePath(Path, this, _isLiteralPath);

if (!File.Exists(resolvedPath))
{
PathUtils.ReportPathNotFound(Path, this);
}

WriteObject(PathUtils.GetPathEncoding(resolvedPath));
}
}
}
17 changes: 16 additions & 1 deletion src/code/Microsoft.PowerShell.TextUtility.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0-*">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="6.0.6" >
<PackageReference Include="System.Text.Json" Version="6.0.6">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Expand All @@ -28,4 +28,19 @@
</Content>
</ItemGroup>

<ItemGroup>
<Compile Update="Resources\PathUtilityStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PathUtilityStrings.resx</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Resources\PathUtilityStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>PathUtilityStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
115 changes: 115 additions & 0 deletions src/code/PathUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Management.Automation;
using System.Text;
using Microsoft.PowerShell.TextUtility.Resources;

namespace Microsoft.PowerShell.TextUtility
{
/// <summary>
/// Defines generic path utilities and helper methods for TextUtility.
/// </summary>
internal static class PathUtils
{
/// <summary>
/// Resolves user provided path using file system provider.
/// </summary>
/// <param name="path">The path to resolve.</param>
/// <param name="command">The command.</param>
/// <param name="isLiteralPath">True if the wildcard resolution should not be attempted.</param>
/// <returns>The resolved (absolute) path.</returns>
internal static string ResolveFilePath(string path, PSCmdlet command, bool isLiteralPath)
{
string resolvedPath;

try
{
ProviderInfo provider = null;
PSDriveInfo drive = null;

PathIntrinsics sessionStatePath = command.SessionState.Path;

if (isLiteralPath)
{
resolvedPath = sessionStatePath.GetUnresolvedProviderPathFromPSPath(path, out provider, out drive);
}
else
{
Collection<string> filePaths = sessionStatePath.GetResolvedProviderPathFromPSPath(path, out provider);

if (!provider.Name.Equals("FileSystem", StringComparison.OrdinalIgnoreCase))
{
ReportOnlySupportsFileSystemPaths(path, command);
}

if (filePaths.Count > 1)
{
ReportMultipleFilesNotSupported(command);
}

resolvedPath = filePaths[0];
}
}
catch (ItemNotFoundException)
{
resolvedPath = null;
}

return resolvedPath;
}

/// <summary>
/// Throws terminating error for not using file system provider.
/// </summary>
/// <param name="path">The path to report.</param>
/// <param name="command">The command.</param>
internal static void ReportOnlySupportsFileSystemPaths(string path, PSCmdlet command)
{
var errorMessage = string.Format(CultureInfo.CurrentCulture, PathUtilityStrings.OnlySupportsFileSystemPaths, path);
var exception = new ArgumentException(errorMessage);
var errorRecord = new ErrorRecord(exception, "OnlySupportsFileSystemPaths", ErrorCategory.InvalidArgument, path);
command.ThrowTerminatingError(errorRecord);
}

/// <summary>
/// Throws terminating error for path not found.
/// </summary>
/// <param name="path">The path to report.</param>
/// <param name="command">The command.</param>
internal static void ReportPathNotFound(string path, PSCmdlet command)
{
var errorMessage = string.Format(CultureInfo.CurrentCulture, PathUtilityStrings.PathNotFound, path);
var exception = new ArgumentException(errorMessage);
var errorRecord = new ErrorRecord(exception, "PathNotFound", ErrorCategory.ObjectNotFound, path);
command.ThrowTerminatingError(errorRecord);
}

/// <summary>
/// Throws terminating error for multiple files being used.
/// </summary>
/// <param name="command">The command.</param>
internal static void ReportMultipleFilesNotSupported(PSCmdlet command)
{
var errorMessage = string.Format(CultureInfo.CurrentCulture, PathUtilityStrings.MultipleFilesNotSupported);
var exception = new ArgumentException(errorMessage);
var errorRecord = new ErrorRecord(exception, "MultipleFilesNotSupported", ErrorCategory.InvalidArgument, null);
command.ThrowTerminatingError(errorRecord);
}

/// <summary>
/// Gets encoding for path.
/// </summary>
/// <param name="path">The path to get file encoding.</param>
/// <returns>The encoding of file.</returns>
internal static Encoding GetPathEncoding(string path)
{
using (var reader = new StreamReader(path, Encoding.Default, detectEncodingFromByteOrderMarks: true))
{
_ = reader.Read();
return reader.CurrentEncoding;
}
}
}
}
105 changes: 105 additions & 0 deletions src/code/Resources/PathUtilityStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading