diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CSVCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CSVCommands.cs index ca7752da8bc..5e3e247a376 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CSVCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CSVCommands.cs @@ -212,8 +212,20 @@ public SwitchParameter NoClobber /// Encoding optional flag /// [Parameter()] - [ValidateSetAttribute(new string[] { "Unicode", "UTF7", "UTF8", "ASCII", "UTF32", "BigEndianUnicode", "Default", "OEM" })] - public string Encoding { get; set; } + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, + EncodingConversion.Unicode, + EncodingConversion.Utf7, + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); /// /// Property that sets append parameter. @@ -373,7 +385,7 @@ private void CreateFileStream() PathUtils.MasterStreamOpen( this, this.Path, - Encoding ?? "ASCII", + Encoding, false, // defaultEncoding Append, Force, @@ -577,8 +589,20 @@ public SwitchParameter UseCulture /// Encoding optional flag /// [Parameter()] - [ValidateSetAttribute(new[] { "Unicode", "UTF7", "UTF8", "ASCII", "UTF32", "BigEndianUnicode", "Default", "OEM" })] - public string Encoding { get; set; } + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, + EncodingConversion.Unicode, + EncodingConversion.Utf7, + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); /// /// Avoid writing out duplicate warning messages when there are diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index b1c65d3f2b1..bc90a9536ba 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -46,14 +46,20 @@ public sealed class FormatHex : PSCmdlet /// Type of character encoding for InputObject /// [Parameter(ParameterSetName = "ByInputObject")] - [ValidateSetAttribute(new string[] { - EncodingConversion.Unicode, + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, EncodingConversion.BigEndianUnicode, - EncodingConversion.Utf8, + EncodingConversion.OEM, + EncodingConversion.Unicode, EncodingConversion.Utf7, - EncodingConversion.Utf32, - EncodingConversion.Ascii})] - public string Encoding { get; set; } = "Ascii"; + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); /// /// This parameter is no-op @@ -239,8 +245,7 @@ private void ProcessObjectContent(PSObject inputObject) else if (obj is string) { string inputString = obj.ToString(); - Encoding resolvedEncoding = EncodingConversion.Convert(this, Encoding); - inputBytes = resolvedEncoding.GetBytes(inputString); + inputBytes = Encoding.GetBytes(inputString); } else if (obj is byte) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/out-file/Out-File.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/out-file/Out-File.cs index b8fd1cc09b6..d7c0d43c58c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/out-file/Out-File.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/out-file/Out-File.cs @@ -3,6 +3,7 @@ --********************************************************************/ using System; +using System.Text; using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Host; @@ -72,25 +73,20 @@ public string LiteralPath /// /// [Parameter(Position = 1)] - [ValidateNotNullOrEmpty] - [ValidateSetAttribute(new string[] { - EncodingConversion.Unknown, - EncodingConversion.String, - EncodingConversion.Unicode, + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, EncodingConversion.BigEndianUnicode, - EncodingConversion.Utf8, + EncodingConversion.OEM, + EncodingConversion.Unicode, EncodingConversion.Utf7, - EncodingConversion.Utf32, - EncodingConversion.Ascii, - EncodingConversion.Default, - EncodingConversion.OEM })] - public string Encoding - { - get { return _encoding; } - set { _encoding = value; } - } - - private string _encoding; + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); /// /// Property that sets append parameter. @@ -196,7 +192,7 @@ private LineOutput InstantiateLineOutputInterface() PathUtils.MasterStreamOpen( this, FilePath, - _encoding, + Encoding, false, // defaultEncoding Append, Force, diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs index e694403c6b8..7cd0138844e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs @@ -77,19 +77,20 @@ public SwitchParameter Force /// Encoding optional flag /// [Parameter] - [ValidateSetAttribute(new string[] { "Unicode", "UTF7", "UTF8", "ASCII", "UTF32", "BigEndianUnicode", "Default", "OEM" })] - public string Encoding - { - get - { - return _encoding.GetType().Name; - } - set - { - _encoding = EncodingConversion.Convert(this, value); - } - } - private Encoding _encoding = System.Text.Encoding.UTF8; + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, + EncodingConversion.Unicode, + EncodingConversion.Utf7, + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); #endregion Parameters @@ -144,7 +145,7 @@ protected override void BeginProcessing() List generatedFiles = GenerateProxyModule( tempDirectory, Path.GetFileName(directory.FullName), - _encoding, + Encoding, _force, listOfCommandMetadata, alias2resolvedCommandName, diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs index deaa37323db..e6bcadf02bf 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs @@ -3,6 +3,7 @@ --********************************************************************/ using System; +using System.Text; using System.Text.RegularExpressions; using System.IO; using System.Collections; @@ -1201,19 +1202,20 @@ public SwitchParameter AllMatches /// The text encoding to process each file as. /// [Parameter] - [ValidateNotNullOrEmpty] - [ValidateSetAttribute(new string[] { + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, EncodingConversion.Unicode, EncodingConversion.Utf7, EncodingConversion.Utf8, - EncodingConversion.Utf32, - EncodingConversion.Ascii, - EncodingConversion.BigEndianUnicode, - EncodingConversion.Default, - EncodingConversion.OEM })] - public string Encoding { get; set; } - - private System.Text.Encoding _textEncoding; + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); /// /// The number of context lines to collect. If set to a @@ -1282,16 +1284,6 @@ public SwitchParameter AllMatches /// protected override void BeginProcessing() { - // Process encoding switch. - if (Encoding != null) - { - _textEncoding = EncodingConversion.Convert(this, Encoding); - } - else - { - _textEncoding = new System.Text.UTF8Encoding(); - } - if (!_simpleMatch) { RegexOptions regexOptions = (_caseSensitive) ? RegexOptions.None : RegexOptions.IgnoreCase; @@ -1434,7 +1426,7 @@ private bool ProcessFile(string filename) using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { - using (StreamReader sr = new StreamReader(fs, _textEncoding)) + using (StreamReader sr = new StreamReader(fs, Encoding)) { String line; int lineNo = 0; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs index b2667207242..58328c56b35 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs @@ -89,20 +89,24 @@ public SwitchParameter BodyAsHtml /// /// Specifies the encoding used for the content of the body and also the subject. + /// This is set to ASCII to ensure there are no problems with any email server /// [Parameter()] [Alias("BE")] [ValidateNotNullOrEmpty] - [ArgumentToEncodingNameTransformationAttribute()] - public Encoding Encoding - { - get { return _encoding; } - set - { - _encoding = value; - } - } - private Encoding _encoding = new ASCIIEncoding(); + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, + EncodingConversion.Unicode, + EncodingConversion.Utf7, + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ArgumentToEncodingTransformationAttribute()] + public Encoding Encoding { get; set; } = Encoding.ASCII; /// /// Specifies the address collection that contains the @@ -369,8 +373,8 @@ protected override _mMailMessage.Body = _body; //set the subject and body encoding - _mMailMessage.SubjectEncoding = _encoding; - _mMailMessage.BodyEncoding = _encoding; + _mMailMessage.SubjectEncoding = Encoding; + _mMailMessage.BodyEncoding = Encoding; // Set the format of the mail message body as HTML _mMailMessage.IsBodyHtml = _bodyashtml; @@ -490,37 +494,5 @@ protected override void EndProcessing() #endregion } - /// - /// To make it easier to specify -Encoding parameter, we add an ArgumentTransformationAttribute here. - /// When the input data is of type string and is valid to be converted to System.Text.Encoding, we do - /// the conversion and return the converted value. Otherwise, we just return the input data. - /// - internal sealed class ArgumentToEncodingNameTransformationAttribute : ArgumentTransformationAttribute - { - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) - { - string encodingName; - if (LanguagePrimitives.TryConvertTo(inputData, out encodingName)) - { - if (string.Equals(encodingName, EncodingConversion.Unknown, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.String, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Unicode, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.BigEndianUnicode, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Utf8, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Utf7, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Utf32, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Ascii, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Default, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.OEM, StringComparison.OrdinalIgnoreCase)) - { - // the encodingName is guaranteed to be valid, so it is safe to pass null to method - // Convert(Cmdlet cmdlet, string encoding) as the value of 'cmdlet'. - return EncodingConversion.Convert(null, encodingName); - } - } - return inputData; - } - } - #endregion -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs index 50edf754d29..4f87989f169 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs @@ -108,8 +108,20 @@ public SwitchParameter NoClobber /// /// [Parameter] - [ValidateSetAttribute(new string[] { "Unicode", "UTF7", "UTF8", "ASCII", "UTF32", "BigEndianUnicode", "Default", "OEM" })] - public string Encoding { get; set; } = "Unicode"; + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, + EncodingConversion.Unicode, + EncodingConversion.Utf7, + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = ClrFacade.GetDefaultEncoding(); #endregion Command Line Parameters diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index dc50286acf9..f6b6449c9d8 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1147,11 +1147,12 @@ internal static bool Succeeded(int hresult) return hresult >= 0; } - internal static FileSystemCmdletProviderEncoding GetEncoding(string path) + // Attempt to determine the existing encoding + internal static Encoding GetEncoding(string path) { if (!File.Exists(path)) { - return FileSystemCmdletProviderEncoding.Default; + return ClrFacade.GetDefaultEncoding(); } byte[] initialBytes = new byte[100]; @@ -1169,12 +1170,12 @@ internal static FileSystemCmdletProviderEncoding GetEncoding(string path) } catch (IOException) { - return FileSystemCmdletProviderEncoding.Default; + return ClrFacade.GetDefaultEncoding(); } // Test for four-byte preambles string preamble = null; - FileSystemCmdletProviderEncoding foundEncoding = FileSystemCmdletProviderEncoding.Default; + Encoding foundEncoding = ClrFacade.GetDefaultEncoding(); if (bytesRead > 3) { @@ -1210,77 +1211,26 @@ internal static FileSystemCmdletProviderEncoding GetEncoding(string path) string initialBytesAsAscii = System.Text.Encoding.ASCII.GetString(initialBytes, 0, bytesRead); if (initialBytesAsAscii.IndexOfAny(nonPrintableCharacters) >= 0) { - return FileSystemCmdletProviderEncoding.Byte; + return Encoding.Unicode; } - return FileSystemCmdletProviderEncoding.Ascii; + return Encoding.ASCII; } - internal static Encoding GetEncodingFromEnum(FileSystemCmdletProviderEncoding encoding) - { - // Default to unicode encoding - Encoding result = Encoding.Unicode; - - switch (encoding) - { - case FileSystemCmdletProviderEncoding.String: - result = Encoding.Unicode; - break; - - case FileSystemCmdletProviderEncoding.Unicode: - result = Encoding.Unicode; - break; - - case FileSystemCmdletProviderEncoding.BigEndianUnicode: - result = Encoding.BigEndianUnicode; - break; - - case FileSystemCmdletProviderEncoding.UTF8: - result = Encoding.UTF8; - break; - - case FileSystemCmdletProviderEncoding.UTF7: - result = Encoding.UTF7; - break; - - case FileSystemCmdletProviderEncoding.UTF32: - result = Encoding.UTF32; - break; - - case FileSystemCmdletProviderEncoding.BigEndianUTF32: - result = Encoding.BigEndianUnicode; - break; - - case FileSystemCmdletProviderEncoding.Ascii: - result = Encoding.ASCII; - break; - - case FileSystemCmdletProviderEncoding.Default: - result = ClrFacade.GetDefaultEncoding(); - break; - - case FileSystemCmdletProviderEncoding.Oem: - result = ClrFacade.GetOEMEncoding(); - break; - - default: - break; - } - - return result; - } // GetEncodingFromEnum + // BigEndianUTF32 encoding is possible, but requires creation + internal static Encoding BigEndianUTF32Encoding = new UTF32Encoding(bigEndian: true, byteOrderMark: true); // [System.Text.Encoding]::GetEncodings() | Where-Object { $_.GetEncoding().GetPreamble() } | // Add-Member ScriptProperty Preamble { $this.GetEncoding().GetPreamble() -join "-" } -PassThru | // Format-Table -Auto - internal static Dictionary encodingMap = - new Dictionary() + internal static Dictionary encodingMap = + new Dictionary() { - { "255-254", FileSystemCmdletProviderEncoding.Unicode }, - { "254-255", FileSystemCmdletProviderEncoding.BigEndianUnicode }, - { "255-254-0-0", FileSystemCmdletProviderEncoding.UTF32 }, - { "0-0-254-255", FileSystemCmdletProviderEncoding.BigEndianUTF32 }, - { "239-187-191", FileSystemCmdletProviderEncoding.UTF8 }, + { "255-254", Encoding.Unicode }, + { "254-255", Encoding.BigEndianUnicode }, + { "255-254-0-0", Encoding.UTF32 }, + { "0-0-254-255", BigEndianUTF32Encoding }, + { "239-187-191", Encoding.UTF8 }, }; internal static char[] nonPrintableCharacters = { diff --git a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs index d3cf16bc0a8..6d64065a5bd 100644 --- a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs +++ b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs @@ -1068,14 +1068,8 @@ internal string Path set { _path = value; - - Encoding = Encoding.UTF8; - FileSystemCmdletProviderEncoding fileEncoding = Utils.GetEncoding(value); - - if (fileEncoding != FileSystemCmdletProviderEncoding.Default) - { - Encoding = Utils.GetEncodingFromEnum(fileEncoding); - } + // Get the encoding from the file, or default (UTF8-NoBom) + Encoding = Utils.GetEncoding(value); } } private string _path; diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 172e8849570..d08125db5c2 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -6614,12 +6614,17 @@ public IContentReader GetContentReader(string path) delimiter = dynParams.Delimiter; // Get the stream type - usingByteEncoding = dynParams.UsingByteEncoding; + usingByteEncoding = dynParams.AsByteStream; streamTypeSpecified = dynParams.WasStreamTypeSpecified; + if (usingByteEncoding && streamTypeSpecified) + { + WriteWarning(FileSystemProviderStrings.EncodingNotUsed); + } + if (streamTypeSpecified) { - encoding = dynParams.EncodingType; + encoding = dynParams.Encoding; } // Get the wait value @@ -6765,12 +6770,17 @@ public IContentWriter GetContentWriter(string path) if (dynParams != null) { - usingByteEncoding = dynParams.UsingByteEncoding; + usingByteEncoding = dynParams.AsByteStream; streamTypeSpecified = dynParams.WasStreamTypeSpecified; + if (usingByteEncoding && streamTypeSpecified) + { + WriteWarning(FileSystemProviderStrings.EncodingNotUsed); + } + if (streamTypeSpecified) { - encoding = dynParams.EncodingType; + encoding = dynParams.Encoding; } #if !UNIX @@ -7477,73 +7487,6 @@ public static Hashtable Invoke(System.Management.Automation.PowerShell ps, FileS } } - /// - /// Defines the values that can be supplied as the encoding parameter in the - /// FileSystemContentDynamicParametersBase class. - /// - public enum FileSystemCmdletProviderEncoding - { - /// - /// No encoding. - /// - Unknown, - - /// - /// Unicode encoding. - /// - String, - - /// - /// Unicode encoding. - /// - Unicode, - - /// - /// Byte encoding. - /// - Byte, - - /// - /// Big Endian Unicode encoding. - /// - BigEndianUnicode, - - /// - /// UTF8 encoding. - /// - UTF8, - - /// - /// UTF7 encoding. - /// - UTF7, - - /// - /// UTF32 encoding. - /// - UTF32, - - /// - /// ASCII encoding. - /// - Ascii, - - /// - /// Default encoding. - /// - Default, - - /// - /// OEM encoding. - /// - Oem, - - /// - /// Big Endian UTF32 encoding. - /// - BigEndianUTF32, - } // FileSystemCmdletProviderEncoding - #endregion #region Dynamic Parameters @@ -7647,7 +7590,39 @@ public class FileSystemContentDynamicParametersBase /// reading data from the file. /// [Parameter] - public FileSystemCmdletProviderEncoding Encoding { get; set; } = FileSystemCmdletProviderEncoding.String; + [ArgumentToEncodingTransformationAttribute()] + [ArgumentCompletions( + EncodingConversion.Ascii, + EncodingConversion.BigEndianUnicode, + EncodingConversion.OEM, + EncodingConversion.Unicode, + EncodingConversion.Utf7, + EncodingConversion.Utf8, + EncodingConversion.Utf8Bom, + EncodingConversion.Utf8NoBom, + EncodingConversion.Utf32 + )] + [ValidateNotNullOrEmpty] + public Encoding Encoding + { + get + { + return _encoding; + } + set + { + _encoding = value; + // If an encoding was explicitly set, be sure to capture that. + WasStreamTypeSpecified = true; + } + } + private Encoding _encoding = ClrFacade.GetDefaultEncoding(); + + /// + /// Return file contents as a byte stream or create file from a series of bytes + /// + [Parameter] + public SwitchParameter AsByteStream { get; set; } #if !UNIX /// @@ -7657,40 +7632,11 @@ public class FileSystemContentDynamicParametersBase public String Stream { get; set; } #endif - /// - /// Gets the encoding from the specified StreamType parameter. - /// - public Encoding EncodingType - { - get - { - return Utils.GetEncodingFromEnum(Encoding); - } - } // EncodingType - - /// - /// Gets the Byte Encoding status of the StreamType parameter. Returns true - /// if the stream was opened with "Byte" encoding, false otherwise. - /// - public bool UsingByteEncoding - { - get - { - return Encoding == FileSystemCmdletProviderEncoding.Byte; - } // get - } // UsingByteEncoding - /// /// Gets the status of the StreamType parameter. Returns true /// if the stream was opened with a user-specified encoding, false otherwise. /// - public bool WasStreamTypeSpecified - { - get - { - return (Encoding != FileSystemCmdletProviderEncoding.String); - } // get - } // WasStreamTypeSpecified + public bool WasStreamTypeSpecified { get; private set; } } // class FileSystemContentDynamicParametersBase @@ -7748,13 +7694,13 @@ public string Delimiter get { return _delimiter; - } // get + } set { DelimiterSpecified = true; _delimiter = value; - } // set + } } private string _delimiter = "\n"; diff --git a/src/System.Management.Automation/resources/FileSystemProviderStrings.resx b/src/System.Management.Automation/resources/FileSystemProviderStrings.resx index 696fefc7380..36894977dca 100644 --- a/src/System.Management.Automation/resources/FileSystemProviderStrings.resx +++ b/src/System.Management.Automation/resources/FileSystemProviderStrings.resx @@ -243,6 +243,9 @@ Cannot process path '{0}' because the target represents a reserved device name. + + Encoding not used when '-AsByteStream' specified. + Cannot proceed with byte encoding. When using byte encoding the content must be of type byte. diff --git a/src/System.Management.Automation/utils/ClrFacade.cs b/src/System.Management.Automation/utils/ClrFacade.cs index 03c4b252c88..f8eafa3622e 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -93,14 +93,9 @@ internal static Encoding GetDefaultEncoding() { if (s_defaultEncoding == null) { -#if UNIX // PowerShell Core on Unix - s_defaultEncoding = new UTF8Encoding(false); -#else // PowerShell Core on Windows + // load all available encodings EncodingRegisterProvider(); - - uint currentAnsiCp = NativeMethods.GetACP(); - s_defaultEncoding = Encoding.GetEncoding((int)currentAnsiCp); -#endif + s_defaultEncoding = new UTF8Encoding(false); } return s_defaultEncoding; } @@ -109,16 +104,17 @@ internal static Encoding GetDefaultEncoding() /// /// Facade for getting OEM encoding + /// OEM encodings work on all platforms, or rather codepage 437 is available on both Windows and Non-Windows /// internal static Encoding GetOEMEncoding() { if (s_oemEncoding == null) { -#if UNIX // PowerShell Core on Unix - s_oemEncoding = GetDefaultEncoding(); -#else // PowerShell Core on Windows + // load all available encodings EncodingRegisterProvider(); - +#if UNIX + s_oemEncoding = new UTF8Encoding(false); +#else uint oemCp = NativeMethods.GetOEMCP(); s_oemEncoding = Encoding.GetEncoding((int)oemCp); #endif diff --git a/src/System.Management.Automation/utils/EncodingUtils.cs b/src/System.Management.Automation/utils/EncodingUtils.cs new file mode 100644 index 00000000000..e98ef8968a1 --- /dev/null +++ b/src/System.Management.Automation/utils/EncodingUtils.cs @@ -0,0 +1,106 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +using System.Management.Automation.Internal; + +namespace System.Management.Automation +{ + + internal static class EncodingConversion + { + internal const string Unknown = "unknown"; + internal const string String = "string"; + internal const string Unicode = "unicode"; + internal const string BigEndianUnicode = "bigendianunicode"; + internal const string Ascii = "ascii"; + internal const string Utf8 = "utf8"; + internal const string Utf8NoBom = "utf8NoBOM"; + internal const string Utf8Bom = "utf8BOM"; + internal const string Utf7 = "utf7"; + internal const string Utf32 = "utf32"; + internal const string Default = "default"; + internal const string OEM = "oem"; + internal static readonly string[] TabCompletionResults = { + Ascii, BigEndianUnicode, OEM, Unicode, Utf7, Utf8, Utf8Bom, Utf8NoBom, Utf32 + }; + + internal static Dictionary encodingMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { Ascii, System.Text.Encoding.ASCII }, + { BigEndianUnicode, System.Text.Encoding.BigEndianUnicode }, + { Default, ClrFacade.GetDefaultEncoding() }, + { OEM, ClrFacade.GetOEMEncoding() }, + { Unicode, System.Text.Encoding.Unicode }, + { Utf7, System.Text.Encoding.UTF7 }, + { Utf8, ClrFacade.GetDefaultEncoding() }, + { Utf8Bom, System.Text.Encoding.UTF8 }, + { Utf8NoBom, ClrFacade.GetDefaultEncoding() }, + { Utf32, System.Text.Encoding.UTF32 }, + { String, System.Text.Encoding.Unicode }, + { Unknown, System.Text.Encoding.Unicode }, + }; + + /// + /// retrieve the encoding parameter from the command line + /// it throws if the encoding does not match the known ones + /// + /// a System.Text.Encoding object (null if no encoding specified) + internal static Encoding Convert(Cmdlet cmdlet, string encoding) + { + if (string.IsNullOrEmpty(encoding)) + { + // no parameter passed, default to UTF8 + return ClrFacade.GetDefaultEncoding(); + } + + Encoding foundEncoding; + if (encodingMap.TryGetValue(encoding, out foundEncoding)) + { + return foundEncoding; + } + + // error condition: unknown encoding value + string validEncodingValues = string.Join(", ", TabCompletionResults); + string msg = StringUtil.Format(PathUtilsStrings.OutFile_WriteToFileEncodingUnknown, encoding, validEncodingValues); + + ErrorRecord errorRecord = new ErrorRecord( + PSTraceSource.NewArgumentException("Encoding"), + "WriteToFileEncodingUnknown", + ErrorCategory.InvalidArgument, + null); + + errorRecord.ErrorDetails = new ErrorDetails(msg); + cmdlet.ThrowTerminatingError(errorRecord); + + return null; + } + + } + + /// + /// To make it easier to specify -Encoding parameter, we add an ArgumentTransformationAttribute here. + /// When the input data is of type string and is valid to be converted to System.Text.Encoding, we do + /// the conversion and return the converted value. Otherwise, we just return the input data. + /// + internal sealed class ArgumentToEncodingTransformationAttribute : ArgumentTransformationAttribute + { + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + string encodingName = inputData as String; + Encoding foundEncoding; + if (encodingName != null && EncodingConversion.encodingMap.TryGetValue(encodingName, out foundEncoding)) + { + return foundEncoding; + } + return inputData; + } + + } + +} diff --git a/src/System.Management.Automation/utils/PathUtils.cs b/src/System.Management.Automation/utils/PathUtils.cs index 632938e40e7..62ffc54faea 100644 --- a/src/System.Management.Automation/utils/PathUtils.cs +++ b/src/System.Management.Automation/utils/PathUtils.cs @@ -188,17 +188,10 @@ internal static void ReportFileOpenFailure(Cmdlet cmdlet, string filePath, Excep cmdlet.ThrowTerminatingError(errorRecord); } - internal static StreamReader OpenStreamReader(PSCmdlet command, string filePath, string encoding, bool isLiteralPath) + internal static StreamReader OpenStreamReader(PSCmdlet command, string filePath, Encoding encoding, bool isLiteralPath) { FileStream fileStream = OpenFileStream(filePath, command, isLiteralPath); - if (encoding == null) - { - return new StreamReader(fileStream); - } - else - { - return new StreamReader(fileStream, EncodingConversion.Convert(command, encoding)); - } + return new StreamReader(fileStream, encoding); } internal static FileStream OpenFileStream(string filePath, PSCmdlet command, bool isLiteralPath) @@ -436,84 +429,4 @@ internal static DirectoryInfo CreateTemporaryDirectory() return new DirectoryInfo(moduleDirectory.FullName); } } - - internal static class EncodingConversion - { - internal const string Unknown = "unknown"; - internal const string String = "string"; - internal const string Unicode = "unicode"; - internal const string BigEndianUnicode = "bigendianunicode"; - internal const string Ascii = "ascii"; - internal const string Utf8 = "utf8"; - internal const string Utf7 = "utf7"; - internal const string Utf32 = "utf32"; - internal const string Default = "default"; - internal const string OEM = "oem"; - - /// - /// retrieve the encoding parameter from the command line - /// it throws if the encoding does not match the known ones - /// - /// a System.Text.Encoding object (null if no encoding specified) - internal static Encoding Convert(Cmdlet cmdlet, string encoding) - { - if (string.IsNullOrEmpty(encoding)) - { - // no parameter passed, default to Unicode (OS preferred) - return System.Text.Encoding.Unicode; - } - - // Default to unicode (this matches Get-Content) - if (string.Equals(encoding, Unknown, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.Unicode; - - if (string.Equals(encoding, String, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.Unicode; - - // these are the encodings the CLR supports - if (string.Equals(encoding, Unicode, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.Unicode; - - if (string.Equals(encoding, BigEndianUnicode, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.BigEndianUnicode; - - if (string.Equals(encoding, Utf8, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.UTF8; - - if (string.Equals(encoding, Ascii, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.ASCII; - - if (string.Equals(encoding, Utf7, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.UTF7; - - if (string.Equals(encoding, Utf32, StringComparison.OrdinalIgnoreCase)) - return System.Text.Encoding.UTF32; - - if (string.Equals(encoding, Default, StringComparison.OrdinalIgnoreCase)) - return ClrFacade.GetDefaultEncoding(); - - if (string.Equals(encoding, OEM, StringComparison.OrdinalIgnoreCase)) - { - return ClrFacade.GetOEMEncoding(); - } - - // error condition: unknown encoding value - string validEncodingValues = string.Join( - ", ", - new string[] { Unknown, String, Unicode, BigEndianUnicode, Ascii, Utf8, Utf7, Utf32, Default, OEM }); - string msg = StringUtil.Format(PathUtilsStrings.OutFile_WriteToFileEncodingUnknown, - encoding, validEncodingValues); - - ErrorRecord errorRecord = new ErrorRecord( - PSTraceSource.NewArgumentException("Encoding"), - "WriteToFileEncodingUnknown", - ErrorCategory.InvalidArgument, - null); - - errorRecord.ErrorDetails = new ErrorDetails(msg); - cmdlet.ThrowTerminatingError(errorRecord); - - return null; - } - } } diff --git a/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 b/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 index e4e5fc7efcb..0b7d9a7f9e0 100644 --- a/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 +++ b/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 @@ -10,7 +10,7 @@ Describe "Redirection operator now supports encoding changes" -Tags "CI" { } - # If out-file -encoding happens to have a default, be sure to + # If Out-File -Encoding happens to have a default, be sure to # save it away $SavedValue = $null $oldDefaultParameterValues = $psDefaultParameterValues.Clone() @@ -22,21 +22,22 @@ Describe "Redirection operator now supports encoding changes" -Tags "CI" { } BeforeEach { # start each test with a clean plate! - $psdefaultParameterValues.Remove("out-file:encoding") + $psdefaultParameterValues.Remove("Out-File:Encoding") } AfterEach { # end each test with a clean plate! - $psdefaultParameterValues.Remove("out-file:encoding") + $psdefaultParameterValues.Remove("Out-File:Encoding") } - It "If encoding is unset, redirection should be Unicode" { + It "If encoding is unset, redirection should be UTF8 without bom" { $asciiString > TESTDRIVE:\file.txt - $bytes = get-content -encoding byte TESTDRIVE:\file.txt - # create the expected - $BOM = [text.encoding]::unicode.GetPreamble() - $TXT = [text.encoding]::unicode.GetBytes($asciiString) - $CR = [text.encoding]::unicode.GetBytes($asciiCR) - $expectedBytes = .{ $BOM; $TXT; $CR } + $bytes = Get-Content -AsByteStream TESTDRIVE:\file.txt + # create the expected - utf8 encoding without a bom + $encoding = [Text.UTF8Encoding]::new($false) + # we know that there will be no preamble, so don't provide any bytes + $TXT = $encoding.GetBytes($asciiString) + $CR = $encoding.GetBytes($asciiCR) + $expectedBytes = .{ $TXT; $CR } $bytes.Count | should be $expectedBytes.count for($i = 0; $i -lt $bytes.count; $i++) { $bytes[$i] | Should be $expectedBytes[$i] @@ -44,7 +45,7 @@ Describe "Redirection operator now supports encoding changes" -Tags "CI" { } # $availableEncodings = "unknown","string","unicode","bigendianunicode","utf8","utf7", "utf32","ascii","default","oem" - $availableEncodings = (get-command out-file).Parameters["Encoding"].Attributes.ValidValues + $availableEncodings = (Get-Command Out-File).Parameters["Encoding"].Attributes.ValidValues foreach($encoding in $availableEncodings) { $skipTest = $false @@ -56,21 +57,21 @@ Describe "Redirection operator now supports encoding changes" -Tags "CI" { $skipTest = $true } - # some of the encodings accepted by out-file aren't real, - # and out-file has its own translation, so we'll + # some of the encodings accepted by Out-File aren't real, + # and Out-File has its own translation, so we'll # not do that logic here, but simply ignore those encodings # as they eventually are translated to "real" encoding - $enc = [system.text.encoding]::$encoding + $enc = [System.Text.Encoding]::$encoding if ( $enc ) { - $msg = "Overriding encoding for out-file is respected for $encoding" + $msg = "Overriding encoding for Out-File is respected for $encoding" $BOM = $enc.GetPreamble() $TXT = $enc.GetBytes($asciiString) $CR = $enc.GetBytes($asciiCR) $expectedBytes = .{ $BOM; $TXT; $CR } - $psdefaultparameterValues["out-file:encoding"] = "$encoding" + $psdefaultparameterValues["Out-File:Encoding"] = "$encoding" $asciiString > TESTDRIVE:/file.txt - $observedBytes = Get-Content -encoding Byte TESTDRIVE:/file.txt + $observedBytes = Get-Content -AsByteStream TESTDRIVE:/file.txt # THE TEST It $msg -Skip:$skipTest { $observedBytes.Count | Should be $expectedBytes.Count diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/TestGetCommand.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/TestGetCommand.Tests.ps1 index 8f48d529620..11b0e6f7841 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/TestGetCommand.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/TestGetCommand.Tests.ps1 @@ -13,11 +13,11 @@ ) DynamicParam { - if ( ! $testToRun ) { - $testToRun = "returnnull" + if ( ! $TestToRun ) { + $TestToRun = "returnnull" } $dynamicParamDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() - switch ( $testToRun ) + switch ( $TestToRun ) { "returnnull" { $dynamicParamDictionary = $null @@ -161,16 +161,16 @@ } } - It "Verify that get-command get-content includes the dynamic parameters when the cmdlet is checked against the file system provider implementation" { + It "Verify that Get-Command Get-Content includes the dynamic parameters when the cmdlet is checked against the file system provider implementation" { $fullPath = Join-Path $TestDrive -ChildPath "blah" New-Item -Path $fullPath -ItemType directory -Force - $results = get-command get-content -path $fullPath + $results = Get-Command Get-Content -Path $fullPath $dynamicParameter = "Wait", "Encoding", "Delimiter" VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $dynamicParameter } - It "Verify that get-command get-content doesn't have any dynamic parameters for Function provider" { - $results =get-command get-content -path function: + It "Verify that Get-Command Get-Content doesn't have any dynamic parameters for Function provider" { + $results =Get-Command Get-Content -Path function: $dynamicParameter = "Wait", "Encoding", "Delimiter" foreach ($dynamicPara in $dynamicParameter) { @@ -179,14 +179,14 @@ } It "Verify that the specified dynamic parameter exists in the CmdletInfo result returned" { - $results = get-command testgetcommand-dynamicparametersdcr -testtorun return1 + $results = Get-Command TestGetCommand-DynamicParametersDCR -TestToRun return1 $dynamicParameter = "OneString" VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $dynamicParameter VerifyParameterType -cmdlet $results[0] -parameterName $dynamicParameter -ParameterType string } It "Verify three dynamic parameters are created properly" { - $results = get-command testgetcommand-dynamicparametersdcr -testtorun return3 + $results = Get-Command TestGetCommand-DynamicParametersDCR -TestToRun return3 $dynamicParameter = "OneString", "TwoInt", "ThreeBool" VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $dynamicParameter @@ -196,26 +196,26 @@ } It "Verify dynamic parameter type is process" { - $results = get-command testgetcommand-dynamicparametersdcr -args '-testtorun','returngenericparameter','-parametertype','System.Diagnostics.Process' + $results = Get-Command TestGetCommand-DynamicParametersDCR -Args '-TestToRun','returngenericparameter','-parametertype','System.Diagnostics.Process' VerifyParameterType -cmdlet $results[0] -parameterName "TypedValue" -parameterType System.Diagnostics.Process } It "Verify a single cmdlet returned using verb and noun parameter set syntax works properly" { $paramName = "OneString" - $results = get-command -verb testgetcommand -noun dynamicparametersdcr -testtorun Return1 + $results = Get-Command -Verb TestGetCommand -Noun DynamicParametersDCR -TestToRun Return1 VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $paramName VerifyParameterType -cmdlet $results[0] -parameterName $paramName -parameterType string } It "Verify Single Cmdlet Using Verb&Noun ParameterSet" { $paramName = "Encoding" - $results = get-command -verb get -noun content -Encoding Unicode + $results = Get-Command -Verb get -Noun content -Encoding Unicode VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $paramName - VerifyParameterType -cmdlet $results[0] -parameterName $paramName -parameterType Microsoft.PowerShell.Commands.FileSystemCmdletProviderEncoding + VerifyParameterType -cmdlet $results[0] -parameterName $paramName -parameterType System.Text.Encoding } It "Verify Single Cmdlet Using Verb&Noun ParameterSet With Usage" { - $results = get-command -verb get -noun content -Encoding Unicode -syntax + $results = Get-Command -Verb get -Noun content -Encoding Unicode -Syntax $results.ToString() | Should Match "-Encoding" $results.ToString() | Should Match "-Wait" $results.ToString() | Should Match "-Delimiter" @@ -226,24 +226,24 @@ $tempFile = "mytempfile.ps1" $fullPath = Join-Path $TestDrive -ChildPath $tempFile "$a = dir" > $fullPath - $results = get-command $fullPath + $results = Get-Command $fullPath $results.Name | Should Be $tempFile $results.Definition | Should Be $fullPath } It "Two dynamic parameters are created properly" { - $results = get-command testgetcommand-dynamicparametersdcr -testtorun return2 + $results = Get-Command TestGetCommand-DynamicParametersDCR -TestToRun return2 $dynamicParameter = "OneString", "TwoInt" VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $dynamicParameter VerifyParameterType -cmdlet $results[0] -parameterName "OneString" -ParameterType string VerifyParameterType -cmdlet $results[0] -parameterName "TwoInt" -ParameterType int } - It "Throw an Exception when set testtorun to 'returnduplicateparameter'" { + It "Throw an Exception when set TestToRun to 'returnduplicateparameter'" { try { - Get-Command testgetcommand-dynamicparametersdcr -testtorun returnduplicateparameter -ErrorAction Stop + Get-Command TestGetCommand-DynamicParametersDCR -TestToRun returnduplicateparameter -ErrorAction Stop throw "No Exception!" } catch @@ -253,22 +253,22 @@ } It "verify if get the proper dynamic parameter type skipped by issue #1430" -Pending { - $results = Get-Command testgetcommand-dynamicparametersdcr -testtorun returngenericparameter -parametertype System.Diagnostics.Process + $results = Get-Command TestGetCommand-DynamicParametersDCR -TestToRun returngenericparameter -parametertype System.Diagnostics.Process VerifyParameterType -cmdlet $results[0] -parameterName "TypedValue" -parameterType System.Diagnostics.Process } It "It works with Single Cmdlet Using Verb&Noun ParameterSet" { $paramName = "Encoding" - $results = Get-Command -verb get -noun content -encoding UTF8 + $results = Get-Command -Verb get -Noun content -encoding UTF8 VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $paramName - VerifyParameterType -cmdlet $results[0] -parameterName $paramName -ParameterType Microsoft.PowerShell.Commands.FileSystemCmdletProviderEncoding + VerifyParameterType -cmdlet $results[0] -parameterName $paramName -ParameterType System.Text.Encoding } #unsupported parameter: -synop It "[Unsupported]It works with Single Cmdlet Using Verb&Noun ParameterSet With Synopsis" -Pending { $paramName = "Encoding" - $results = get-command -verb get -noun content -encoding UTF8 -synop + $results = Get-Command -Verb get -Noun content -encoding UTF8 -Synop VerifyDynamicParametersExist -cmdlet $results[0] -parameterNames $paramName - VerifyParameterType -cmdlet $results[0] -parameterName $paramName -ParameterType Microsoft.PowerShell.Commands.FileSystemCmdletProviderEncoding + VerifyParameterType -cmdlet $results[0] -parameterName $paramName -ParameterType System.Text.Encoding } -} \ No newline at end of file +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 index d338a7cabea..af573035193 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 @@ -70,8 +70,8 @@ Describe "Get-Content" -Tags "CI" { Pop-Location } #[BugId(BugDatabase.WindowsOutOfBandReleases, 906022)] - It "should throw 'PSNotSupportedException' when you set-content to an unsupported provider" -Skip:($IsLinux -Or $IsMacOS) { - {get-content -path HKLM:\\software\\microsoft -ea stop} | Should Throw "IContentCmdletProvider interface is not implemented" + It "should throw 'PSNotSupportedException' when you Set-Content to an unsupported provider" -Skip:($IsLinux -Or $IsMacOS) { + {Get-Content -Path HKLM:\\software\\microsoft -EA stop} | Should Throw "IContentCmdletProvider interface is not implemented" } It 'Verifies -Tail reports a TailNotSupported error for unsupported providers' { {Get-Content -Path Variable:\PSHOME -Tail 1 -ErrorAction Stop} | ShouldBeErrorId 'TailNotSupported,Microsoft.PowerShell.Commands.GetContentCommand' @@ -99,53 +99,52 @@ baz "@ $expected = 'foo' $tailCount = 3 - [Microsoft.PowerShell.Commands.FileSystemCmdletProviderEncoding] $encoding = $EncodingName $testPath = Join-Path -Path $TestDrive -ChildPath 'TailWithEncoding.txt' - $content | Set-Content -Path $testPath -Encoding $encoding + $content | Set-Content -Path $testPath -Encoding $encodingName $expected = 'foo' - $actual = Get-Content -Path $testPath -Tail $tailCount -Encoding $encoding + $actual = Get-Content -Path $testPath -Tail $tailCount -Encoding $encodingName $actual.GetType() | Should Be "System.Object[]" $actual.Length | Should Be $tailCount $actual[0] | Should Be $expected } It "should Get-Content with a variety of -Tail and -ReadCount values" {#[DRT] - set-content -path $testPath "Hello,World","Hello2,World2","Hello3,World3","Hello4,World4" - $result=get-content -path $testPath -readcount:-1 -tail 5 + Set-Content -Path $testPath "Hello,World","Hello2,World2","Hello3,World3","Hello4,World4" + $result=Get-Content -Path $testPath -Readcount:-1 -Tail 5 $result.Length | Should Be 4 $expected = "Hello,World","Hello2,World2","Hello3,World3","Hello4,World4" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -readcount 0 -tail 3 + $result=Get-Content -Path $testPath -Readcount 0 -Tail 3 $result.Length | Should Be 3 $expected = "Hello2,World2","Hello3,World3","Hello4,World4" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -readcount 1 -tail 3 + $result=Get-Content -Path $testPath -Readcount 1 -Tail 3 $result.Length | Should Be 3 $expected = "Hello2,World2","Hello3,World3","Hello4,World4" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -readcount 99999 -tail 3 + $result=Get-Content -Path $testPath -Readcount 99999 -Tail 3 $result.Length | Should Be 3 $expected = "Hello2,World2","Hello3,World3","Hello4,World4" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -readcount 2 -tail 3 + $result=Get-Content -Path $testPath -Readcount 2 -Tail 3 $result.Length | Should Be 2 $expected = "Hello2,World2","Hello3,World3" $expected = $expected,"Hello4,World4" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -readcount 2 -tail 2 + $result=Get-Content -Path $testPath -Readcount 2 -Tail 2 $result.Length | Should Be 2 $expected = "Hello3,World3","Hello4,World4" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -delimiter "," -tail 2 + $result=Get-Content -Path $testPath -Delimiter "," -Tail 2 $result.Length | Should Be 2 $expected = "World3${nl}Hello4", "World4${nl}" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -delimiter "o" -tail 3 + $result=Get-Content -Path $testPath -Delimiter "o" -Tail 3 $result.Length | Should Be 3 $expected = "rld3${nl}Hell", '4,W', "rld4${nl}" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} - $result=get-content -path $testPath -encoding:Byte -tail 10 + $result=Get-Content -Path $testPath -AsByteStream -Tail 10 $result.Length | Should Be 10 if ($IsWindows) { $expected = 52, 44, 87, 111, 114, 108, 100, 52, 13, 10 @@ -155,9 +154,9 @@ baz for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} } #[BugId(BugDatabase.WindowsOutOfBandReleases, 905829)] - It "should get-content that matches the input string"{ - set-content $testPath "Hello,llllWorlld","Hello2,llllWorlld2" - $result=get-content $testPath -delimiter "ll" + It "should Get-Content that matches the input string"{ + Set-Content $testPath "Hello,llllWorlld","Hello2,llllWorlld2" + $result=Get-Content $testPath -Delimiter "ll" $result.Length | Should Be 9 $expected = 'He', 'o,', '', 'Wor', "d${nl}He", 'o2,', '', 'Wor', "d2${nl}" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should BeExactly $expected[$i]} @@ -170,7 +169,7 @@ baz Get-Content $testPath | Should BeExactly $testString } - It "Should support NTFS streams using -stream" -Skip:(!$IsWindows) { + It "Should support NTFS streams using -Stream" -Skip:(!$IsWindows) { Set-Content -Path $testPath -Stream hello -Value World Get-Content -Path $testPath | Should Be $testString Get-Content -Path $testPath -Stream hello | Should Be "World" @@ -190,12 +189,12 @@ baz } It "-Stream is not a valid parameter for on Linux/Mac" -Skip:($IsWindows) -TestCases @( - @{cmdlet="get-content"}, - @{cmdlet="set-content"}, - @{cmdlet="clear-content"}, - @{cmdlet="add-content"}, - @{cmdlet="get-item"}, - @{cmdlet="remove-item"} + @{cmdlet="Get-Content"}, + @{cmdlet="Set-Content"}, + @{cmdlet="Clear-Content"}, + @{cmdlet="Add-Content"}, + @{cmdlet="Get-Item"}, + @{cmdlet="Remove-Item"} ) { param($cmdlet) (Get-Command $cmdlet).Parameters["stream"] | Should BeNullOrEmpty @@ -237,7 +236,7 @@ baz $fileContent = $firstLine,$secondLine,$thirdLine,$fourthLine } BeforeEach{ - Set-content -Path $testPath $fileContent + Set-Content -Path $testPath $fileContent } It "Should return all lines when -Tail value is more than number of lines in the file"{ $result = Get-Content -Path $testPath -ReadCount -1 -Tail 5 -Encoding UTF7 @@ -283,6 +282,10 @@ baz $expected[1] = $thirdLine Compare-Object -ReferenceObject $expected -DifferenceObject $result | Should BeNullOrEmpty } + It "A warning should be emitted if both -AsByteStream and -Encoding are used together" { + [byte[]][char[]]"test" | Set-Content -Encoding Unicode -AsByteStream "${TESTDRIVE}\bfile.txt" -WarningVariable contentWarning *>$null + $contentWarning.Message | Should Match "-AsByteStream" + } } } @@ -295,7 +298,7 @@ Describe "Get-Content -Raw test" -Tags "CI" { @{character = "a`r`nb"; testname = "CRLF-separated files without trailing newline"; filename = "crlf-nt.txt"} ) { param ($character, $filename) - Set-Content -Encoding Ascii -NoNewline "$TestDrive\$filename" -value $character + Set-Content -Encoding Ascii -NoNewline "$TestDrive\$filename" -Value $character Get-Content -Raw "$TestDrive\$filename" | Should BeExactly $character } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Pester.Commands.Cmdlets.NoNewlineParameter.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Pester.Commands.Cmdlets.NoNewlineParameter.Tests.ps1 index fc3231b2445..fab6e4ce6af 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Pester.Commands.Cmdlets.NoNewlineParameter.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Pester.Commands.Cmdlets.NoNewlineParameter.Tests.ps1 @@ -6,19 +6,19 @@ Describe "Tests for -NoNewline parameter of Out-File, Add-Content and Set-Conten It "NoNewline parameter works on Out-File" { $temp = "${TESTDRIVE}/test1.txt" 1..5 | Out-File $temp -Encoding 'ASCII' -NoNewline - (Get-Content $temp -Encoding Byte).Count | Should Be 5 + (Get-Content $temp -AsByteStream).Count | Should Be 5 } It "NoNewline parameter works on Set-Content" { $temp = "${TESTDRIVE}/test2.txt" Set-Content -Path $temp -Value 'a','b','c' -Encoding 'ASCII' -NoNewline - (Get-Content $temp -Encoding Byte).Count | Should Be 3 + (Get-Content $temp -AsByteStream).Count | Should Be 3 } It "NoNewline parameter works on Add-Content" { $temp = "${TESTDRIVE}/test3.txt" $temp = New-TemporaryFile 1..9 | ForEach-Object {Add-Content -Path $temp -Value $_ -Encoding 'ASCII' -NoNewline} - (Get-Content $temp -Encoding Byte).Count | Should Be 9 + (Get-Content $temp -AsByteStream).Count | Should Be 9 } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 index 087680434d1..a63295fde4f 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 @@ -1,17 +1,25 @@ Describe "Set-Content cmdlet tests" -Tags "CI" { BeforeAll { $file1 = "file1.txt" - $filePath1 = join-path $testdrive $file1 + $filePath1 = Join-Path $testdrive $file1 # if the registry doesn't exist, don't run those tests - $skipRegistry = ! (test-path hklm:/) + $skipRegistry = ! (Test-Path hklm:/) } + + It "A warning should be emitted if both -AsByteStream and -Encoding are used together" { + $testfile = "${TESTDRIVE}\bfile.txt" + "test" | Set-Content $testfile + $result = Get-Content -AsByteStream -Encoding Unicode -Path $testfile -WarningVariable contentWarning *>$null + $contentWarning.Message | Should Match "-AsByteStream" + } + Context "Set-Content should create a file if it does not exist" { AfterEach { - Remove-Item -path $filePath1 -Force -ErrorAction SilentlyContinue + Remove-Item -Path $filePath1 -Force -ErrorAction SilentlyContinue } It "should create a file if it does not exist" { - set-content -path $filePath1 -value "$file1" - $result = Get-Content -path $filePath1 + Set-Content -Path $filePath1 -Value "$file1" + $result = Get-Content -Path $filePath1 $result| Should be "$file1" } } @@ -20,33 +28,33 @@ Describe "Set-Content cmdlet tests" -Tags "CI" { New-Item -Path $filePath1 -ItemType File -Force } It "should set-Content of testdrive\$file1" { - set-content -path $filePath1 -value "ExpectedContent" - $result = Get-Content -path $filePath1 + Set-Content -Path $filePath1 -Value "ExpectedContent" + $result = Get-Content -Path $filePath1 $result| Should be "ExpectedContent" } It "should return expected string from testdrive\$file1" { - $result = get-content -path $filePath1 + $result = Get-Content -Path $filePath1 $result | Should BeExactly "ExpectedContent" } It "should Set-Content to testdrive\dynamicfile.txt with dynamic parameters" { - set-content -path $testdrive\dynamicfile.txt -value "ExpectedContent" - $result = Get-Content -path $testdrive\dynamicfile.txt + Set-Content -Path $testdrive\dynamicfile.txt -Value "ExpectedContent" + $result = Get-Content -Path $testdrive\dynamicfile.txt $result| Should BeExactly "ExpectedContent" } It "should return expected string from testdrive\dynamicfile.txt" { - $result = get-content -path $testdrive\dynamicfile.txt + $result = Get-Content -Path $testdrive\dynamicfile.txt $result | Should BeExactly "ExpectedContent" } It "should remove existing content from testdrive\$file1 when the -Value is `$null" { - $AsItWas=get-content $filePath1 + $AsItWas=Get-Content $filePath1 $AsItWas |Should BeExactly "ExpectedContent" - set-content -path $filePath1 -value $null -ea stop - $AsItIs=get-content $filePath1 + Set-Content -Path $filePath1 -Value $null -EA stop + $AsItIs=Get-Content $filePath1 $AsItIs| Should Not Be $AsItWas } It "should throw 'ParameterArgumentValidationErrorNullNotAllowed' when -Path is `$null" { try { - set-content -path $null -value "ShouldNotWorkBecausePathIsNull" -ea stop + Set-Content -Path $null -Value "ShouldNotWorkBecausePathIsNull" -EA stop Throw "Previous statement unexpectedly succeeded..." } catch { @@ -55,16 +63,16 @@ Describe "Set-Content cmdlet tests" -Tags "CI" { } It "should throw 'ParameterArgumentValidationErrorNullNotAllowed' when -Path is `$()" { try { - set-content -path $() -value "ShouldNotWorkBecausePathIsInvalid" -ea stop + Set-Content -Path $() -Value "ShouldNotWorkBecausePathIsInvalid" -EA stop Throw "Previous statement unexpectedly succeeded..." } catch { $_.FullyQualifiedErrorId | Should Be "ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SetContentCommand" } } - It "should throw 'PSNotSupportedException' when you set-content to an unsupported provider" -skip:$skipRegistry { + It "should throw 'PSNotSupportedException' when you Set-Content to an unsupported provider" -skip:$skipRegistry { try { - set-content -path HKLM:\\software\\microsoft -value "ShouldNotWorkBecausePathIsUnsupported" -ea stop + Set-Content -Path HKLM:\\software\\microsoft -Value "ShouldNotWorkBecausePathIsUnsupported" -EA stop Throw "Previous statement unexpectedly succeeded..." } catch { @@ -73,8 +81,8 @@ Describe "Set-Content cmdlet tests" -Tags "CI" { } #[BugId(BugDatabase.WindowsOutOfBandReleases, 9058182)] It "should be able to pass multiple [string]`$objects to Set-Content through the pipeline to output a dynamic Path file" { - "hello","world"|set-content $testdrive\dynamicfile2.txt - $result=get-content $testdrive\dynamicfile2.txt + "hello","world"|Set-Content $testdrive\dynamicfile2.txt + $result=Get-Content $testdrive\dynamicfile2.txt $result.length |Should be 2 $result[0] |Should be "hello" $result[1] |Should be "world" @@ -87,7 +95,7 @@ Describe "Set-Content should work for PSDrive with UNC path as root" -Tags @('CI $file1 = "file1.txt" #create a random folder $randomFolderName = "TestFolder_" + (Get-Random).ToString() - $randomFolderPath = join-path $testdrive $randomFolderName + $randomFolderPath = Join-Path $testdrive $randomFolderName $null = New-Item -Path $randomFolderPath -ItemType Directory -ErrorAction SilentlyContinue } # test is Pending due to https://github.com/PowerShell/PowerShell/issues/3883 @@ -97,8 +105,8 @@ Describe "Set-Content should work for PSDrive with UNC path as root" -Tags @('CI # create share net share testshare=$randomFolderPath /grant:everyone,FULL New-PSDrive -Name Foo -Root \\localhost\testshare -PSProvider FileSystem - set-content -path Foo:\$file1 -value "$file1" - $result = Get-Content -path Foo:\$file1 + Set-Content -Path Foo:\$file1 -Value "$file1" + $result = Get-Content -Path Foo:\$file1 $result| Should be "$file1" } finally diff --git a/test/powershell/engine/Basic/Encoding.Tests.ps1 b/test/powershell/engine/Basic/Encoding.Tests.ps1 new file mode 100644 index 00000000000..309f2c26af1 --- /dev/null +++ b/test/powershell/engine/Basic/Encoding.Tests.ps1 @@ -0,0 +1,73 @@ +Describe "File encoding tests" -Tag CI { + + Context "ParameterType for parameter 'Encoding' should be 'Encoding'" { + BeforeAll { + $testCases = Get-Command -Module Microsoft.PowerShell.* | + Where-Object { $_.Parameters -and $_.Parameters['Encoding'] } | + ForEach-Object { @{ Command = $_ } } + } + It "Encoding parameter of command '' is type 'Encoding'" -Testcase $testCases { + param ( $command ) + $command.Parameters['Encoding'].ParameterType.FullName | Should BeExactly "System.Text.Encoding" + } + } + Context "File contents are UTF8 without BOM" { + BeforeAll { + $testStr = "t" + ([char]233) + "st" + $nl = [environment]::newline + $utf8Bytes = 116,195,169,115,116 + $nlBytes = [byte[]][char[]]$nl + $ExpectedWithNewline = $( $utf8Bytes ; $nlBytes ) + $outputFile = "${TESTDRIVE}/file.txt" + $utf8Preamble = [text.encoding]::UTF8.GetPreamble() + $simpleTestCases = @( + # New-Item does not add CR/NL + @{ Command = "New-Item"; Parameters = @{ type = "file";value = $testStr; Path = $outputFile }; Expected = $utf8Bytes ; Operator = "be" } + # the following commands add a CR/NL + @{ Command = "Set-Content"; Parameters = @{ value = $testStr; Path = $outputFile }; Expected = $ExpectedWithNewline ; Operator = "be" } + @{ Command = "Add-Content"; Parameters = @{ value = $testStr; Path = $outputFile }; Expected = $ExpectedWithNewline ; Operator = "be" } + @{ Command = "Out-File"; Parameters = @{ InputObject = $testStr; Path = $outputFile }; Expected = $ExpectedWithNewline ; Operator = "be" } + # Redirection + @{ Command = { $testStr > $outputFile } ; Expected = $ExpectedWithNewline ; Operator = "be" } + ) + function Get-FileBytes ( $path ) { + [io.file]::ReadAllBytes($path) + } + } + + AfterEach { + if ( Test-Path $outputFile ) { + Remove-Item -Force $outputFile + } + } + + It " produces correct content ''" -Testcases $simpleTestCases { + param ( $Command, $parameters, $Expected, $Operator) + & $command @parameters + $bytes = Get-FileBytes $outputFile + $bytes -join "-" | should ${Operator} ($Expected -join "-") + } + + It "Export-CSV creates file with UTF-8 encoding without BOM" { + [pscustomobject]@{ Key = $testStr } | Export-Csv $outputFile + $bytes = Get-FileBytes $outputFile + $bytes[0,1,2] -join "-" | should not be ($utf8Preamble -join "-") + $bytes -join "-" | should match ($utf8bytes -join "-") + } + + It "Export-CliXml creates file with UTF-8 encoding without BOM" { + [pscustomobject]@{ Key = $testStr } | Export-Clixml $outputFile + $bytes = Get-FileBytes $outputFile + $bytes[0,1,2] -join "-" | should not be ($utf8Preamble -join "-") + $bytes -join "-" | should match ($utf8bytes -join "-") + } + + It "Appends correctly on non-Windows systems" -Skip:$IsWindows { + bash -c "echo '${testStr}' > $outputFile" + ${testStr} >> $outputFile + $bytes = Get-FileBytes $outputFile + $Expected = $( $ExpectedWithNewline; $ExpectedWithNewline ) + $bytes -join "-" | should be ($Expected -join "-") + } + } +} diff --git a/test/powershell/engine/Module/NewModuleManifest.Tests.ps1 b/test/powershell/engine/Module/NewModuleManifest.Tests.ps1 index 0604dc41553..8508db33c1d 100644 --- a/test/powershell/engine/Module/NewModuleManifest.Tests.ps1 +++ b/test/powershell/engine/Module/NewModuleManifest.Tests.ps1 @@ -34,7 +34,7 @@ Describe "New-ModuleManifest tests" -tags "CI" { function TestNewModuleManifestEncoding { param ([byte[]]$expected) New-ModuleManifest -Path $testModulePath - (Get-Content -Encoding Byte -Path $testModulePath -TotalCount $expected.Length) -join ',' | Should Be ($expected -join ',') + (Get-Content -AsByteStream -Path $testModulePath -TotalCount $expected.Length) -join ',' | Should Be ($expected -join ',') } It "Verify module manifest encoding" {