diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ccda2..31d68d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # CHANGELOG +## 12/13/2025 + +- **Native Zip Entry Objects** + + Zip entries returned by `Get-ZipEntry` (and created by `New-ZipEntry`) are now backed directly by `ICSharpCode.SharpZipLib.Zip.ZipEntry`. + This exposes additional useful properties on `ZipEntryBase` derived objects: + - `IsEncrypted` (`bool`) – Indicates whether the entry is encrypted. + - `AESKeySize` (`int`) – AES key size (0, 128, 192, or 256) if AES encryption is used. + - `CompressionMethod` (`ICSharpCode.SharpZipLib.Zip.CompressionMethod`) – The actual compression method used. + - `Comment` (`string`) – The entry comment. + - `Crc` (`long`) – Cyclic redundancy check. + +- **Support for Encrypted Zip Entries** + + `Get-ZipEntryContent` and `Expand-ZipEntry` now fully support reading and extracting password-protected entries. + A new common parameter has been added to both cmdlets: + + ```powershell + -Password + ``` + + - If an entry is encrypted and no password is provided, the cmdlets will securely prompt for one. + - Examples and detailed guidance for handling encrypted entries have been added to the help documentation. + +- **Documentation Improvements** + + All cmdlet help files have been reviewed and updated for consistency and clarity. + Significant enhancements to `Get-ZipEntryContent` and `Expand-ZipEntry` help: + - Added dedicated examples demonstrating password-protected entry handling. + - Updated parameter descriptions and notes for the new `-Password` parameter. + - Improved phrasing, removed outdated example output, and ensured uniform formatting across the module. + ## 07/02/2025 - Added `AssemblyLoadContext` support for PowerShell 7 (.NET 8.0 or later) to resolve DLL hell by isolating module dependencies. PowerShell 5.1 (.NET Framework) users can't get around this issue due to lack of `AssemblyLoadContext` in that runtime. diff --git a/README.md b/README.md index c87bab7..424036f 100644 --- a/README.md +++ b/README.md @@ -22,143 +22,38 @@ ## Cmdlets -### Zip Archive Cmdlets - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CmdletAliasDescription
Compress-ZipArchivezipcompressCompresses files and folders into a zip archive, overcoming built-in PowerShell limitations.
Expand-ZipEntryunzipentryExtracts individual zip entries to a destination directory.
Get-ZipEntryzipgeLists zip archive entries from paths or streams, serving as the entry point for zip cmdlets.
Get-ZipEntryContentzipgecRetrieves the content of zip entries as text or bytes.
New-ZipEntryzipneAdds new entries to a zip archive from files or paths.
Remove-ZipEntryziprmRemoves entries from one or more zip archives.
Rename-ZipEntryziprenRenames entries in one or more zip archives.
Set-ZipEntryContentzipscSets or appends content to a zip entry.
+### Zip Archive + +- [__`Compress-ZipArchive`__](docs/en-US/Compress-ZipArchive.md) — Compresses files and folders into a zip archive, overcoming built-in PowerShell limitations. +- [__`Expand-ZipEntry`__](docs/en-US/Expand-ZipEntry.md) — Extracts individual zip entries to a destination directory. +- [__`Get-ZipEntry`__](docs/en-US/Get-ZipEntry.md) — Lists zip archive entries from paths or streams, serving as the entry point for zip cmdlets. +- [__`Get-ZipEntryContent`__](docs/en-US/Get-ZipEntryContent.md) — Retrieves the content of zip entries as text or bytes. +- [__`New-ZipEntry`__](docs/en-US/New-ZipEntry.md) — Adds new entries to a zip archive from files or paths. +- [__`Remove-ZipEntry`__](docs/en-US/Remove-ZipEntry.md) — Removes entries from one or more zip archives. +- [__`Rename-ZipEntry`__](docs/en-US/Rename-ZipEntry.md) — Renames entries in one or more zip archives. +- [__`Set-ZipEntryContent`__](docs/en-US/Set-ZipEntryContent.md) — Sets or appends content to a zip entry. > [!NOTE] > Due to a .NET limitation, cmdlets like `New-ZipEntry`, `Compress-ZipArchive` with `-Update`, and `Set-ZipEntryContent` may fail when handling files or content > 2 GB __in existing zip archives__. As a workaround, recreate the zip archive or use tools like 7-Zip, which support larger files. See [issue #19](https://github.com/santisq/PSCompression/issues/19) for details. -### Tar Archive Cmdlets - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CmdletAliasDescription
Compress-TarArchivetarcompressCompresses files and folders into a tar archive with optional compression (gz, bz2, zst, lz, none).
Expand-TarArchiveuntarExtracts a tar archive with support for gz, bz2, zst, lz, and uncompressed formats.
Expand-TarEntryuntarentryExtracts individual tar entries to a destination directory.
Get-TarEntrytargeLists tar archive entries from paths or streams, serving as the entry point for tar cmdlets.
Get-TarEntryContenttargecRetrieves the content of tar entries as text or bytes.
- -### String Compression Cmdlets - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CmdletAliasDescription
ConvertFrom-BrotliStringfrombrotlistringDecompresses a Brotli-compressed string.
ConvertFrom-DeflateStringfromdeflatestringDecompresses a Deflate-compressed string.
ConvertFrom-GzipStringfromgzipstringDecompresses a Gzip-compressed string.
ConvertFrom-ZlibStringfromzlibstringDecompresses a Zlib-compressed string.
ConvertTo-BrotliStringtobrotlistringCompresses a string using Brotli.
ConvertTo-DeflateStringtodeflatestringCompresses a string using Deflate.
ConvertTo-GzipStringtogzipstringCompresses a string using Gzip.
ConvertTo-ZlibStringtozlibstringCompresses a string using Zlib.
+### Tar Archive + +- [__`Compress-TarArchive`__](docs/en-US/Compress-TarArchive.md) — Compresses files and folders into a tar archive with optional compression (gz, bz2, zst, lz, none). +- [__`Expand-TarArchive`__](docs/en-US/Expand-TarArchive.md) — Extracts a tar archive with support for gz, bz2, zst, lz, and uncompressed formats. +- [__`Expand-TarEntry`__](docs/en-US/Expand-TarEntry.md) — Extracts individual tar entries to a destination directory. +- [__`Get-TarEntry`__](docs/en-US/Get-TarEntry.md) — Lists tar archive entries from paths or streams. +- [__`Get-TarEntryContent`__](docs/en-US/Get-TarEntryContent.md) — Retrieves the content of tar entries as text or bytes. + +### String Compression + +- [__`ConvertFrom-BrotliString`__](docs/en-US/ConvertFrom-BrotliString.md) — Decompresses a Brotli-compressed string. +- [__`ConvertFrom-DeflateString`__](docs/en-US/ConvertFrom-DeflateString.md) — Decompresses a Deflate-compressed string. +- [__`ConvertFrom-GzipString`__](docs/en-US/ConvertFrom-GzipString.md) — Decompresses a Gzip-compressed string. +- [__`ConvertFrom-ZlibString`__](docs/en-US/ConvertFrom-ZlibString.md) — Decompresses a Zlib-compressed string. +- [__`ConvertTo-BrotliString`__](docs/en-US/ConvertTo-BrotliString.md) — Compresses a string using Brotli. +- [__`ConvertTo-DeflateString`__](docs/en-US/ConvertTo-DeflateString.md) — Compresses a string using Deflate. +- [__`ConvertTo-GzipString`__](docs/en-US/ConvertTo-GzipString.md) — Compresses a string using Gzip. +- [__`ConvertTo-ZlibString`__](docs/en-US/ConvertTo-ZlibString.md) — Compresses a string using Zlib. > [!NOTE] > The `Compress-GzipArchive` and `Expand-GzipArchive` cmdlets have been removed, as their single-file gzip functionality is now handled by `Compress-TarArchive` and `Expand-TarArchive`. For a workaround to compress or decompress single files using gzip, see [Example 2 in `ConvertTo-GzipString`](./docs/en-US/ConvertTo-GzipString.md#example-2-create-a-gzip-compressed-file-from-a-string). diff --git a/assets/helloworld.zip b/assets/helloworld.zip new file mode 100644 index 0000000..73bd236 Binary files /dev/null and b/assets/helloworld.zip differ diff --git a/docs/en-US/Compress-TarArchive.md b/docs/en-US/Compress-TarArchive.md index 893a01e..076c60b 100644 --- a/docs/en-US/Compress-TarArchive.md +++ b/docs/en-US/Compress-TarArchive.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -The `Compress-TarArchive` cmdlet creates a compressed tar archive file from one or more specified files or directories. It supports multiple compression algorithms and provides flexible file inclusion and exclusion options, similar to the `Compress-ZipArchive` cmdlet. +The `Compress-TarArchive` cmdlet creates a compressed tar archive file from one or more specified files or directories. It supports multiple compression algorithms and provides flexible file inclusion and exclusion options, similar to the `Compress-ZipArchive` cmdlet in this module. ## SYNTAX @@ -43,7 +43,7 @@ Compress-TarArchive ## DESCRIPTION -The `Compress-TarArchive` cmdlet creates a tar archive, optionally compressed with algorithms like gzip, bzip2, zstd, or lz4, in a PowerShell-native environment. It simplifies file and directory archiving by integrating seamlessly with PowerShell’s object-oriented pipeline, allowing flexible file selection through cmdlets like `Get-ChildItem` or `Get-Item`. With support for selective inclusion via `-Exclude`, customizable compression levels, and the ability to overwrite existing archives, it provides a convenient alternative to traditional tar utilities for PowerShell users, while preserving directory structures and metadata. +The `Compress-TarArchive` cmdlet creates a tar archive, optionally compressed with algorithms like gzip, bzip2, zstd, or lz4, in a PowerShell-native environment. It simplifies file and directory archiving by integrating seamlessly with PowerShell’s object-oriented pipeline, allowing flexible file selection through cmdlets like `Get-ChildItem` or `Get-Item`. With support for selective exclusion via `-Exclude`, customizable compression levels, and the ability to overwrite existing archives, it provides a convenient alternative to traditional tar utilities for PowerShell users, while preserving directory structures and metadata. ## EXAMPLES @@ -57,20 +57,20 @@ Get-ChildItem C:\Logs -Recurse -Filter *.log | This example demonstrates how to compress all `.log` files in the `C:\Logs` directory into a gzip-compressed tar archive named `logs.tar.gz` in the `C:\Archives` directory. > [!NOTE] -> If not specified, the cmdlet will use the gzip algorithm as default. +> If not specified, the cmdlet uses the gzip algorithm as default. ### Example 2: Compress a folder using `Fastest` Compression Level ```powershell -Compress-TarArchive -Path .\path -Destination myPath.tar.gz -CompressionLevel Fastest +Compress-TarArchive -Path . -Destination myPath.tar.gz -CompressionLevel Fastest ``` -This example shows how to compress the entire path directory into a gzip-compressed tar archive named `myPath.tar.gz` using the `Fastest` compression level for quicker processing. +This example shows how to compress the current directory (`.`) into a gzip-compressed tar archive named `myPath.tar.gz` using the `Fastest` compression level for quicker processing. -### Example 3: Replacing an existing Tar Archive +### Example 3: Overwrite an existing tar archive ```powershell -Compress-TarArchive -Path .\path -Destination dest.tar.gz -Force +Compress-TarArchive -Path .\Path -Destination dest.tar.gz -Force ``` This example illustrates how to create a new tar archive named `dest.tar.gz` from the path directory, overwriting any existing archive with the same name using the `-Force` parameter. @@ -78,16 +78,14 @@ This example illustrates how to create a new tar archive named `dest.tar.gz` fro ### Example 4: Exclude files and folders from source ```powershell -Compress-TarArchive -Path .\path -Destination myPath.tar.gz -Exclude *.xyz, *\test\* +Compress-TarArchive -Path .\Path -Destination myPath.tar.gz -Exclude *.xyz, *\test\* ``` -This example shows how to compress all items in `path` excluding all files having a `.xyz` extension and excluding -a folder with name `test` and all its child items. +This example shows how to compress all items in `path` excluding all files having a `.xyz` extension, any folder named `test` and all its child items. > [!TIP] > -> The `-Exclude` parameter supports [wildcard patterns](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.4&viewFallbackFrom=powershell-7.3), -exclusion patterns are tested against the items `.FullName` property. +> The `-Exclude` parameter supports [wildcard patterns](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards). Exclusion patterns are tested against each item's `.FullName` property. ### Example 5: Compress a directory using bzip2 algorithm @@ -113,14 +111,14 @@ Aliases: Accepted values: gz, bz2, zst, lz, none Required: False Position: Named -Default value: none +Default value: gz Accept pipeline input: False Accept wildcard characters: False ``` ### -CompressionLevel -Specifies the compression level for the selected algorithm, balancing speed and file size. See [`CompressionLevel` Enum](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel) for details. The default is algorithm-dependent but typically `Optimal`. +Specifies the compression level for the selected algorithm, balancing speed and file size. The default is algorithm-dependent but typically `Optimal`. ```yaml Type: CompressionLevel @@ -156,9 +154,6 @@ Accept wildcard characters: False Specifies an array of string patterns to exclude files or directories from the archive. Matching items are excluded based on their `.FullName` property. Wildcard characters are supported. -> [!NOTE] -> Patterns are tested against the object's `.FullName` property. - ```yaml Type: String[] Parameter Sets: (All) @@ -263,7 +258,7 @@ This cmdlet is designed to provide a PowerShell-native way to create tar archive ## RELATED LINKS -[__Compress-ZipArchive__](https://github.com/santisq/PSCompression) +[__`Compress-ZipArchive`__](https://github.com/santisq/PSCompression) [__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) diff --git a/docs/en-US/Compress-ZipArchive.md b/docs/en-US/Compress-ZipArchive.md index c6d05d7..d605265 100644 --- a/docs/en-US/Compress-ZipArchive.md +++ b/docs/en-US/Compress-ZipArchive.md @@ -1,5 +1,5 @@ --- -external help file: PSCompression-help.xml +external help file: PSCompression.dll-Help.xml Module Name: PSCompression online version: https://github.com/santisq/PSCompression schema: 2.0.0 @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -The `Compress-ZipArchive` cmdlet creates a compressed, or zipped, archive file from one or more specified files or directories. It aims to overcome a few limitations of [`Compress-Archive`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive?view=powershell-7.2) while keeping similar pipeline capabilities. +The `Compress-ZipArchive` cmdlet creates a compressed, or zipped, archive file from one or more specified files or directories. It aims to overcome a few limitations of [`Compress-Archive`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive) while keeping similar pipeline capabilities. ## SYNTAX @@ -43,7 +43,7 @@ Compress-ZipArchive ## DESCRIPTION -PowerShell cmdlet that overcomes the limitation that the built-in cmdlet `Compress-Archive` has: +This cmdlet overcomes several limitations of the built-in `Compress-Archive` cmdlet: > The `Compress-Archive` cmdlet uses the Microsoft .NET API [`System.IO.Compression.ZipArchive`](https://docs.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchive) to compress files. The maximum file size is 2 GB because there's a limitation of the underlying API. @@ -53,9 +53,9 @@ However, there are 3 limitations while using this method: 1. The source __must be a directory__, a single file cannot be compressed. 2. All files (recursively) on the source folder __will be compressed__, we can't pick / filter files to compress. - 3. It's not possible to __Update__ the entries of an existing Zip Archive. + 3. It is not possible to update the entries of an existing zip archive. -This cmdlet should be able to handle compression same as `ZipFile.CreateFromDirectory` Method but also allow filtering files and folders to compress while keeping the __file / folder structure intact__. +This cmdlet handles compression like the `ZipFile.CreateFromDirectory` method but also allows filtering of files and folders while preserving the file and folder structure. > [!NOTE] > When appending to an existing zip archive using the [`-Update` parameter](#-update), a .NET limitation may cause failures for files larger than 2 GB. To handle such files, recreate the archive or use tools like 7-Zip. See [issue #19](https://github.com/santisq/PSCompression/issues/19) for details. @@ -65,11 +65,11 @@ This cmdlet should be able to handle compression same as `ZipFile.CreateFromDire ### Example 1: Compress all `.ext` files from a specific folder ```powershell -Get-ChildItem .\path -Recurse -Filter *.ext | +Get-ChildItem .\Path -Recurse -Filter *.ext | Compress-ZipArchive -Destination dest.zip ``` -### Example 2: Compress all `.txt` files contained in all folders in the Current Directory +### Example 2: Compress all `.txt` files from subfolders in the current directory ```powershell Compress-ZipArchive .\*\*.txt -Destination dest.zip @@ -84,52 +84,51 @@ Compress-ZipArchive .\*.ext, .\*.ext2 -Destination dest.zip ### Example 4: Compress a folder using `Fastest` Compression Level ```powershell -Compress-ZipArchive .\path -Destination myPath.zip -CompressionLevel Fastest +Compress-ZipArchive .\Path -Destination myPath.zip -CompressionLevel Fastest ``` ### Example 5: Compressing all directories in `.\Path` ```powershell -Get-ChildItem .\path -Recurse -Directory | +Get-ChildItem .\Path -Recurse -Directory | Compress-ZipArchive -Destination dest.zip ``` ### Example 6: Replacing an existing Zip Archive -Demonstrates the use of `-Force` parameter switch. - ```powershell -Compress-ZipArchive -Path .\path -Destination dest.zip -Force +Compress-ZipArchive -Path .\Path -Destination dest.zip -Force ``` -### Example 7: Adding and updating new entries to an existing Zip Archive +Demonstrates the use of `-Force` parameter switch. This overwrites any existing archive at the destination path. -Demonstrates the use of `-Update` parameter switch. +### Example 7: Adding and updating new entries to an existing Zip Archive ```powershell -Get-ChildItem .\path -Recurse -Directory | +Get-ChildItem .\Path -Recurse -Directory | Compress-ZipArchive -Destination dest.zip -Update ``` +Demonstrates the use of `-Update` parameter switch. This adds the directories to an existing archive or updates them if they already exist. + ### Example 8: Exclude files and folders from source ```powershell -Compress-ZipArchive .\path -Destination myPath.zip -Exclude *.xyz, *\test\* +Compress-ZipArchive .\Path -Destination myPath.zip -Exclude *.xyz, *\test\* ``` -This example shows how to compress all items in `path` excluding all files having a `.xyz` extension and excluding -a folder with name `test` and all its child items. +This example shows how to compress all items in `Path` excluding all files having a `.xyz` extension and excluding +a folder named `test` and all its child items. > [!TIP] > -> The `-Exclude` parameter supports [wildcard patterns](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.4&viewFallbackFrom=powershell-7.3), -exclusion patterns are tested against the items `.FullName` property. +> The `-Exclude` parameter supports [wildcard patterns](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards). Exclusion patterns are tested against each item's `.FullName` property. ## PARAMETERS ### -Path -Specifies the path or paths to the files or directories to include in the archive file. To specify multiple paths and include files from multiple locations, use commas to separate the paths. This parameter accepts wildcard characters, allowing you to include all files in a directory or match specific patterns. +Specifies the path and file name of the output zip archive. To specify multiple paths and include files from multiple locations, use commas to separate the paths. This parameter accepts wildcard characters, allowing you to include all files in a directory or match specific patterns. > [!TIP] > Using wildcards with a root directory affects the archive's contents: @@ -226,7 +225,8 @@ Accept wildcard characters: True Updates the specified archive by replacing older file versions in the archive with newer file versions that have the same names. You can also use this parameter to add files to an existing archive. > [!NOTE] -> If `-Force` and `-Update` are used together this cmdlet will add or update entries. +> +> When used with `-Force`, the cmdlet adds new entries or updates existing ones instead of overwriting the entire archive. ```yaml Type: SwitchParameter @@ -242,14 +242,11 @@ Accept wildcard characters: False ### -Force -Overwrites the destination archive if exists otherwise it creates a new one. All existing entries are lost. - -> [!NOTE] -> If `-Force` and `-Update` are used together this cmdlet will add or update entries. +Overwrites the destination archive if it exists; otherwise, creates a new one. All existing entries are lost. ```yaml Type: SwitchParameter -Parameter Sets: PathWithForce, LiteralPathWithForce +Parameter Sets: (All) Aliases: Required: True @@ -261,7 +258,7 @@ Accept wildcard characters: False ### -PassThru -Outputs the object representing the compressed file. The cmdlet produces no output by default. +Returns a `System.IO.FileInfo` object representing the created or updated zip archive. ```yaml Type: SwitchParameter @@ -293,11 +290,11 @@ By default, this cmdlet produces no output. ### System.IO.FileInfo -When the `-PassThru` switch is used this cmdlet outputs the `FileInfo` instance representing the compressed file. +When the `-PassThru` switch is used, this cmdlet outputs a `System.IO.FileInfo` object representing the created or updated archive. ## NOTES -This cmdlet was initially posted to address [this Stack Overflow question](https://stackoverflow.com/a/72611161/15339544). [Another question](https://stackoverflow.com/q/74129754/15339544) in the same site pointed out another limitation with the native cmdlet, it can't compress if another process has a handle on a file. To overcome this issue, and also to emulate explorer's behavior when compressing files used by another process, the cmdlet defaults to __[`FileShare 'ReadWrite, Delete'`](https://learn.microsoft.com/en-us/dotnet/api/system.io.fileshare?view=net-6.0)__ when opening a [`FileStream`](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.open?view=net-7.0). +This cmdlet was initially posted to address [this Stack Overflow question](https://stackoverflow.com/a/72611161/15339544). [Another question](https://stackoverflow.com/q/74129754/15339544) in the same site pointed out another limitation with the native cmdlet, it can't compress if another process has a handle on a file. To overcome this issue, and also to emulate explorer's behavior when compressing files used by another process, the cmdlet defaults to __[FileShare `ReadWrite, Delete`](https://learn.microsoft.com/en-us/dotnet/api/system.io.fileshare)__ when opening a [`FileStream`](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.open). ## RELATED LINKS @@ -306,3 +303,5 @@ This cmdlet was initially posted to address [this Stack Overflow question](https [__ZipArchive Class__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchive) [__ZipArchiveEntry Class__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchiveentry) + +[__`Compress-Archive`__](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive) diff --git a/docs/en-US/ConvertFrom-BrotliString.md b/docs/en-US/ConvertFrom-BrotliString.md index 189034b..c8cdac8 100644 --- a/docs/en-US/ConvertFrom-BrotliString.md +++ b/docs/en-US/ConvertFrom-BrotliString.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Expands Brotli Base64 compressed input strings. +Decompresses Brotli-compressed Base64-encoded strings. ## SYNTAX @@ -23,11 +23,11 @@ ConvertFrom-BrotliString ## DESCRIPTION -The `ConvertFrom-BrotliString` cmdlet expands Base64 encoded Brotli compressed strings using the `BrotliStream` class from the `BrotliSharpLib` library. This cmdlet is the counterpart of [`ConvertTo-BrotliString`](./ConvertTo-BrotliString.md). +The `ConvertFrom-BrotliString` cmdlet decompresses Base64-encoded strings that were compressed using Brotli compression (via the `BrotliStream` class from the `BrotliSharpLib` library). It is the counterpart to [`ConvertTo-BrotliString`](./ConvertTo-BrotliString.md). ## EXAMPLES -### Example 1: Expanding a Brotli compressed string +### Example 1: Decompress a Brotli-compressed Base64 string ```powershell PS ..\pwsh> ConvertFrom-BrotliString CwiAaGVsbG8NCndvcmxkDQohDQoD @@ -37,36 +37,32 @@ world ! ``` -This example expands a Brotli Base64 encoded string back to its original strings. +This example decompresses a Brotli-compressed Base64 string, restoring the original multi-line text. -### Example 2: Demonstrates how `-Raw` works +### Example 2: Compare default behavior with the `-Raw` switch ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' - -# New lines are preserved when the cmdlet receives an array of strings. -PS ..\pwsh> $strings | ConvertTo-BrotliString | ConvertFrom-BrotliString +PS ..\pwsh> $compressed = $strings | ConvertTo-BrotliString +PS ..\pwsh> $decompressed = $compressed | ConvertFrom-BrotliString -Raw +PS ..\pwsh> $decompressed.GetType() # System.String +PS ..\pwsh> $decompressed hello world ! - -# When using the `-Raw` switch, all strings are returned as a single string -PS ..\pwsh> $strings | ConvertTo-BrotliString -NoNewLine | ConvertFrom-BrotliString -Raw - -helloworld! ``` -This example shows how the `-Raw` switch concatenates the expanded strings into a single string with newlines preserved. +This example compares the default behavior (outputting an array of strings split on newlines) with the `-Raw` switch (returning a single string with newlines preserved). ## PARAMETERS ### -Encoding -Determines the character encoding used when expanding the input strings. +Specifies the text encoding to use for the decompressed output string(s). > [!NOTE] -> The default encoding is `utf8NoBOM`. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -75,7 +71,7 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` @@ -98,7 +94,7 @@ Accept wildcard characters: False ### -Raw -Outputs the expanded string as a single string with newlines preserved. By default, newline characters in the expanded string are used as delimiters to separate the input into an array of strings. +By default, the cmdlet splits the decompressed text on newline characters and outputs an array of strings. The `-Raw` switch returns the entire decompressed text as a single string (with newlines preserved). ```yaml Type: SwitchParameter @@ -126,13 +122,13 @@ You can pipe Brotli Base64 strings to this cmdlet. ### System.String -By default, this cmdlet streams strings. When the `-Raw` switch is used, it returns a single multi-line string. +By default: `System.String[]` (one element per line). With `-Raw`: `System.String` (single multi-line string). ## NOTES ## RELATED LINKS -[__ConvertTo-BrotliString__](https://github.com/santisq/PSCompression/) +[__`ConvertTo-BrotliString`__](./ConvertTo-BrotliString.md) [__BrotliSharpLib__](https://github.com/master131/BrotliSharpLib) diff --git a/docs/en-US/ConvertFrom-DeflateString.md b/docs/en-US/ConvertFrom-DeflateString.md index 46e4a95..5a9e9b9 100644 --- a/docs/en-US/ConvertFrom-DeflateString.md +++ b/docs/en-US/ConvertFrom-DeflateString.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Expands Deflate Base64 compressed input strings. +Decompresses Deflate-compressed Base64-encoded strings. ## SYNTAX @@ -23,11 +23,11 @@ ConvertFrom-DeflateString ## DESCRIPTION -The `ConvertFrom-DeflateString` cmdlet expands Base64 encoded Deflate compressed strings using the [`DeflateStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). This cmdlet is the counterpart of [`ConvertTo-DeflateString`](./ConvertTo-DeflateString.md). +The ConvertFrom-DeflateString cmdlet decompresses Base64-encoded strings that were compressed using Deflate compression (via the [`DeflateStream` class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream)). It is the counterpart to [`ConvertTo-DeflateString`](./ConvertTo-DeflateString.md). ## EXAMPLES -### Example 1: Expanding a Deflate compressed string +### Example 1: Decompress a Deflate-compressed Base64 string ```powershell PS ..\pwsh> ConvertFrom-DeflateString ykjNycnn5SrPL8pJ4eVS5OUCAAAA//8DAA== @@ -37,36 +37,32 @@ world ! ``` -This example expands a Deflate Base64 encoded string back to its original strings. +This example decompresses a Deflate-compressed Base64 string, restoring the original multi-line text. -### Example 2: Demonstrates how `-Raw` works +### Example 2: Compare default behavior with the `-Raw` switch ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' - -# New lines are preserved when the cmdlet receives an array of strings. -PS ..\pwsh> $strings | ConvertTo-DeflateString | ConvertFrom-DeflateString +PS ..\pwsh> $compressed = $strings | ConvertTo-DeflateString +PS ..\pwsh> $decompressed = $compressed | ConvertFrom-DeflateString -Raw +PS ..\pwsh> $decompressed.GetType() # System.String +PS ..\pwsh> $decompressed hello world ! - -# When using the `-Raw` switch, all strings are returned as a single string -PS ..\pwsh> $strings | ConvertTo-DeflateString -NoNewLine | ConvertFrom-DeflateString -Raw - -helloworld! ``` -This example shows how the `-Raw` switch concatenates the expanded strings into a single string with newlines preserved. +This example compares the default behavior (outputting an array of strings split on newlines) with the `-Raw` switch (returning a single string with newlines preserved). ## PARAMETERS ### -Encoding -Determines the character encoding used when expanding the input strings. +Specifies the text encoding to use for the decompressed output string(s). > [!NOTE] -> The default encoding is `utf8NoBOM`. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -75,7 +71,7 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` @@ -98,7 +94,7 @@ Accept wildcard characters: False ### -Raw -Outputs the expanded string as a single string with newlines preserved. By default, newline characters in the expanded string are used as delimiters to separate the input into an array of strings. +By default, the cmdlet splits the decompressed text on newline characters and outputs an array of strings. The `-Raw` switch returns the entire decompressed text as a single string (with newlines preserved). ```yaml Type: SwitchParameter @@ -120,20 +116,20 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### System.String[] -You can pipe Deflate Base64 strings to this cmdlet. +You can pipe Deflate-compressed Base64 strings to this cmdlet. ## OUTPUTS ### System.String -By default, this cmdlet streams strings. When the `-Raw` switch is used, it returns a single multi-line string. +By default: `System.String[]` (one element per line). With `-Raw`: `System.String` (single multi-line string). ## NOTES ## RELATED LINKS -[__ConvertTo-DeflateString__](https://github.com/santisq/PSCompression) +[__`ConvertTo-DeflateString`__](./ConvertTo-DeflateString.md) -[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression?view=net-6.0) +[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) [__DeflateStream Class__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream) diff --git a/docs/en-US/ConvertFrom-GzipString.md b/docs/en-US/ConvertFrom-GzipString.md index 45f192d..492d4b5 100644 --- a/docs/en-US/ConvertFrom-GzipString.md +++ b/docs/en-US/ConvertFrom-GzipString.md @@ -1,5 +1,5 @@ --- -external help file: PSCompression-help.xml +external help file: PSCompression.dll-Help.xml Module Name: PSCompression online version: https://github.com/santisq/PSCompression schema: 2.0.0 @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Expands Gzip Base64 compressed input strings. +Decompresses Gzip-compressed Base64-encoded strings. ## SYNTAX @@ -23,11 +23,11 @@ ConvertFrom-GzipString ## DESCRIPTION -The `ConvertFrom-GzipString` cmdlet can expand Base64 encoded Gzip compressed strings using the [`GzipStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.gzipstream). This cmdlet is the counterpart of [`ConvertTo-GzipString`](ConvertTo-GzipString.md). +The `ConvertFrom-GzipString` cmdlet decompresses Base64-encoded strings that were compressed using GZip compression (via the [`GZipStream` class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.gzipstream)). It is the counterpart to [`ConvertTo-GzipString`](./ConvertTo-GzipString.md). ## EXAMPLES -### Example 1: Expanding a Gzip compressed string +### Example 1: Decompress a GZip-compressed Base64 string ```powershell PS ..\pwsh> ConvertFrom-GzipString H4sIAAAAAAAACstIzcnJ5+Uqzy/KSeHlUuTlAgBLr/K2EQAAAA== @@ -37,34 +37,32 @@ world ! ``` -### Example 2: Demonstrates how `-NoNewLine` works +This example decompresses a GZip-compressed Base64 string, restoring the original multi-line text. + +### Example 2: Compare default behavior with the `-Raw` switch ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' - -# New lines are preserved when the cmdlet receives an array of strings. -PS ..\pwsh> $strings | ConvertTo-GzipString | ConvertFrom-GzipString +PS ..\pwsh> $compressed = $strings | ConvertTo-GzipString +PS ..\pwsh> $decompressed = $compressed | ConvertFrom-GzipString -Raw +PS ..\pwsh> $decompressed.GetType() # System.String +PS ..\pwsh> $decompressed hello world ! - -# When using the `-NoNewLine` switch, all strings are concatenated -PS ..\pwsh> $strings | ConvertTo-GzipString -NoNewLine | ConvertFrom-GzipString - -helloworld! ``` -This example shows how the `-Raw` switch concatenates the expanded strings into a single string with newlines preserved. +This example compares the default behavior (outputting an array of strings split on newlines) with the `-Raw` switch (returning a single string with newlines preserved). ## PARAMETERS ### -Encoding -Determines the character encoding used when expanding the input strings. +Specifies the text encoding to use for the decompressed output string(s). > [!NOTE] -> The default encoding is __`utf8NoBOM`__. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -73,14 +71,14 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` ### -InputObject -Specifies the input string or strings to expand. +Specifies the GZip-compressed Base64 string(s) to decompress. ```yaml Type: String[] @@ -96,8 +94,7 @@ Accept wildcard characters: False ### -Raw -Outputs the expanded string as a single string with newlines preserved. -By default, newline characters in the expanded string are used as delimiters to separate the input into an array of strings. +By default, the cmdlet splits the decompressed text on newline characters and outputs an array of strings. The `-Raw` switch returns the entire decompressed text as a single string (with newlines preserved). ```yaml Type: SwitchParameter @@ -117,21 +114,21 @@ This cmdlet supports the common parameters. For more information, see [about_Com ## INPUTS -### System.String +### System.String[] -You can pipe Gzip Base64 strings to this cmdlet. +You can pipe GZip-compressed Base64 strings to this cmdlet. ## OUTPUTS ### System.String -By default, this cmdlet streams strings. When the `-Raw` switch is used, it returns a single multi-line string. +By default: `System.String[]` (one element per line). With `-Raw`: `System.String` (single multi-line string). ## NOTES ## RELATED LINKS -[__ConvertTo-GzipString__](https://github.com/santisq/PSCompression) +[__`ConvertTo-GzipString`__](./ConvertTo-GzipString.md) [__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) diff --git a/docs/en-US/ConvertFrom-ZLibString.md b/docs/en-US/ConvertFrom-ZLibString.md index 6231024..78184eb 100644 --- a/docs/en-US/ConvertFrom-ZLibString.md +++ b/docs/en-US/ConvertFrom-ZLibString.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Expands ZLib Base64 compressed input strings. +Decompresses ZLib-compressed Base64-encoded strings. ## SYNTAX @@ -23,11 +23,12 @@ ConvertFrom-ZLibString ## DESCRIPTION -The `ConvertFrom-ZLibString` cmdlet expands Base64 encoded ZLib compressed strings using a custom Zlib implementation built on the [`DeflateStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). For the implementation details, see the PSCompression source code. This cmdlet is the counterpart of [`ConvertTo-ZLibString`](./ConvertTo-ZLibString.md). +The `ConvertFrom-ZLibString` cmdlet decompresses Base64-encoded strings that were compressed using ZLib compression. It uses a custom ZLib implementation built on top of the [`DeflateStream` class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). For implementation details, see the PSCompression source code. +This cmdlet is the counterpart of [`ConvertTo-ZLibString`](./ConvertTo-ZLibString.md)." ## EXAMPLES -### Example 1: Expanding a ZLib compressed string +### Example 1: Decompress a ZLib-compressed Base64 string ```powershell PS ..\pwsh> ConvertFrom-ZLibString eJzKSM3JyeflKs8vyknh5VLk5QIAAAD//wMAMosEow== @@ -37,36 +38,32 @@ world ! ``` -This example expands a ZLib Base64 encoded string back to its original strings. +This example decompresses a ZLib-compressed Base64 string, restoring the original multi-line text. -### Example 2: Demonstrates how `-Raw` works +### Example 2: Compare default behavior with the `-Raw` switch ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' - -# New lines are preserved when the cmdlet receives an array of strings. -PS ..\pwsh> $strings | ConvertTo-ZLibString | ConvertFrom-ZLibString +PS ..\pwsh> $compressed = $strings | ConvertTo-ZlibString +PS ..\pwsh> $decompressed = $compressed | ConvertFrom-ZlibString -Raw +PS ..\pwsh> $decompressed.GetType() # System.String +PS ..\pwsh> $decompressed hello world ! - -# When using the `-Raw` switch, all strings are returned as a single string -PS ..\pwsh> $strings | ConvertTo-ZLibString -NoNewLine | ConvertFrom-ZLibString -Raw - -helloworld! ``` -This example shows how the `-Raw` switch concatenates the expanded strings into a single string with newlines preserved. +This example compares the default behavior (outputting an array of strings split on newlines) with the `-Raw` switch (returning a single string with newlines preserved). ## PARAMETERS ### -Encoding -Determines the character encoding used when expanding the input strings. +Specifies the text encoding to use for the decompressed output string(s). > [!NOTE] -> The default encoding is `utf8NoBOM`. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -75,14 +72,14 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` ### -InputObject -Specifies the input string or strings to expand. +Specifies the ZLib-compressed Base64 string(s) to decompress. ```yaml Type: String[] @@ -98,7 +95,7 @@ Accept wildcard characters: False ### -Raw -Outputs the expanded string as a single string with newlines preserved. By default, newline characters in the expanded string are used as delimiters to separate the input into an array of strings. +By default, the cmdlet splits the decompressed text on newline characters and outputs an array of strings. The `-Raw` switch returns the entire decompressed text as a single string (with newlines preserved). ```yaml Type: SwitchParameter @@ -120,19 +117,19 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### System.String[] -You can pipe ZLib Base64 strings to this cmdlet. +You can pipe ZLib-compressed Base64 strings to this cmdlet. ## OUTPUTS ### System.String -By default, this cmdlet streams strings. When the `-Raw` switch is used, it returns a single multi-line string. +By default: `System.String[]` (one element per line). With `-Raw`: `System.String` (single multi-line string). ## NOTES ## RELATED LINKS -[__ConvertTo-ZLibString__](https://github.com/santisq/PSCompression) +[__`ConvertTo-ZLibString`__](./ConvertTo-ZLibString.md) [__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) diff --git a/docs/en-US/ConvertTo-BrotliString.md b/docs/en-US/ConvertTo-BrotliString.md index cf5991a..b7a6692 100644 --- a/docs/en-US/ConvertTo-BrotliString.md +++ b/docs/en-US/ConvertTo-BrotliString.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Creates a Brotli Base64 compressed string from a specified input string or strings. +Compresses input strings into a Brotli-compressed Base64-encoded string. ## SYNTAX @@ -25,11 +25,11 @@ ConvertTo-BrotliString ## DESCRIPTION -The `ConvertTo-BrotliString` cmdlet compresses input strings into Brotli Base64 encoded strings or raw bytes using the `BrotliStream` class from the `BrotliSharpLib` library. For expansion of Base64 Brotli strings, see [`ConvertFrom-BrotliString`](./ConvertFrom-BrotliString.md). +The `ConvertTo-BrotliString` cmdlet compresses input strings into Brotli-compressed Base64-encoded strings or raw bytes using the `BrotliStream` class from the `BrotliSharpLib` library. It is the counterpart to [`ConvertFrom-BrotliString`](./ConvertFrom-BrotliString.md). ## EXAMPLES -### Example 1: Compress strings to Brotli compressed Base64 encoded string +### Example 1: Compress strings into a Brotli-compressed Base64 string ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' @@ -43,9 +43,9 @@ PS ..\pwsh> $strings | ConvertTo-BrotliString CwiAaGVsbG8NCndvcmxkDQohDQoD ``` -This example demonstrates compressing an array of strings into a single Brotli Base64 encoded string using either positional binding or pipeline input. +This example shows how to compress an array of strings into a single Brotli-compressed Base64 string, using either argument or pipeline input. -### Example 2: Create a Brotli compressed file from a string +### Example 2: Save Brotli-compressed bytes to a file using `-AsByteStream` ```powershell PS ..\pwsh> 'hello world!' | ConvertTo-BrotliString -AsByteStream | Set-Content -FilePath .\helloworld.br -AsByteStream @@ -57,7 +57,7 @@ PS ..\pwsh> [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($pat hello world! ``` -Demonstrates how `-AsByteStream` outputs a byte array that can be saved to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. +This example shows how to use `-AsByteStream` to output raw compressed bytes that can be written to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. > [!NOTE] > The example uses `-AsByteStream` with `Set-Content`, which is available in PowerShell 7+. In Windows PowerShell 5.1, use `-Encoding Byte` with `Set-Content` or `Out-File` to write the byte array to a file. @@ -74,7 +74,7 @@ PS ..\pwsh> 'ñ' | ConvertTo-BrotliString -Encoding utf8BOM | ConvertFrom-Brotli This example shows how different encodings affect the compression and decompression of special characters. The default encoding is `utf8NoBOM`. -### Example 4: Compressing multiple files into one Brotli Base64 string +### Example 4: Compress the contents of multiple files into a single Brotli Base64 string ```powershell # Check the total length of the files @@ -86,7 +86,7 @@ PS ..\pwsh> (Get-Content myLogs\*.txt | ConvertTo-BrotliString).Length / 1kb 35.123456789 ``` -This example demonstrates compressing the contents of multiple text files into a single Brotli Base64 string and compares the total length before and after compression. +This example demonstrates compressing the contents of multiple text files into a single Brotli-compressed Base64 string and compares the total length before and after compression. ## PARAMETERS @@ -131,7 +131,7 @@ Accept wildcard characters: False Determines the character encoding used when compressing the input strings. > [!NOTE] -> The default encoding is `utf8NoBOM`. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -140,7 +140,7 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` @@ -185,13 +185,13 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### System.String[] -You can pipe strings to this cmdlet. +You can pipe one or more strings to this cmdlet. ## OUTPUTS ### System.String -By default, this cmdlet outputs a single Base64 encoded string. +By default, this cmdlet outputs a single Base64-encoded string. ### System.Byte[] @@ -201,8 +201,8 @@ When the `-AsByteStream` switch is used, this cmdlet outputs a byte array down t ## RELATED LINKS -[__ConvertFrom-BrotliString__](https://github.com/santisq/PSCompression) +[__`ConvertFrom-BrotliString`__](./ConvertFrom-BrotliString.md) [__BrotliSharpLib__](https://github.com/master131/BrotliSharpLib) -[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression?view=net-6.0) +[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) diff --git a/docs/en-US/ConvertTo-DeflateString.md b/docs/en-US/ConvertTo-DeflateString.md index 3d962ec..0c29005 100644 --- a/docs/en-US/ConvertTo-DeflateString.md +++ b/docs/en-US/ConvertTo-DeflateString.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Creates a Deflate Base64 compressed string from a specified input string or strings. +Compresses input strings into a Deflate-compressed Base64-encoded string. ## SYNTAX @@ -25,7 +25,7 @@ ConvertTo-DeflateString ## DESCRIPTION -The `ConvertTo-DeflateString` cmdlet compresses input strings into Deflate Base64 encoded strings or raw bytes using the [`DeflateStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). For expansion of Base64 Deflate strings, see [`ConvertFrom-DeflateString`](./ConvertFrom-DeflateString.md). +The `ConvertTo-DeflateString` cmdlet compresses input strings into Deflate-compressed Base64-encoded strings or raw bytes using the [`DeflateStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). It is the counterpart to [`ConvertFrom-DeflateString`](./ConvertFrom-DeflateString.md). ## EXAMPLES @@ -43,21 +43,21 @@ PS ..\pwsh> $strings | ConvertTo-DeflateString ykjNycnn5SrPL8pJ4eVS5OUCAAAA//8DAA== ``` -This example demonstrates compressing an array of strings into a single Deflate Base64 encoded string using either positional binding or pipeline input. +This example shows how to compress an array of strings into a single Deflate-compressed Base64 string, using either argument or pipeline input. -### Example 2: Create a Deflate compressed file from a string +### Example 2: Save Deflate-compressed bytes to a file using `-AsByteStream` ```powershell PS ..\pwsh> 'hello world!' | ConvertTo-DeflateString -AsByteStream | Set-Content -FilePath .\helloworld.deflate -AsByteStream -# To read the file back you can use `ConvertFrom-BrotliString` following these steps: +# To read the file back you can use `ConvertFrom-DeflateString` following these steps: PS ..\pwsh> $path = Convert-Path .\helloworld.deflate PS ..\pwsh> [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($path)) | ConvertFrom-DeflateString hello world! ``` -Demonstrates how `-AsByteStream` outputs a byte array that can be saved to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. +This example shows how to use `-AsByteStream` to output raw compressed bytes that can be written to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. > [!NOTE] > The example uses `-AsByteStream` with `Set-Content`, which is available in PowerShell 7+. In Windows PowerShell 5.1, use `-Encoding Byte` with `Set-Content` or `Out-File` to write the byte array to a file. @@ -74,7 +74,7 @@ PS ..\pwsh> 'ñ' | ConvertTo-DeflateString -Encoding utf8BOM | ConvertFrom-Defla This example shows how different encodings affect the compression and decompression of special characters. The default encoding is `utf8NoBOM`. -### Example 4: Compressing multiple files into one Deflate Base64 string +### Example 4: Compress the contents of multiple files into a single Deflate Base64 string ```powershell # Check the total length of the files @@ -86,7 +86,7 @@ PS ..\pwsh> (Get-Content myLogs\*.txt | ConvertTo-DeflateString).Length / 1kb 35.123456789 ``` -This example demonstrates compressing the contents of multiple text files into a single Deflate Base64 string and compares the total length before and after compression. +This example demonstrates compressing the contents of multiple text files into a single Deflate-compressed Base64 string and compares the total length before and after compression. ## PARAMETERS @@ -131,7 +131,7 @@ Accept wildcard characters: False Determines the character encoding used when compressing the input strings. > [!NOTE] -> The default encoding is `utf8NoBOM`. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -140,7 +140,7 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` @@ -185,13 +185,13 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### System.String[] -You can pipe strings to this cmdlet. +You can pipe one or more strings to this cmdlet. ## OUTPUTS ### System.String -By default, this cmdlet outputs a single Base64 encoded string. +By default, this cmdlet outputs a single Base64-encoded string. ### System.Byte[] @@ -201,8 +201,8 @@ When the `-AsByteStream` switch is used, this cmdlet outputs a byte array down t ## RELATED LINKS -[__ConvertFrom-DeflateString__](https://github.com/santisq/PSCompression) +[__`ConvertFrom-DeflateString`__](./ConvertFrom-DeflateString.md) -[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression?view=net-6.0) +[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) [__DeflateStream Class__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream) diff --git a/docs/en-US/ConvertTo-GzipString.md b/docs/en-US/ConvertTo-GzipString.md index 6f52368..37bc9f1 100644 --- a/docs/en-US/ConvertTo-GzipString.md +++ b/docs/en-US/ConvertTo-GzipString.md @@ -1,5 +1,5 @@ --- -external help file: PSCompression-help.xml +external help file: PSCompression.dll-Help.xml Module Name: PSCompression online version: https://github.com/santisq/PSCompression schema: 2.0.0 @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Creates a Gzip Base64 compressed string from a specified input string or strings. +Compresses input strings into a Gzip-compressed Base64-encoded string. ## SYNTAX @@ -25,11 +25,11 @@ ConvertTo-GzipString ## DESCRIPTION -The `ConvertTo-GzipString` cmdlet can compress input strings into Gzip Base64 encoded strings or raw bytes using the [`GzipStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.gzipstream). For expansion of Base64 Gzip strings, see [`ConvertFrom-GzipString`](ConvertFrom-GzipString.md). +The `ConvertTo-GzipString` cmdlet compresses input strings into Gzip-compressed Base64-encoded strings or raw bytes using the [`GzipStream` class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.gzipstream). It is the counterpart to [`ConvertFrom-GzipString`](ConvertFrom-GzipString.md). ## EXAMPLES -### Example 1: Compress strings to Gzip compressed Base64 encoded string +### Example 1: Compress strings into a GZip-compressed Base64 string ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' @@ -45,21 +45,21 @@ PS ..\pwsh> $strings | ConvertTo-GzipString H4sIAAAAAAAEAMtIzcnJ5+Uqzy/KSeHlUuTlAgBLr/K2EQAAAA== ``` -This example demonstrates compressing an array of strings into a single Brotli Base64 encoded string using either positional binding or pipeline input. +This example shows how to compress an array of strings into a single Gzip-compressed Base64 string, using either argument or pipeline input. -### Example 2: Create a Gzip compressed file from a string +### Example 2: Save Gzip-compressed bytes to a file using `-AsByteStream` ```powershell PS ..\pwsh> 'hello world!' | ConvertTo-GzipString -AsByteStream | Set-Content -FilePath .\helloworld.gz -AsByteStream -# To read the file back you can use `ConvertFrom-BrotliString` following these steps: +# To read the file back you can use `ConvertFrom-GzipString` following these steps: PS ..\pwsh> $path = Convert-Path .\helloworld.gz PS ..\pwsh> [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($path)) | ConvertFrom-GzipString hello world! ``` -Demonstrates how `-AsByteStream` outputs a byte array that can be saved to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. +This example shows how to use `-AsByteStream` to output raw compressed bytes that can be written to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. > [!NOTE] > The example uses `-AsByteStream` with `Set-Content`, which is available in PowerShell 7+. In Windows PowerShell 5.1, use `-Encoding Byte` with `Set-Content` or `Out-File` to write the byte array to a file. @@ -74,9 +74,9 @@ PS ..\pwsh> 'ñ' | ConvertTo-GzipString -Encoding utf8BOM | ConvertFrom-GzipStri ñ ``` -The default Encoding is `utf8NoBom`. +This example shows how different encodings affect the compression and decompression of special characters. The default encoding is `utf8NoBOM`. -### Example 4: Compressing multiple files into one Gzip Base64 string +### Example 4: Compress the contents of multiple files into a single GZip-compressed Base64 string ```powershell # Check the total length of the files @@ -88,7 +88,7 @@ PS ..\pwsh> (Get-Content myLogs\*.txt | ConvertTo-GzipString).Length / 1kb 35.123456789 ``` -This example demonstrates compressing the contents of multiple text files into a single Gzip Base64 string and compares the total length before and after compression. +This example demonstrates compressing the contents of multiple text files into a single GZip-compressed Base64 string and compares the total length before and after compression. ## PARAMETERS @@ -113,8 +113,7 @@ Accept wildcard characters: False ### -CompressionLevel -Define the compression level that should be used. -__See [`CompressionLevel` Enum](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel) for details__. +Specifies the compression level for the GZip algorithm, balancing speed and compression size. See [`CompressionLevel` Enum](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel) for details. ```yaml Type: CompressionLevel @@ -134,7 +133,7 @@ Accept wildcard characters: False Determines the character encoding used when compressing the input strings. > [!NOTE] -> The default encoding is __`utf8NoBOM`__. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -143,7 +142,7 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` @@ -166,8 +165,7 @@ Accept wildcard characters: False ### -NoNewLine -The encoded string representation of the input objects are concatenated to form the output. -No new line character is added after each output string when this switch is used. +The encoded string representations of the input objects are concatenated to form the output. No newline character is added after each input string when this switch is used. ```yaml Type: SwitchParameter @@ -189,23 +187,23 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### String -You can pipe strings to this cmdlet. +You can pipe one or more strings to this cmdlet. ## OUTPUTS -### String +### System.String -By default, this cmdlet outputs a single string. +By default, this cmdlet outputs a single Base64-encoded string. -### Byte[] +### System.Byte[] -When the `-AsByteStream` switch is used this cmdlet outputs a byte array down the pipeline. +When the `-AsByteStream` switch is used, this cmdlet outputs a byte array down the pipeline. ## NOTES ## RELATED LINKS -[__ConvertFrom-GzipString__](https://github.com/santisq/PSCompression) +[__`ConvertFrom-GzipString`__](./ConvertFrom-GzipString.md) [__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) diff --git a/docs/en-US/ConvertTo-ZLibString.md b/docs/en-US/ConvertTo-ZLibString.md index 0ad57e9..e68665a 100644 --- a/docs/en-US/ConvertTo-ZLibString.md +++ b/docs/en-US/ConvertTo-ZLibString.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Creates a ZLib Base64 compressed string from a specified input string or strings. +Compresses input strings into a ZLib-compressed Base64-encoded string. ## SYNTAX @@ -25,11 +25,11 @@ ConvertTo-ZLibString ## DESCRIPTION -The `ConvertTo-ZLibString` cmdlet compresses input strings into ZLib Base64 encoded strings or raw bytes using a custom Zlib implementation built on the [`DeflateStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). For the implementation details, see the PSCompression source code. For expansion of Base64 ZLib strings, see [`ConvertFrom-ZLibString`](./ConvertFrom-ZLibString.md). +The `ConvertTo-ZLibString` cmdlet compresses input strings into ZLib-compressed Base64-encoded strings or raw bytes using a custom Zlib implementation built on the [`DeflateStream` Class](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream). For the implementation details, see the PSCompression source code. It is the counterpart to [`ConvertFrom-ZLibString`](./ConvertFrom-ZLibString.md). ## EXAMPLES -### Example 1: Compress strings to ZLib compressed Base64 encoded string +### Example 1: Compress strings into a ZLib-compressed Base64 string ```powershell PS ..\pwsh> $strings = 'hello', 'world', '!' @@ -43,21 +43,21 @@ PS ..\pwsh> $strings | ConvertTo-ZLibString eJzKSM3JyeflKs8vyknh5VLk5QIAAAD//wMAMosEow== ``` -This example demonstrates compressing an array of strings into a single ZLib Base64 encoded string using either positional binding or pipeline input. +This example shows how to compress an array of strings into a single ZLib-compressed Base64 string, using either argument or pipeline input. -### Example 2: Create a ZLib compressed file from a string +### Example 2: Save ZLib-compressed bytes to a file using `-AsByteStream` ```powershell PS ..\pwsh> 'hello world!' | ConvertTo-ZLibString -AsByteStream | Set-Content -FilePath .\helloworld.zlib -AsByteStream -# To read the file back you can use `ConvertFrom-BrotliString` following these steps: +# To read the file back you can use `ConvertFrom-ZLibString` following these steps: PS ..\pwsh> $path = Convert-Path .\helloworld.zlib PS ..\pwsh> [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($path)) | ConvertFrom-ZLibString hello world! ``` -Demonstrates how `-AsByteStream` outputs a byte array that can be saved to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. +This example shows how to use `-AsByteStream` to output raw compressed bytes that can be written to a file using `Set-Content` or `Out-File`. Note that the byte array is not enumerated. > [!NOTE] > The example uses `-AsByteStream` with `Set-Content`, which is available in PowerShell 7+. In Windows PowerShell 5.1, use `-Encoding Byte` with `Set-Content` or `Out-File` to write the byte array to a file. @@ -74,7 +74,7 @@ PS ..\pwsh> 'ñ' | ConvertTo-ZLibString -Encoding utf8BOM | ConvertFrom-ZLibStri This example shows how different encodings affect the compression and decompression of special characters. The default encoding is `utf8NoBOM`. -### Example 4: Compressing multiple files into one ZLib Base64 string +### Example 4: Compress the contents of multiple files into a single ZLib-compressed Base64 string ```powershell # Check the total length of the files @@ -82,11 +82,11 @@ PS ..\pwsh> (Get-Content myLogs\*.txt | Measure-Object Length -Sum).Sum / 1kb 87.216796875 # Check the total length after compression -PS ..\pwsh> (Get-Content myLogs\*.txt | ConvertTo-GzipString).Length / 1kb +PS ..\pwsh> (Get-Content myLogs\*.txt | ConvertTo-ZLibString).Length / 1kb 35.123456789 ``` -This example demonstrates compressing the contents of multiple text files into a single ZLib Base64 string and compares the total length before and after compression. +This example demonstrates compressing the contents of multiple text files into a single ZLib-compressed Base64 string and compares the total length before and after compression. ## PARAMETERS @@ -131,7 +131,7 @@ Accept wildcard characters: False Determines the character encoding used when compressing the input strings. > [!NOTE] -> The default encoding is `utf8NoBOM`. +> The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -140,7 +140,7 @@ Aliases: Required: False Position: Named -Default value: Utf8 +Default value: utf8NoBOM Accept pipeline input: False Accept wildcard characters: False ``` @@ -163,7 +163,7 @@ Accept wildcard characters: False ### -NoNewLine -The encoded string representation of the input objects is concatenated to form the output. No newline character is added after each input string when this switch is used. +The encoded string representations of the input objects are concatenated to form the output. No newline character is added after each input string when this switch is used. ```yaml Type: SwitchParameter @@ -185,13 +185,13 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### System.String[] -You can pipe strings to this cmdlet. +You can pipe one or more strings to this cmdlet. ## OUTPUTS ### System.String -By default, this cmdlet outputs a single Base64 encoded string. +By default, this cmdlet outputs a single Base64-encoded string. ### System.Byte[] @@ -201,9 +201,9 @@ When the `-AsByteStream` switch is used, this cmdlet outputs a byte array down t ## RELATED LINKS -[__ConvertFrom-ZLibString__](https://github.com/santisq/PSCompression) +[__`ConvertFrom-ZLibString`__](./ConvertFrom-ZLibString.md) -[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression?view=net-6.0) +[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) [__DeflateStream Class__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream) diff --git a/docs/en-US/Expand-TarArchive.md b/docs/en-US/Expand-TarArchive.md index daafdd5..02e9af0 100644 --- a/docs/en-US/Expand-TarArchive.md +++ b/docs/en-US/Expand-TarArchive.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Extracts files from a tar archive, optionally compressed with gzip, bzip2, Zstandard, or lzip. +Extracts files and directories from a tar archive, optionally compressed with gzip, bzip2, Zstandard, or lzip. ## SYNTAX @@ -39,7 +39,7 @@ Expand-TarArchive ## DESCRIPTION -The `Expand-TarArchive` cmdlet extracts files and directories from a tar archive, with support for compressed tar formats using gzip (`.tar.gz`), bzip2 (`.tar.bz2`), Zstandard (`.tar.zst`), lzip (`.tar.lz`), or uncompressed tar (`.tar`). It uses libraries such as `SharpZipLib` for tar handling, `System.IO.Compression` for gzip, `SharpCompress` for lzip, and `ZstdSharp` for Zstandard. This cmdlet is the counterpart to [`Compress-TarArchive`](./Compress-TarArchive.md). By default, files are extracted to the current directory unless a `-Destination` is specified. Use `-Force` to overwrite existing files and `-PassThru` to output the extracted file and directory objects. +The `Expand-TarArchive` cmdlet extracts files and directories from a tar archive, with support for compressed formats such as gzip (`.tar.gz`), bzip2 (`.tar.bz2`), Zstandard (`.tar.zst`), lzip (`.tar.lz`), or uncompressed tar (`.tar`). It uses libraries such as `SharpZipLib` for tar handling, `System.IO.Compression` for gzip, `SharpCompress` for lzip, and `ZstdSharp` for Zstandard. This cmdlet is the counterpart to [`Compress-TarArchive`](./Compress-TarArchive.md). By default, contents are extracted to the current directory unless `-Destination` is specified. Use `-Force` to overwrite existing files and `-PassThru` to output `FileInfo` and `DirectoryInfo` objects for the extracted items. > [!NOTE] > When the `-Algorithm` parameter is not specified, the cmdlet infers the compression algorithm from the file extension (e.g., `.tar.gz` for gzip, `.tar.zst` for Zstandard, `.tar` for uncompressed). See the `-Algorithm` parameter for supported extensions. @@ -50,7 +50,6 @@ The `Expand-TarArchive` cmdlet extracts files and directories from a tar archive ```powershell PS C:\> Expand-TarArchive -Path .\archive.tar - PS C:\> Get-ChildItem Directory: C:\ @@ -61,13 +60,12 @@ d---- 2025-06-23 7:00 PM folder1 -a--- 2025-06-23 7:00 PM 2048 file2.txt ``` -Extracts the contents of `archive.tar` to the current directory, creating `folder1`, `file1.txt`, and `file2.txt`. +This example extracts the contents of an uncompressed `archive.tar` file to the current directory, creating folders and files while preserving the archive structure. ### Example 2: Extract a gzip-compressed tar archive to a specific destination ```powershell PS C:\> Expand-TarArchive -Path .\archive.tar.gz -Destination .\extracted - PS C:\> Get-ChildItem .\extracted Directory: C:\extracted @@ -78,7 +76,7 @@ d---- 2025-06-23 7:00 PM folder1 -a--- 2025-06-23 7:00 PM 2048 file2.txt ``` -Extracts `archive.tar.gz` to the `extracted` directory using the gzip algorithm, creating the directory if it doesn’t exist. +This example extracts a gzip-compressed tar archive (`archive.tar.gz`) to the specified `.\extracted` directory. The destination directory is created if it does not exist. ### Example 3: Extract multiple Zstandard-compressed tar archives with PassThru @@ -96,13 +94,12 @@ d---- 2025-06-23 7:00 PM folder2 -a--- 2025-06-23 7:00 PM 4096 file3.txt ``` -Extracts all `.tar.zst` files in the current directory and outputs the extracted files and directories to the pipeline. +This example pipes multiple Zstandard-compressed tar archives (`.tar.zst`) to the cmdlet and uses `-PassThru` to output `FileInfo` and `DirectoryInfo` objects for all extracted items. -### Example 4: Overwrite existing files with Force +### Example 4: Overwrite existing files with `-Force` ```powershell PS C:\> Expand-TarArchive -Path .\archive.tar -Destination .\extracted -Force - PS C:\> Get-ChildItem .\extracted Directory: C:\extracted @@ -111,7 +108,7 @@ Mode LastWriteTime Length Name -a--- 2025-06-23 7:00 PM 1024 file1.txt ``` -Extracts `archive.tar` to the `extracted` directory, overwriting any existing `file1.txt` due to the `-Force` parameter. +This example extracts `archive.tar` to the `.\extracted` directory and overwrites any existing files with the same name due to the `-Force` parameter. ## PARAMETERS @@ -136,7 +133,7 @@ Accepted values: gz, bz2, zst, lz, none Required: False Position: Named -Default value: None +Default value: (Inferred from extension) Accept pipeline input: False Accept wildcard characters: False ``` @@ -159,7 +156,7 @@ Accept wildcard characters: False ### -Force -Overwrites existing files in the destination directory without prompting. If not specified, the cmdlet skips files that already exist. +Overwrites existing files in the destination directory without prompting. Without `-Force`, the cmdlet skips files that already exist. ```yaml Type: SwitchParameter @@ -191,7 +188,7 @@ Accept wildcard characters: False ### -PassThru -Outputs `System.IO.FileInfo` and `System.IO.DirectoryInfo` objects representing the extracted files and directories to the pipeline. By default, no output is produced unless an error occurs. +Outputs `System.IO.FileInfo` and `System.IO.DirectoryInfo` objects for the extracted files and directories. By default, the cmdlet produces no output. ```yaml Type: SwitchParameter @@ -239,13 +236,13 @@ By default, this cmdlet returns no output. ### System.IO.FileSystemInfo -When the `-PassThru` parameter is used, the cmdlet outputs objects representing the extracted files and directories. +When the `-PassThru` parameter is used, the cmdlet outputs `FileInfo` and `DirectoryInfo` objects representing the extracted items. ## NOTES ## RELATED LINKS -[__Compress-TarArchive__](https://github.com/santisq/PSCompression) +[__`Compress-TarArchive`__](./Compress-TarArchive.md) [__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) @@ -253,4 +250,4 @@ When the `-PassThru` parameter is used, the cmdlet outputs objects representing [__ZstdSharp__](https://github.com/oleg-st/ZstdSharp) -[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression?view=net-6.0) +[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) diff --git a/docs/en-US/Expand-TarEntry.md b/docs/en-US/Expand-TarEntry.md index f64d840..cd69fc5 100644 --- a/docs/en-US/Expand-TarEntry.md +++ b/docs/en-US/Expand-TarEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Expands tar archive entries to a destination directory. +Extracts selected tar archive entries to a destination directory while preserving their relative paths. ## SYNTAX @@ -24,7 +24,7 @@ Expand-TarEntry ## DESCRIPTION -The `Expand-TarEntry` cmdlet extracts tar entries output by the [`Get-TarEntry`](./Get-TarEntry.md) cmdlet to a destination directory. Expanded entries maintain their original folder structure based on their relative path within the tar archive. This cmdlet supports both uncompressed (`.tar`) and compressed tar archives (e.g., `.tar.gz`, `.tar.bz2`) processed by `Get-TarEntry`. +The `Expand-TarEntry` cmdlet extracts tar entries produced by [`Get-TarEntry`](./Get-TarEntry.md) to a destination directory. Extracted entries preserve their original relative paths and directory structure from the archive. It works with both uncompressed and compressed tar archives that have been processed by `Get-TarEntry`. ## EXAMPLES @@ -34,7 +34,7 @@ The `Expand-TarEntry` cmdlet extracts tar entries output by the [`Get-TarEntry`] PS C:\> Get-TarEntry .\archive.tar -Include *.txt | Expand-TarEntry ``` -Extracts all `.txt` files from `archive.tar` to the current directory, preserving their relative paths. +This example extracts only the `.txt` files from `archive.tar` to the current directory, preserving their relative paths within the archive. ### Example 2: Extract all `.txt` files from a tar archive to a specific directory @@ -42,7 +42,7 @@ Extracts all `.txt` files from `archive.tar` to the current directory, preservin PS C:\> Get-TarEntry .\archive.tar.gz -Include *.txt | Expand-TarEntry -Destination .\extracted ``` -Extracts all `.txt` files from `archive.tar.gz` to the `extracted` directory, creating the directory if it doesn’t exist. +This example extracts only the `.txt` files from a gzip-compressed tar archive to the specified `.\extracted` directory (created automatically if needed). ### Example 3: Extract all entries excluding `.txt` files from a tar archive @@ -50,7 +50,7 @@ Extracts all `.txt` files from `archive.tar.gz` to the `extracted` directory, cr PS C:\> Get-TarEntry .\archive.tar -Exclude *.txt | Expand-TarEntry ``` -Extracts all entries except `.txt` files from `archive.tar` to the current directory. +This example extracts everything except `.txt` files from `archive.tar` to the current directory, preserving the original structure. ### Example 4: Extract entries overwriting existing files @@ -58,7 +58,7 @@ Extracts all entries except `.txt` files from `archive.tar` to the current direc PS C:\> Get-TarEntry .\archive.tar -Include *.txt | Expand-TarEntry -Force ``` -Demonstrates how the `-Force` switch overwrites existing files in the destination directory. +This example extracts the `.txt` files and overwrites any existing files with the same name in the destination due to the `-Force` switch. ### Example 5: Extract entries and output the expanded items @@ -73,23 +73,19 @@ d---- 2025-06-23 7:00 PM folder1 -a--- 2025-06-23 7:00 PM 2048 image.png ``` -By default, this cmdlet produces no output. When `-PassThru` is used, it outputs `FileInfo` and `DirectoryInfo` objects representing the extracted entries. +This example extracts everything except `.txt` files and uses `-PassThru` to output `FileInfo` and `DirectoryInfo` objects for the extracted items. By default, the cmdlet produces no output. ### Example 6: Extract a specific entry from a compressed tar archive ```powershell PS C:\> $stream = Invoke-WebRequest https://example.com/archive.tar.gz -PS C:\> $file = $stream | Get-TarEntry -Include readme.md -Algorithm gz | Expand-TarEntry -PassThru -PS C:\> Get-Content $file.FullName - -# My Project -This is the README file for my project. +PS C:\> $stream | Get-TarEntry -Include readme.md -Algorithm gz | Expand-TarEntry -PassThru | Get-Content ``` -Extracts the `readme.md` file from a gzip-compressed tar archive retrieved via a web request and displays its contents. +This example extracts only the `readme.md` file from a gzip-compressed tar archive streamed from the web and immediately displays its contents. > [!NOTE] -> When `Get-TarEntry` processes a stream, it defaults to the `gz` (gzip) algorithm. Specify the `-Algorithm` parameter (e.g., `-Algorithm bz2` for bzip2) to match the compression type of the tar archive, or an error may occur if the stream is not gzip-compressed. +> When `Get-TarEntry` processes a stream, it defaults to the `gz` (gzip) algorithm. Specify `-Algorithm` on `Get-TarEntry` if the stream uses a different compression format. ## PARAMETERS @@ -111,7 +107,7 @@ Accept wildcard characters: False ### -Force -Overwrites existing files in the destination directory when this switch is used. +Overwrites existing files in the destination directory. Without `-Force`, existing files are skipped. ```yaml Type: SwitchParameter @@ -146,7 +142,7 @@ Accept wildcard characters: False ### -PassThru -Outputs `FileInfo` and `DirectoryInfo` objects representing the extracted entries when this switch is used. By default, the cmdlet produces no output. +Outputs `System.IO.FileInfo` and `System.IO.DirectoryInfo` objects for the extracted entries. By default, the cmdlet produces no output. ```yaml Type: SwitchParameter @@ -178,15 +174,15 @@ By default, this cmdlet produces no output. ### System.IO.FileSystemInfo -When the `-PassThru` switch is used, the cmdlet outputs objects representing the extracted files and directories. +When the `-PassThru` switch is used, the cmdlet outputs `FileInfo` and `DirectoryInfo` objects representing the extracted items. ## NOTES ## RELATED LINKS -[__Get-TarEntry__](https://github.com/santisq/PSCompression) +[__`Get-TarEntry`__](./Get-TarEntry.md) -[__Expand-TarArchive__](https://github.com/santisq/PSCompression) +[__`Expand-TarArchive`__](./Expand-TarArchive.md) [__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) diff --git a/docs/en-US/Expand-ZipEntry.md b/docs/en-US/Expand-ZipEntry.md index 3039aba..128b000 100644 --- a/docs/en-US/Expand-ZipEntry.md +++ b/docs/en-US/Expand-ZipEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Expands Zip Archive Entries to a destination directory. +Extracts selected zip archive entries to a destination directory while preserving their relative paths. ## SYNTAX @@ -19,12 +19,13 @@ Expand-ZipEntry [-Destination ] [-Force] [-PassThru] + [-Password ] [] ``` ## DESCRIPTION -The `Expand-ZipEntry` cmdlet can expand zip entries outputted by the [`Get-ZipEntry`](./Get-ZipEntry.md) command to a destination directory. Expanded entries maintain their original folder structure based on their relative path. +The `Expand-ZipEntry` cmdlet extracts zip entries produced by [`Get-ZipEntry`](./Get-ZipEntry.md) (or [`New-ZipEntry`](./New-ZipEntry.md)) to a destination directory. Extracted entries preserve their original relative paths and directory structure from the archive. It also supports extracting password-protected entries. ## EXAMPLES @@ -34,25 +35,31 @@ The `Expand-ZipEntry` cmdlet can expand zip entries outputted by the [`Get-ZipEn PS ..\pwsh> Get-ZipEntry path\to\myZip.zip -Include *.txt | Expand-ZipEntry ``` +This example extracts only the `.txt` files from a zip archive to the current directory, preserving their relative paths within the archive. + ### Example 2: Extract all `.txt` files from a Zip Archive to the a desired directory ```powershell PS ..\pwsh> Get-ZipEntry path\to\myZip.zip -Include *.txt | Expand-ZipEntry -Destination path\to\myfolder ``` +This example extracts only the `.txt` files from a zip archive to the specified destination directory (created automatically if needed). + ### Example 3: Extract all entries excluding `.txt` files to the current directory ```powershell PS ..\pwsh> Get-ZipEntry path\to\myZip.zip -Exclude *.txt | Expand-ZipEntry ``` +This example extracts everything except `.txt` files from a zip archive to the current directory, preserving the original structure. + ### Example 4: Extract all entries excluding `.txt` files to the current directory overwritting existing files ```powershell PS ..\pwsh> Get-ZipEntry path\to\myZip.zip -Exclude *.txt | Expand-ZipEntry -Force ``` -Demonstrates how `-Force` switch works. +This example extracts everything except `.txt` files and overwrites any existing files with the same name due to the `-Force` switch. ### Example 5: Extract all entries excluding `.txt` files to the current directory outputting the expanded entries @@ -60,7 +67,7 @@ Demonstrates how `-Force` switch works. PS ..\pwsh> Get-ZipEntry path\to\myZip.zip -Exclude *.txt | Expand-ZipEntry -PassThru ``` -By default this cmdlet produces no output. When `-PassThru` is used, this cmdlet outputs the `FileInfo` and `DirectoryInfo` instances representing the expanded entries. +This example extracts everything except `.txt` files and uses `-PassThru` to output `FileInfo` and `DirectoryInfo` objects for the extracted items. By default, the cmdlet produces no output. ### Example 6: Extract an entry from input Stream @@ -88,14 +95,27 @@ Copyright (c) Santiago Squarzon. All rights reserved. AliasesToExport {gziptofile, gzipfromfile, gziptostring, gzipfromstring…} ``` +This example downloads a NuGet package (which is a zip archive) from PowerShell Gallery, extracts the manifest (`.psd1`) file, and immediately evaluates it to display module metadata. + +### Example 7: Expand a password protected entry to the current directory + +```powershell +PS ..\pwsh> Get-ZipEntry .\myZip.zip -Include myEncryptedEntry.txt | Expand-ZipEntry -Password (Read-Host -AsSecureString) +``` + +This example demonstrates how to expand an encrypted entry using `Read-Host -AsSecureString` to provide the password. + +> [!TIP] +> If an entry is encrypted and no password is supplied, the cmdlet will prompt for one. + ## PARAMETERS ### -Destination -The destination directory where to extract the Zip Entries. +Specifies the root directory where zip entries are extracted. > [!NOTE] -> This parameter is optional, when not used, the entries are extracted to the their relative zip path in the current directory. +> This parameter is optional. When not used, entries are extracted relative to the current directory, preserving their paths from the archive and creating subdirectories as needed. ```yaml Type: String @@ -111,7 +131,7 @@ Accept wildcard characters: False ### -Force -Existing files in the destination directory are overwritten when this switch is used. +Overwrites existing files in the destination directory. Without `-Force`, existing files are skipped. ```yaml Type: SwitchParameter @@ -127,7 +147,7 @@ Accept wildcard characters: False ### -PassThru -The cmdlet outputs the `FileInfo` and `DirectoryInfo` instances representing the extracted entries when this switch is used. +Outputs `System.IO.FileInfo` and `System.IO.DirectoryInfo` objects for the extracted entries. By default, the cmdlet produces no output. ```yaml Type: SwitchParameter @@ -141,14 +161,33 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Password + +Specifies the password as a `SecureString` to extract the encrypted zip entry. + +> [!TIP] +> If an entry is encrypted and no password is supplied, the cmdlet will prompt for one. + +```yaml +Type: SecureString +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -InputObject The zip entries to expand. > [!NOTE] > -> - This parameter takes input from pipeline, however binding by name is also possible. -> - The input are instances inheriting from `ZipEntryBase` (`ZipEntryFile` or `ZipEntryDirectory`) outputted by [`Get-ZipEntry`](Get-ZipEntry.md) and [`New-ZipEntry`](New-ZipEntry.md) cmdlets. +> - This parameter accepts pipeline input (by value). Binding by property name is also supported. +> - The input are instances inheriting from `ZipEntryBase` (`ZipEntryFile` or `ZipEntryDirectory`) produced by [`Get-ZipEntry`](./Get-ZipEntry.md) and [`New-ZipEntry`](./New-ZipEntry.md) cmdlets. ```yaml Type: ZipEntryBase[] @@ -168,9 +207,9 @@ This cmdlet supports the common parameters. For more information, see [about_Com ## INPUTS -### PSCompression.Abstractions.ZipEntryBase +### PSCompression.Abstractions.ZipEntryBase[] -You can pipe instances of `ZipEntryFile` or `ZipEntryDirectory` to this cmdlet. These instances are produced by [`Get-ZipEntry`](Get-ZipEntry.md) and [`New-ZipEntry`](New-ZipEntry.md) cmdlets. +You can pipe instances of `ZipEntryFile` or `ZipEntryDirectory` to this cmdlet. These instances are produced by [`Get-ZipEntry`](./Get-ZipEntry.md) and [`New-ZipEntry`](./New-ZipEntry.md). ## OUTPUTS @@ -180,4 +219,12 @@ By default, this cmdlet produces no output. ### System.IO.FileSystemInfo -The cmdlet outputs the `FileInfo` and `DirectoryInfo` instances of the extracted entries when `-PassThru` switch is used. +When the `-PassThru` switch is used, the cmdlet outputs `FileInfo` and `DirectoryInfo` objects representing the extracted items. + +## NOTES + +## RELATED LINKS + +[__`Get-ZipEntry`__](./Get-ZipEntry.md) + +[__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) diff --git a/docs/en-US/Get-TarEntry.md b/docs/en-US/Get-TarEntry.md index 4b9adb7..f5c8222 100644 --- a/docs/en-US/Get-TarEntry.md +++ b/docs/en-US/Get-TarEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Lists tar archive entries from a specified path or input stream. +Lists entries from tar archives, supporting file paths and input streams. ## SYNTAX @@ -51,7 +51,7 @@ Get-TarEntry ## DESCRIPTION -The `Get-TarEntry` cmdlet lists entries in tar archives, including both uncompressed (`.tar`) and compressed formats (e.g., `.tar.gz`, `.tar.bz2`, `.tar.zst`, `.tar.lz`). It supports input from file paths or streams and outputs `TarEntryFile` or `TarEntryDirectory` objects, which can be piped to cmdlets like [`Expand-TarEntry`](./Expand-TarEntry.md) and [`Get-TarEntryContent`](./Get-TarEntryContent.md). The cmdlet uses libraries such as `SharpZipLib` for tar handling, `System.IO.Compression` for gzip, `SharpCompress` for lzip, and `ZstdSharp` for Zstandard. Use `-Include` and `-Exclude` to filter entries by name and `-Type` to filter by entry type (file or directory). +The `Get-TarEntry` cmdlet lists entries in tar archives, supporting both uncompressed (`.tar`) and compressed formats (e.g., `.tar.gz`, `.tar.bz2`, `.tar.zst`, `.tar.lz`). It accepts input from file paths and streams, and outputs `TarEntryFile` or `TarEntryDirectory` objects that can be piped to cmdlets like [`Expand-TarEntry`](./Expand-TarEntry.md) and [`Get-TarEntryContent`](./Get-TarEntryContent.md). The cmdlet uses `SharpZipLib` for tar handling, `System.IO.Compression` for gzip, `SharpCompress` for lzip, and `ZstdSharp` for Zstandard. ## EXAMPLES @@ -69,7 +69,7 @@ Archive 6/23/2025 11:08 PM 1.00 KB file1.txt Archive 6/23/2025 11:08 PM 2.00 KB file2.txt ``` -Lists all entries in `archive.tar`, including directories and files. +This example lists all entries in an uncompressed tar archive (`archive.tar`). ### Example 2: List entries from all gzip-compressed tar archives in the current directory @@ -85,8 +85,7 @@ Archive 6/23/2025 11:08 PM 1.00 KB file1.txt Archive 6/23/2025 11:08 PM 2.00 KB file2.txt ``` -> [!TIP] -> The `-Path` parameter supports wildcards. +This example lists entries from all gzip-compressed tar archives in the current directory using wildcard matching. ### Example 3: List all file entries from a tar archive @@ -101,7 +100,7 @@ Archive 6/23/2025 11:08 PM 1.00 KB file1.txt Archive 6/23/2025 11:08 PM 2.00 KB file2.txt ``` -Filters entries to show only files using `-Type Archive`. +This example lists only file entries (excluding directories) from `archive.tar` using `-Type Archive`. ### Example 4: Filter entries with Include and Exclude parameters @@ -116,7 +115,7 @@ Directory 2025-06-23 7:00 PM folder1 Archive 2025-06-23 7:00 PM 3.00 KB image.png ``` -Filters entries to include only those under `folder1/` but excludes `.txt` files. +This example lists entries under `folder1/` while excluding any `.txt` files. > [!NOTE] > If not specified, the cmdlet infers the compression algorithm from the file extension: `gz` for `.gz`, `.gzip`, `.tgz`; `bz2` for `.bz2`, `.bzip2`, `.tbz2`, `.tbz`; `zst` for `.zst`; `lz` for `.lz`; `none` for `.tar`. If the extension is unrecognized, it defaults to `none` (uncompressed tar). @@ -136,10 +135,10 @@ Archive 2025-06-23 7:00 PM 1.50 KB readme.md Archive 2025-06-23 7:00 PM 2.50 KB license.txt ``` -Lists the first three entries from a gzip-compressed tar archive stream, specifying `-Algorithm gz`. +This example lists the first three entries from a gzip-compressed tar archive retrieved via a web request. > [!NOTE] -> When processing a stream, the cmdlet defaults to the `gz` (gzip) algorithm. Specify `-Algorithm` (e.g., `-Algorithm bz2` for bzip2) to match the compression type, or an error may occur if the stream is not gzip-compressed. +> When processing a stream, the cmdlet defaults to the `gz` (gzip) algorithm. Specify `-Algorithm` (e.g., `-Algorithm bz2` for bzip2) if the stream uses a different compression format. ## PARAMETERS @@ -262,7 +261,7 @@ Accept wildcard characters: True ### -Type -Filters entries by type: `Archive` for files or `Directory` for directories. +Filters output to include only files (`Archive`) or only directories (`Directory`). ```yaml Type: EntryType @@ -303,11 +302,11 @@ Outputs objects representing directories or files in the tar archive. ## RELATED LINKS -[__Expand-TarEntry__](https://github.com/santisq/PSCompression) +[__`Expand-TarEntry`__](https://github.com/santisq/PSCompression) -[__Expand-TarArchive__](https://github.com/santisq/PSCompression) +[__`Expand-TarArchive`__](https://github.com/santisq/PSCompression) -[__Get-TarEntryContent__](https://github.com/santisq/PSCompression) +[__`Get-TarEntryContent`__](https://github.com/santisq/PSCompression) [__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) @@ -315,4 +314,4 @@ Outputs objects representing directories or files in the tar archive. [__ZstdSharp__](https://github.com/oleg-st/ZstdSharp) -[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression?view=net-6.0) +[__System.IO.Compression__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression) diff --git a/docs/en-US/Get-TarEntryContent.md b/docs/en-US/Get-TarEntryContent.md index 70970e8..48900a9 100644 --- a/docs/en-US/Get-TarEntryContent.md +++ b/docs/en-US/Get-TarEntryContent.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Gets the content of a tar archive entry. +Retrieves the content of one or more file entries from a tar archive. ## SYNTAX @@ -36,7 +36,7 @@ Get-TarEntryContent ## DESCRIPTION -The `Get-TarEntryContent` cmdlet retrieves the content of one or more `TarEntryFile` instances. This cmdlet is designed to be used with [`Get-TarEntry`](./Get-TarEntry.md) as the entry point. +The `Get-TarEntryContent` cmdlet retrieves the content of `TarEntryFile` objects produced by [`Get-TarEntry`](./Get-TarEntry.md). This cmdlet supports text output (line-by-line or raw string) and binary output (byte arrays or streams). > [!TIP] > Entries output by `Get-TarEntry` can be piped to this cmdlet. @@ -47,49 +47,27 @@ The `Get-TarEntryContent` cmdlet retrieves the content of one or more `TarEntryF ```powershell PS C:\> Get-TarEntry .\archive.tar -Include folder1/file1.txt | Get-TarEntryContent - -Line 1 of file1.txt -Line 2 of file1.txt ``` -The `-Include` parameter from `Get-TarEntry` targets a specific entry by its relative path, and the output is piped to `Get-TarEntryContent`. By default, the cmdlet streams content line by line. +This example retrieves the text content of a specific file entry from a tar archive. By default, content is streamed line by line (as an array of strings). ### Example 2: Get raw content of a tar archive entry ```powershell PS C:\> Get-TarEntry .\archive.tar -Include folder1/file1.txt | Get-TarEntryContent -Raw - -Line 1 of file1.txt -Line 2 of file1.txt ``` -The cmdlet outputs a single multi-line string when the `-Raw` switch is used instead of line-by-line streaming. +This example retrieves the entire text content as a single multi-line string using the `-Raw` switch. ### Example 3: Get the bytes of a tar archive entry as a stream ```powershell PS C:\> $bytes = Get-TarEntry .\archive.tar -Include folder1/helloworld.txt | Get-TarEntryContent -AsByteStream -PS C:\> $bytes -104 -101 -108 -108 -111 -32 -119 -111 -114 -108 -100 -33 -13 -10 - PS C:\> [System.Text.Encoding]::UTF8.GetString($bytes) hello world! ``` -The `-AsByteStream` switch is useful for reading non-text tar entries. +This example retrieves the raw bytes of a file entry as a byte array using `-AsByteStream`, then converts them to a string. ### Example 4: Get contents of all `.md` files as byte arrays @@ -105,19 +83,17 @@ PS C:\> $bytes[1].Length 7767 ``` -When the `-Raw` and `-AsByteStream` switches are used together, the cmdlet outputs `byte[]` as single objects for each tar entry. +This example retrieves the raw bytes of all `.md` files as an array of `byte[]` objects (one per entry) using `-AsByteStream` and `-Raw`. ### Example 5: Get content from an input stream ```powershell PS C:\> $stream = Invoke-WebRequest https://example.com/archive.tar.gz -PS C:\> $content = $stream | Get-TarEntry -Include readme.md -Algorithm gz | Get-TarEntryContent -Raw -PS C:\> $content - -# My Project -This is the README file for my project. +PS C:\> $stream | Get-TarEntry -Include readme.md -Algorithm gz | Get-TarEntryContent -Raw ``` +This example retrieves the content of `readme.md` from a gzip-compressed tar archive streamed from the web. + > [!NOTE] > When `Get-TarEntry` processes a stream, it defaults to the `gz` (gzip) algorithm. Specify `-Algorithm` (e.g., `-Algorithm bz2` for bzip2) to match the compression type, or an error may occur if the stream is not gzip-compressed. @@ -162,7 +138,7 @@ Specifies the character encoding used to read the entry content. . The default e > [!NOTE] > > - This parameter applies only when `-AsByteStream` is not used. -> - The default encoding is __`utf8NoBOM`__. +> - The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -194,7 +170,7 @@ Accept wildcard characters: False ### -Raw -Returns the entire contents of an entry as a single string with newlines preserved, ignoring newline characters. By default, newline characters are used to separate the content into an array of strings. +By default, the cmdlet outputs text content as an array of strings (split on newlines). The `-Raw` switch returns the entire content as a single string with newlines preserved. ```yaml Type: SwitchParameter @@ -216,7 +192,7 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### PSCompression.TarEntryFile[] -You can pipe instances of `TarEntryFile` to this cmdlet, produced by [`Get-TarEntry`](./Get-TarEntry.md). +You can pipe one or more `TarEntryFile` objects produced by [`Get-TarEntry`](./Get-TarEntry.md) to this cmdlet. ## OUTPUTS @@ -226,15 +202,16 @@ By default, this cmdlet returns the content as an array of strings, one per line ### System.Byte -This cmdlet returns the content as bytes when the `-AsByteStream` parameter is used. +- When the `-AsByteStream` parameter is used, this cmdlet returns the content as a byte array (`System.Byte[]`). +- When `-AsByteStream` and `-Raw` are combined, it returns an array of byte arrays (one per entry). ## NOTES ## RELATED LINKS -[__Get-TarEntry__](https://github.com/santisq/PSCompression) +[__`Get-TarEntry`__](./Get-TarEntry.md) -[__Expand-TarEntry__](https://github.com/santisq/PSCompression) +[__`Expand-TarEntry`__](./Expand-TarEntry.md) [__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) diff --git a/docs/en-US/Get-ZipEntry.md b/docs/en-US/Get-ZipEntry.md index 3308690..0224d20 100644 --- a/docs/en-US/Get-ZipEntry.md +++ b/docs/en-US/Get-ZipEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Lists zip archive entries from a specified path or input stream. +Lists entries from zip archives, supporting file paths and input streams. ## SYNTAX @@ -48,7 +48,7 @@ Get-ZipEntry ## DESCRIPTION -The `Get-ZipEntry` cmdlet is the main entry point for the `*-ZipEntry` cmdlets in this module. It can list zip archive entries from specified paths or input stream. +The `Get-ZipEntry` cmdlet lists entries in zip archives. It supports input from file paths and streams, and outputs `ZipEntryFile` or `ZipEntryDirectory` objects that can be piped to other `*-ZipEntry` cmdlets such as [`Expand-ZipEntry`](./Expand-ZipEntry.md) or [`Get-ZipEntryContent`](./Get-ZipEntryContent.md). ## EXAMPLES @@ -58,13 +58,15 @@ The `Get-ZipEntry` cmdlet is the main entry point for the `*-ZipEntry` cmdlets i PS ..\pwsh> Get-ZipEntry path\to\myZip.zip ``` +This example lists all entries in the specified zip archive. + ### Example 2: List entries from all files with `.zip` extension in the current directory ```powershell PS ..\pwsh> Get-ZipEntry *.zip ``` -The `-Path` parameter supports wildcards. +This example lists entries from all `.zip` files in the current directory. The `-Path` parameter supports wildcards. ### Example 3: List all `Archive` entries from a Zip file @@ -166,11 +168,13 @@ Type LastWriteTime CompressedSize Size Name Archive 11/6/2024 10:29 PM 635.00 B 1.55 KB 3212d87de09c4241a06e0166a08c3b13.psmdcp ``` +This example downloads a NuGet package (a zip archive) from PowerShell Gallery and lists the first five entries from the streamed content. + ## PARAMETERS ### -Type -Filters entries by type: `Archive` for files or `Directory` for directories. +Filters output to include only files (`Archive`) or only directories (`Directory`). ```yaml Type: EntryType @@ -260,7 +264,7 @@ Accept wildcard characters: True Specifies an input stream containing a zip archive. > [!TIP] -> Output from `Invoke-WebRequest` is bound to this paremeter automatically. +> Output from `Invoke-WebRequest` is automatically bound to this parameter. ```yaml Type: Stream @@ -280,16 +284,26 @@ This cmdlet supports the common parameters. For more information, see [about_Com ## INPUTS -### String +### System.String[] You can pipe a string that contains a paths to this cmdlet. Output from `Get-ChildItem` or `Get-Item` can be piped to this cmdlet. -### Stream +### System.IO.Stream -You can pipe a Stream to this cmdlet. Output from `Invoke-WebRequest` can be piped to this cmdlet. +You can pipe a stream containing a zip archive (e.g., output from `Invoke-WebRequest`) to this cmdlet via the `-InputStream` parameter. ## OUTPUTS ### PSCompression.ZipEntryDirectory ### PSCompression.ZipEntryFile + +## NOTES + +## RELATED LINKS + +[__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) + +[__ZipFile__](https://icsharpcode.github.io/SharpZipLib/help/api/ICSharpCode.SharpZipLib.Zip.ZipFile.html) + +[__ZipEntry__](https://icsharpcode.github.io/SharpZipLib/api/ICSharpCode.SharpZipLib.Zip.ZipEntry.html) diff --git a/docs/en-US/Get-ZipEntryContent.md b/docs/en-US/Get-ZipEntryContent.md index a8d3772..7cca970 100644 --- a/docs/en-US/Get-ZipEntryContent.md +++ b/docs/en-US/Get-ZipEntryContent.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Gets the content of a zip entry. +Retrieves the content of one or more file entries from a zip archive. ## SYNTAX @@ -20,6 +20,7 @@ Get-ZipEntryContent -Entry [-Encoding ] [-Raw] + [-Password ] [] ``` @@ -31,13 +32,13 @@ Get-ZipEntryContent [-Raw] [-AsByteStream] [-BufferSize ] + [-Password ] [] ``` ## DESCRIPTION -The `Get-ZipEntryContent` cmdlet gets the content of one or more `ZipEntryFile` instances. -This cmdlet is meant to be used with [`Get-ZipEntry`](./Get-ZipEntry.md) as your entry point. +The `Get-ZipEntryContent` cmdlet retrieves the content of `ZipEntryFile` objects produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md). This cmdlet supports text output (line-by-line or raw string) and binary output (byte arrays or streams). It also supports reading password-protected entries. > [!TIP] > Entries outputted by `Get-ZipEntry` can be piped to this cmdlet. @@ -50,8 +51,7 @@ This cmdlet is meant to be used with [`Get-ZipEntry`](./Get-ZipEntry.md) as your PS ..pwsh\> Get-ZipEntry .\myZip.zip -Include myrelative/entry.txt | Get-ZipEntryContent ``` -`-Include` parameter from `Get-ZipEntry` can be used to target a specific entry by passing the entry's relative path, from there the output can be piped directly to `Get-ZipEntryContent`. -By default, the cmdlet streams line-by-line . +This example retrieves the text content of a specific file entry from a zip archive. By default, content is streamed line by line (as an array of strings). ### Example 2: Get raw content of a Zip Archive Entry @@ -59,33 +59,17 @@ By default, the cmdlet streams line-by-line . PS ..pwsh\> Get-ZipEntry .\myZip.zip -Include myrelative/entry.txt | Get-ZipEntryContent -Raw ``` -The cmdlet outputs a single multi-line string when the `-Raw` switch is used instead of line-by-line streaming. +This example retrieves the entire text content as a single multi-line string using the `-Raw` switch. ### Example 3: Get the bytes of a Zip Archive Entry as a Stream ```powershell PS ..pwsh\> $bytes = Get-ZipEntry .\test.zip -Include test/helloworld.txt | Get-ZipEntryContent -AsByteStream -PS ..pwsh\> $bytes -104 -101 -108 -108 -111 -32 -119 -111 -114 -108 -100 -33 -13 -10 - PS ..pwsh\> [System.Text.Encoding]::UTF8.GetString($bytes) hello world! ``` -The `-AsByteStream` switch can be useful to read non-text zip entries. +This example retrieves the raw bytes of a file entry as a byte array using `-AsByteStream`, then converts them to a string. ### Example 4: Get contents of all `.md` files as byte arrays @@ -101,7 +85,7 @@ PS ..pwsh\> $bytes[1].Length 7767 ``` -When the `-Raw` and `-AsByteStream` switches are used together the cmdlet outputs `byte[]` as single objects for each zip entry. +This example retrieves the raw bytes of all `.md` files as an array of `byte[]` objects (one per entry) using `-AsByteStream` and `-Raw`. ### Example 5: Get content from input Stream @@ -128,6 +112,19 @@ Copyright (c) Santiago Squarzon. All rights reserved. AliasesToExport {gziptofile, gzipfromfile, gziptostring, gzipfromstring…} ``` +This example downloads a NuGet package (a zip archive) from PowerShell Gallery, extracts the manifest (`.psd1`) file, and immediately evaluates it to display module metadata. + +### Example 6: Get content from a password protected entry + +```powershell +PS ..\pwsh> Get-ZipEntry .\myZip.zip -Include myEncryptedEntry.txt | Get-ZipEntryContent -Password (Read-Host -AsSecureString) +``` + +This example demonstrates how to read an encrypted entry using `Read-Host -AsSecureString` to provide the password. + +> [!TIP] +> If an entry is encrypted and no password is supplied, the cmdlet will prompt for one. + ## PARAMETERS ### -BufferSize @@ -153,7 +150,7 @@ Specifies the character encoding used to read the entry content. . The default e > [!NOTE] > > - This parameter applies only when `-AsByteStream` is not used. -> - The default encoding is __`utf8NoBOM`__. +> - The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -169,7 +166,7 @@ Accept wildcard characters: False ### -Raw -Returns the entire contents of an entry as a single string with newlines preserved, ignoring newline characters. By default, newline characters are used to separate the content into an array of strings. +By default, the cmdlet outputs text content as an array of strings (split on newlines). The `-Raw` switch returns the entire content as a single string with newlines preserved. ```yaml Type: SwitchParameter @@ -215,15 +212,34 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Password + +Specifies the password as a `SecureString` to extract the encrypted zip entry. + +> [!TIP] +> If an entry is encrypted and no password is supplied, the cmdlet will prompt for one. + +```yaml +Type: SecureString +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters. See [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS -### PSCompression.ZipEntryFile +### PSCompression.ZipEntryFile[] -You can pipe instances of `ZipEntryFile` to this cmdlet. These instances are produced by [`Get-ZipEntry`](Get-ZipEntry.md) and [`New-ZipEntry`](New-ZipEntry.md) cmdlets. +You can pipe one or more `ZipEntryFile` objects produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md) to this cmdlet. ## OUTPUTS @@ -233,4 +249,13 @@ By default, this cmdlet returns the content as an array of strings, one per line ### System.Byte -This cmdlet returns the content as bytes when the `-AsByteStream` parameter is used. +- When the `-AsByteStream` parameter is used, this cmdlet returns the content as a byte array (`System.Byte[]`). +- When `-AsByteStream` and `-Raw` are combined, it returns an array of byte arrays (one per entry). + +## NOTES + +## RELATED LINKS + +[__`Get-ZipEntry`__](./Get-ZipEntry.md) + +[__SharpZipLib__](https://github.com/icsharpcode/SharpZipLib) diff --git a/docs/en-US/New-ZipEntry.md b/docs/en-US/New-ZipEntry.md index bbfbdf0..8656540 100644 --- a/docs/en-US/New-ZipEntry.md +++ b/docs/en-US/New-ZipEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Creates zip entries from one or more specified entry relative paths. +Creates new entries in a zip archive from strings or existing files. ## SYNTAX @@ -40,7 +40,7 @@ New-ZipEntry ## DESCRIPTION -The `New-ZipEntry` cmdlet can create one or more Zip Archive Entries from specified paths. The type of the created entries is determined by their path, for example, if a path ends with `\` or `/`, the entry will be created as a `Directory` entry, otherwise it will be an `Archive` entry. +The `New-ZipEntry` cmdlet creates new entries in an existing or new zip archive. Entries can be created from string input (`-Value`) or by copying files from the filesystem (`-SourcePath`). The type of entry (file or directory) is determined by the provided `-EntryPath`: paths ending in `\` or `/` create directory entries; all others create file entries. Entry paths, _arguments of the `-EntryPath` parameter_, are always normalized, a few examples of how paths are normalized: @@ -53,7 +53,7 @@ Entry paths, _arguments of the `-EntryPath` parameter_, are always normalized, a > [!TIP] > The `[PSCompression.Extensions.PathExtensions]::NormalizePath(string path)` static method is available as a public API if you want to normalize your paths before creating new entries. -In addition, `New-ZipEntry` can set the content of the entries that it creates from string input or by specifying a source file path. +When adding from string input (`-Value`), the `-EntryPath` parameter is required. When adding from a file (`-SourcePath`), `-EntryPath` is optional — if omitted, the normalized full path of the source file is used. > [!NOTE] > Due to a .NET limitation, adding files larger than 2 GB to an existing zip archive may fail. To handle such files, recreate the zip archive or use tools like 7-Zip. See [issue #19](https://github.com/santisq/PSCompression/issues/19) for details. @@ -78,6 +78,8 @@ Type LastWriteTime CompressedSize Size Name Archive 2/24/2024 3:22 PM 0.00 B 0.00 B entry ``` +This example creates an empty file entry (`test/entry`) and a directory entry (`newfolder/`) in `test.zip`. + ### Example 2: Create entries with content from input strings ```powershell @@ -92,8 +94,10 @@ world ! ``` +This example pipes three strings into `New-ZipEntry`, creating/overwriting file and directory entries. The content of the file entry (`test/entry`) becomes the piped strings (joined with newlines). + > [!TIP] -> The cmdlet prevents creating entries in a destination Zip archive if an entry with the same relative path already exists. You can use the `-Force` parameter to overwrite them. +> The cmdlet prevents creating duplicate entries in the destination zip archive. Use `-Force` to overwrite existing entries with the same path. ### Example 3: Create entries with content from a source file path @@ -102,6 +106,8 @@ PS ..\pwsh> $file = 'hello world!' | New-Item mytestfile.txt PS ..\pwsh> New-ZipEntry .\test.zip -SourcePath $file.FullName -EntryPath newentry.txt ``` +This example adds the contents of a local file to the zip archive under the specified entry path. + ### Example 4: Archive all files in a specified location ```powershell @@ -109,9 +115,10 @@ PS ..\pwsh> $files = Get-ChildItem -File -Recurse PS ..\pwsh> $files | ForEach-Object { New-ZipEntry .\test.zip -SourcePath $_.FullName } ``` +This example recursively adds all files from the current directory to the zip archive, preserving their relative paths. + > [!TIP] -> The `-EntryPath` parameter is optional when creating an entry from a source file. -> If the entry path isn't specified, the cmdlet will using the file's `.FullName` in its normalized form. +> The `-EntryPath` parameter is optional when using `-SourcePath`. If omitted, the normalized full path of the source file is used as the entry path. ### Example 5: Archive all `.txt` files in a specified location using a specified encoding @@ -123,8 +130,10 @@ PS ..\pwsh> $files | ForEach-Object { } ``` +This example reads `.txt` files with a specific encoding and adds them to the zip archive under their original (normalized) paths. + > [!NOTE] -> As opposed to previous example, when creating entries from input values, __the `-EntryPath` parameter is mandatory__. In this case, when using an absolute path as `-EntryPath` the cmdlet will always create the entries using their normalized form as explained in [__Description__](#description). +> When creating entries from piped string input (`-Value`), the `-EntryPath` parameter is required. Absolute paths are automatically normalized as shown in the Description section. ## PARAMETERS @@ -167,8 +176,8 @@ The character encoding used to set the entry content. > [!NOTE] > -> - __This parameter is applicable only when `-SourcePath` is not used.__ -> - The default encoding is __`utf8NoBOM`__. +> - This parameter applies only when content is provided via `-Value` (string input). +> - The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -200,7 +209,7 @@ Accept wildcard characters: False ### -Force -The cmdlet prevents creating entries in a destination Zip archive if an entry with the same relative path already exists. You can use the `-Force` parameter to overwrite them. +Overwrites existing entries with the same path in the destination zip archive. Without `-Force`, the cmdlet throws an error if a duplicate path is encountered. ```yaml Type: SwitchParameter @@ -252,12 +261,22 @@ This cmdlet supports the common parameters. For more information, see [about_Com ## INPUTS -### System.String +### System.String[] -You can pipe a value for the new zip entry to this cmdlet. +You can pipe one or more strings to this cmdlet to use as content for new file entries (requires `-EntryPath`). ## OUTPUTS ### PSCompression.ZipEntryDirectory ### PSCompression.ZipEntryFile + +The cmdlet outputs the newly created `ZipEntryDirectory` or `ZipEntryFile` objects. + +## NOTES + +## RELATED LINKS + +[__System.IO.Compression.ZipArchive__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchive) + +[__System.IO.Compression.ZipArchiveEntry__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchiveentry) diff --git a/docs/en-US/Remove-ZipEntry.md b/docs/en-US/Remove-ZipEntry.md index 60ad969..0156f6f 100644 --- a/docs/en-US/Remove-ZipEntry.md +++ b/docs/en-US/Remove-ZipEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Removes zip entries from one or more zip archives. +Removes specified entries from zip archives. ## SYNTAX @@ -23,7 +23,7 @@ Remove-ZipEntry ## DESCRIPTION -The `Remove-ZipEntry` cmdlet can remove Zip Archive Entries from one or more Zip Archives. This cmdlet takes input from and is intended to be used in combination with the [`Get-ZipEntry`](./Get-ZipEntry.md) cmdlet. +The `Remove-ZipEntry` cmdlet removes `ZipEntryFile` or `ZipEntryDirectory` objects produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md) from their parent zip archives. ## EXAMPLES @@ -33,12 +33,16 @@ The `Remove-ZipEntry` cmdlet can remove Zip Archive Entries from one or more Zip PS ..pwsh\> Get-ZipEntry .\myZip.zip | Remove-ZipEntry ``` +This example removes all entries from `myZip.zip`, effectively emptying the archive. + ### Example 2: Remove all `.txt` Entries from a Zip Archive ```powershell PS ..pwsh\> Get-ZipEntry .\myZip.zip -Include *.txt | Remove-ZipEntry ``` +This example removes only the entries matching the `*.txt` pattern from `myZip.zip`. + ### Example 3: Prompt for confirmation before removing entries ```powershell @@ -50,13 +54,13 @@ Performing the operation "Remove" on target "test/helloworld.txt". [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): ``` -This cmdlet supports [`ShouldProcess`](https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-shouldprocess?view=powershell-7.3), you can prompt for confirmation before removing entries with `-Confirm` or check what the cmdlet would do without performing any action with `-WhatIf`. +This example prompts for confirmation before removing each matching entry. The cmdlet supports [`ShouldProcess`](https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-shouldprocess): use `-Confirm` to prompt or `-WhatIf` to preview changes without applying them. ## PARAMETERS ### -InputObject -The entries that should be removed. This parameter can be and is meant to be bound from pipeline however can be also used as a named parameter. +Specifies the zip entries to remove. This parameter accepts pipeline input from `Get-ZipEntry` or `New-ZipEntry` and can also be used as a named argument. ```yaml Type: ZipEntryBase[] @@ -88,8 +92,7 @@ Accept wildcard characters: False ### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. +Shows what would happen if the cmdlet runs. The cmdlet is not run. ```yaml Type: SwitchParameter @@ -109,12 +112,24 @@ This cmdlet supports the common parameters. For more information, see [about_Com ## INPUTS -### PSCompression.Abstractions.ZipEntryBase +### PSCompression.Abstractions.ZipEntryBase[] -You can pipe instances of `ZipEntryFile` and `ZipEntryDirectory` to this cmdlet. These instances are produced by [`Get-ZipEntry`](Get-ZipEntry.md) and [`New-ZipEntry`](New-ZipEntry.md) cmdlets. +You can pipe one or more `ZipEntryFile` or `ZipEntryDirectory` objects produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md) to this cmdlet. ## OUTPUTS ### None This cmdlet produces no output. + +## NOTES + +## RELATED LINKS + +[__`Get-ZipEntry`__](./Get-ZipEntry.md) + +[__`New-ZipEntry`__](./New-ZipEntry.md) + +[__System.IO.Compression.ZipArchive__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchive) + +[__System.IO.Compression.ZipArchiveEntry__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchiveentry) diff --git a/docs/en-US/Rename-ZipEntry.md b/docs/en-US/Rename-ZipEntry.md index 4dc2464..0d0f44c 100644 --- a/docs/en-US/Rename-ZipEntry.md +++ b/docs/en-US/Rename-ZipEntry.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Renames zip entries from one or more zip archives. +Renames a zip entry (file or directory) within its archive. ## SYNTAX @@ -25,8 +25,7 @@ Rename-ZipEntry ## DESCRIPTION -The `Rename-ZipEntry` cmdlet changes the name of a specified item. -This cmdlet does not affect the content of the item being renamed. +The `Rename-ZipEntry` cmdlet renames a single `ZipEntryFile` or `ZipEntryDirectory` object produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md). The operation copies the entry with the new name and deletes the original. > [!NOTE] > @@ -46,6 +45,8 @@ PS ..pwsh\> Get-ZipEntry .\myZip.zip -Type Archive -Include relativePath/to/myEn Rename-ZipEntry -NewName myNewName.ext ``` +This example renames a specific file entry inside the zip archive to `myNewName.ext`. + ### Example 2: Rename all entries with `.ext` extension using a delay-bind scriptblock ```powershell @@ -53,10 +54,10 @@ PS ..pwsh\> Get-ZipEntry .\myZip.zip -Type Archive -Include *.ext | Rename-ZipEntry -NewName { $_.BaseName + 'myNewName' + $_.Extension } ``` -[Delay-bind scriptblocks](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.4#using-delay-bind-script-blocks-with-parameters) is supported for renaming multiple entries. +[Delay-bind scriptblocks](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks#using-delay-bind-script-blocks-with-parameters) is supported for renaming multiple entries. > [!TIP] -> In the context of the _delay-bind scriptblock_, [`$_` (`$PSItem`)](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psitem?view=powershell-7.4) represents the current pipeline item, in this case, an instance of `PSCompression.ZipEntryFile` or `PSCompression.ZipEntryDirectory`. +> In the context of the _delay-bind scriptblock_, [`$_` (`$PSItem`)](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psitem) represents the current pipeline item, in this case, an instance of `PSCompression.ZipEntryFile` or `PSCompression.ZipEntryDirectory`. ### Example 3: Rename a Zip Directory Entry @@ -105,6 +106,8 @@ Archive 2/25/2024 12:41 PM 741.00 B 2.16 KB Rename-Zip Archive 2/25/2024 12:41 PM 1.55 KB 5.35 KB Set-ZipEntryContent.md ``` +This example renames a directory entry (`en-US/`) to `en-US123/`. All child entries are automatically updated to reflect the new parent path. + ### Example 4: Prompt for confirmation before renaming entries ```powershell @@ -116,6 +119,8 @@ Performing the operation "Rename" on target "PSCompression/docs/en-US123/". [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): ``` +This example prompts for confirmation before renaming the directory entry. The cmdlet supports `ShouldProcess`: use `-Confirm` to prompt or `-WhatIf` to preview the operation. + ## PARAMETERS ### -NewName @@ -123,7 +128,7 @@ Performing the operation "Rename" on target "PSCompression/docs/en-US123/". Specifies the new name of the zip entry. Enter only a name, not a path and name. > [!TIP] -> [Delay-bind scriptblock](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.4#using-delay-bind-script-blocks-with-parameters) is supported for this parameter. See [Example 2](#example-2-rename-all-entries-with-ext-extension-using-a-delay-bind-scriptblock). +> [Delay-bind scriptblock](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks#using-delay-bind-script-blocks-with-parameters) is supported for this parameter. See [Example 2](#example-2-rename-all-entries-with-ext-extension-using-a-delay-bind-scriptblock). ```yaml Type: String @@ -139,7 +144,7 @@ Accept wildcard characters: False ### -PassThru -The cmdlet outputs the `ZipEntryFile` and `ZipEntryDirectory` instances representing the renamed entries when this switch is used. +Outputs the renamed `ZipEntryFile` or `ZipEntryDirectory` object. By default, the cmdlet produces no output. ```yaml Type: SwitchParameter @@ -159,8 +164,8 @@ The zip entries to rename. > [!NOTE] > -> - This parameter takes input from pipeline, however binding by name is also possible. -> - The input are instances inheriting from `ZipEntryBase` (`ZipEntryFile` or `ZipEntryDirectory`) outputted by [`Get-ZipEntry`](Get-ZipEntry.md) and [`New-ZipEntry`](New-ZipEntry.md) cmdlets. +> - This parameter accepts pipeline input (by value). Binding by property name is also supported. +> - Input objects are instances of `ZipEntryFile` or `ZipEntryDirectory` produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md). ```yaml Type: ZipEntryBase @@ -192,8 +197,7 @@ Accept wildcard characters: False ### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. +Shows what would happen if the cmdlet runs. The cmdlet is not run. ```yaml Type: SwitchParameter @@ -215,7 +219,7 @@ This cmdlet supports the common parameters. For more information, see [about_Com ### PSCompression.Abstractions.ZipEntryBase -You can pipe instances of `ZipEntryFile` or `ZipEntryDirectory` to this cmdlet. These instances are produced by [`Get-ZipEntry`](Get-ZipEntry.md) and [`New-ZipEntry`](New-ZipEntry.md) cmdlets. +You can pipe a single `ZipEntryFile` or `ZipEntryDirectory` object produced by ['`Get-ZipEntry`'](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md) to this cmdlet. ## OUTPUTS @@ -227,4 +231,16 @@ By default, this cmdlet produces no output. ### PSCompression.ZipEntryDirectory -This cmdlet outputs the renamed entries when the `-PassThru` switch is used. +When the `-PassThru` switch is used, the cmdlet outputs the renamed entry object. + +## NOTES + +## RELATED LINKS + +[__`Get-ZipEntry`__](./Get-ZipEntry.md) + +[__`New-ZipEntry`__](./New-ZipEntry.md) + +[__System.IO.Compression.ZipArchive__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchive) + +[__System.IO.Compression.ZipArchiveEntry__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchiveentry) diff --git a/docs/en-US/Set-ZipEntryContent.md b/docs/en-US/Set-ZipEntryContent.md index 7691966..75e8f8b 100644 --- a/docs/en-US/Set-ZipEntryContent.md +++ b/docs/en-US/Set-ZipEntryContent.md @@ -9,7 +9,7 @@ schema: 2.0.0 ## SYNOPSIS -Sets or appends content to an existing zip entry. +Sets or appends content to an existing zip file entry. ## SYNTAX @@ -40,9 +40,9 @@ Set-ZipEntryContent ## DESCRIPTION -The `Set-ZipEntryContent` cmdlet can write or append content to a Zip Archive Entry. By default, this cmdlet replaces the existing content of a Zip Archive Entry, if you need to append content you can use the `-Append` switch. This cmdlet also supports writing or appending raw bytes while using the `-AsByteStream` switch. To send content to `Set-ZipEntryContent` you can use the `-Value` parameter on the command line or send content through the pipeline. +The `Set-ZipEntryContent` cmdlet writes or appends content to a `ZipEntryFile` object produced by [`Get-ZipEntry`](./Get-ZipEntry.md) or [`New-ZipEntry`](./New-ZipEntry.md). By default, it replaces existing content; use `-Append` to add to it. Content can be text (strings) or raw bytes (via `-AsByteStream`). Input can be piped or provided via `-Value`. -If you need to create a new Zip Archive Entry you can use the [`New-ZipEntry` cmdlet](./New-ZipEntry.md). +To create a new entry, use [`New-ZipEntry`](./New-ZipEntry.md). > [!NOTE] > Due to a .NET limitation, writing or appending content larger than 2 GB to an existing zip entry may fail. To handle such content, recreate the zip archive or use tools like 7-Zip. See [issue #19](https://github.com/santisq/PSCompression/issues/19) for details. @@ -60,7 +60,7 @@ world ! ``` -You can send content through the pipeline or using the `-Value` parameter as shown in the next example. +This example creates a new file entry and pipes strings to set its content (replacing any existing data). ### Example 2: Append content to a Zip Archive Entry @@ -75,6 +75,8 @@ world ! ``` +This example appends additional strings to the existing entry content using `-Append`. + ### Example 3: Write raw bytes to a Zip Archive Entry ```powershell @@ -85,7 +87,7 @@ PS ..pwsh\> $entry | Get-ZipEntryContent hello world! ``` -The cmdlet supports writing and appending raw bytes while using the `-AsByteStream` switch. +This example appends the same byte array to the entry using `-AsByteStream` and `-Append`. ### Example 4: Append raw bytes to a Zip Archive Entry @@ -137,8 +139,8 @@ For efficiency purposes this cmdlet buffers bytes before writing them to the Zip > [!NOTE] > -> - __This parameter is applicable only when `-AsByteStream` is used.__ -> The buffer default value is __128 KiB.__ +> - This parameter applies only when `-AsByteStream` is used. +> - The default buffer size is __128 KiB.__ ```yaml Type: Int32 @@ -158,8 +160,8 @@ The character encoding used to read the entry content. > [!NOTE] > -> - __This parameter is applicable only when `-AsByteStream` is not used.__ -> - The default encoding is __`utf8NoBOM`__. +> - This parameter applies only when `-AsByteStream` is not used. +> - The default encoding is UTF-8 without BOM. ```yaml Type: Encoding @@ -175,7 +177,7 @@ Accept wildcard characters: False ### -PassThru -Outputs the object representing the updated zip archive entry. By default, this cmdlet does not generate any output. +Outputs the updated `ZipEntryFile` object. By default, the cmdlet produces no output. ```yaml Type: SwitchParameter @@ -227,16 +229,28 @@ This cmdlet supports the common parameters. For more information, see [about_Com ## INPUTS -### System.Object +### System.Object[] -You can pipe strings or bytes to this cmdlet. +You can pipe strings (for text content) or byte arrays (for binary content) to this cmdlet. ## OUTPUTS ### None -This cmdlet produces no output by default . +By default, this cmdlet produces no output. ### PSCompression.ZipEntryFile -This cmdlet outputs the updated entry when the `-PassThru` switch is used. +When the `-PassThru` switch is used, the cmdlet outputs the updated `ZipEntryFile` object. + +## NOTES + +## RELATED LINKS + +[__`Get-ZipEntry`__](./Get-ZipEntry.md) + +[__`New-ZipEntry`__](./New-ZipEntry.md) + +[__System.IO.Compression.ZipArchive__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchive) + +[__System.IO.Compression.ZipArchiveEntry__](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchiveentry) diff --git a/module/PSCompression.psd1 b/module/PSCompression.psd1 index ab55e7f..b089f59 100644 --- a/module/PSCompression.psd1 +++ b/module/PSCompression.psd1 @@ -11,7 +11,7 @@ RootModule = 'PSCompression.psm1' # Version number of this module. - ModuleVersion = '3.0.1' + ModuleVersion = '3.1.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/src/PSCompression.Shared/LoadContext.cs b/src/PSCompression.Shared/LoadContext.cs index 048bb01..293bc3f 100644 --- a/src/PSCompression.Shared/LoadContext.cs +++ b/src/PSCompression.Shared/LoadContext.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Reflection; using System.Runtime.Loader; +using System.IO; namespace PSCompression.Shared; @@ -47,19 +47,13 @@ private LoadContext(string mainModulePathAssemblyPath) public static Assembly Initialize() { - LoadContext? instance = _instance; - if (instance is not null) + if (_instance is not null) { - return instance._moduleAssembly; + return _instance._moduleAssembly; } lock (s_sync) { - if (_instance is not null) - { - return _instance._moduleAssembly; - } - string assemblyPath = typeof(LoadContext).Assembly.Location; string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); string moduleName = assemblyName[..^7]; @@ -67,7 +61,8 @@ public static Assembly Initialize() Path.GetDirectoryName(assemblyPath)!, $"{moduleName}.dll"); - return new LoadContext(modulePath)._moduleAssembly; + _instance = new LoadContext(modulePath); + return _instance._moduleAssembly; } } } diff --git a/src/PSCompression/Abstractions/EntryBase.cs b/src/PSCompression/Abstractions/EntryBase.cs index 99a5b1f..d36b8fc 100644 --- a/src/PSCompression/Abstractions/EntryBase.cs +++ b/src/PSCompression/Abstractions/EntryBase.cs @@ -17,7 +17,7 @@ public abstract class EntryBase(string source) public string Source { get; } = source; - public abstract string Name { get; protected set; } + public abstract string? Name { get; protected set; } public abstract string RelativePath { get; } diff --git a/src/PSCompression/Abstractions/ZipContentOpsBase.cs b/src/PSCompression/Abstractions/EntryStreamOpsBase.cs similarity index 59% rename from src/PSCompression/Abstractions/ZipContentOpsBase.cs rename to src/PSCompression/Abstractions/EntryStreamOpsBase.cs index e8918a5..d84d038 100644 --- a/src/PSCompression/Abstractions/ZipContentOpsBase.cs +++ b/src/PSCompression/Abstractions/EntryStreamOpsBase.cs @@ -1,21 +1,19 @@ using System; -using System.IO.Compression; +using System.IO; namespace PSCompression.Abstractions; -internal abstract class ZipContentOpsBase(ZipArchive zip) : IDisposable +internal abstract class EntryStreamOpsBase(Stream stream) : IDisposable { - protected byte[]? _buffer; + private bool _disposed; - protected ZipArchive _zip = zip; - - protected bool _disposed; + protected Stream Stream { get; } = stream; protected virtual void Dispose(bool disposing) { if (disposing && !_disposed) { - _zip.Dispose(); + Stream.Dispose(); _disposed = true; } } diff --git a/src/PSCompression/Abstractions/FromCompressedStringCommandBase.cs b/src/PSCompression/Abstractions/FromCompressedStringCommandBase.cs index 14fb148..6aa42b4 100644 --- a/src/PSCompression/Abstractions/FromCompressedStringCommandBase.cs +++ b/src/PSCompression/Abstractions/FromCompressedStringCommandBase.cs @@ -35,11 +35,11 @@ private void Decompress( if (Raw) { - reader.WriteAllTextToPipeline(this); + reader.ReadToEnd(this); return; } - reader.WriteLinesToPipeline(this); + reader.ReadLines(this); } protected abstract Stream CreateDecompressionStream(Stream inputStream); diff --git a/src/PSCompression/Abstractions/GetEntryCommandBase.cs b/src/PSCompression/Abstractions/GetEntryCommandBase.cs index 3e93b7a..ba16ff5 100644 --- a/src/PSCompression/Abstractions/GetEntryCommandBase.cs +++ b/src/PSCompression/Abstractions/GetEntryCommandBase.cs @@ -7,6 +7,7 @@ using PSCompression.Exceptions; using ICSharpCode.SharpZipLib.Tar; using ZstdSharp; +using ICSharpCode.SharpZipLib.Zip; namespace PSCompression.Abstractions; @@ -46,18 +47,18 @@ protected override void BeginProcessing() return; } - const WildcardOptions options = WildcardOptions.Compiled + const WildcardOptions Options = WildcardOptions.Compiled | WildcardOptions.CultureInvariant | WildcardOptions.IgnoreCase; if (Exclude is not null) { - _excludePatterns = [.. Exclude.Select(e => new WildcardPattern(e, options))]; + _excludePatterns = [.. Exclude.Select(e => new WildcardPattern(e, Options))]; } if (Include is not null) { - _includePatterns = [.. Include.Select(e => new WildcardPattern(e, options))]; + _includePatterns = [.. Include.Select(e => new WildcardPattern(e, Options))]; } } @@ -167,6 +168,6 @@ protected bool ShouldExclude(string name) protected bool ShouldSkipEntry(bool isDirectory) => isDirectory && Type is EntryType.Archive || !isDirectory && Type is EntryType.Directory; - private bool IsInvalidArchive(Exception exception) => - exception is InvalidDataException or TarException or ZstdException or IOException; + private static bool IsInvalidArchive(Exception exception) => + exception is ZipException or TarException or ZstdException or IOException; } diff --git a/src/PSCompression/Abstractions/GetEntryContentCommandBase.cs b/src/PSCompression/Abstractions/GetEntryContentCommandBase.cs index 0427221..6266bee 100644 --- a/src/PSCompression/Abstractions/GetEntryContentCommandBase.cs +++ b/src/PSCompression/Abstractions/GetEntryContentCommandBase.cs @@ -8,6 +8,8 @@ namespace PSCompression.Abstractions; public abstract class GetEntryContentCommandBase : PSCmdlet where T : EntryBase { + protected byte[]? Buffer { get; set; } + [Parameter(Mandatory = true, ValueFromPipeline = true)] public T[] Entry { get; set; } = null!; @@ -26,4 +28,12 @@ public abstract class GetEntryContentCommandBase : PSCmdlet [Parameter(ParameterSetName = "Bytes")] [ValidateNotNullOrEmpty] public int BufferSize { get; set; } = 128_000; + + protected override void BeginProcessing() + { + if (ParameterSetName == "Bytes") + { + Buffer = new byte[BufferSize]; + } + } } diff --git a/src/PSCompression/Abstractions/TarEntryBase.cs b/src/PSCompression/Abstractions/TarEntryBase.cs index bdb6bd7..24b88dd 100644 --- a/src/PSCompression/Abstractions/TarEntryBase.cs +++ b/src/PSCompression/Abstractions/TarEntryBase.cs @@ -7,7 +7,7 @@ namespace PSCompression.Abstractions; public abstract class TarEntryBase(TarEntry entry, string source) : EntryBase(source) { - public override string Name { get; protected set; } = Path.GetFileName(entry.Name); + public override string? Name { get; protected set; } public override string RelativePath { get; } = entry.Name; @@ -36,10 +36,9 @@ internal FileSystemInfo ExtractTo( } FileInfo file = new(destination); - file.Directory?.Create(); + file.Directory!.Create(); - using FileStream destStream = File.Open( - destination, + using FileStream destStream = file.Open( overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.Write); diff --git a/src/PSCompression/Abstractions/ToCompressedFileCommandBase.cs b/src/PSCompression/Abstractions/ToCompressedFileCommandBase.cs index 90eefef..5899e9d 100644 --- a/src/PSCompression/Abstractions/ToCompressedFileCommandBase.cs +++ b/src/PSCompression/Abstractions/ToCompressedFileCommandBase.cs @@ -81,11 +81,11 @@ protected override void BeginProcessing() if (Exclude is not null) { - const WildcardOptions options = WildcardOptions.Compiled + const WildcardOptions Options = WildcardOptions.Compiled | WildcardOptions.CultureInvariant | WildcardOptions.IgnoreCase; - _excludePatterns = [.. Exclude.Select(pattern => new WildcardPattern(pattern, options))]; + _excludePatterns = [.. Exclude.Select(pattern => new WildcardPattern(pattern, Options))]; } } diff --git a/src/PSCompression/Abstractions/ZipEntryBase.cs b/src/PSCompression/Abstractions/ZipEntryBase.IO.Compression.cs similarity index 50% rename from src/PSCompression/Abstractions/ZipEntryBase.cs rename to src/PSCompression/Abstractions/ZipEntryBase.IO.Compression.cs index d6ecaf0..875b8a2 100644 --- a/src/PSCompression/Abstractions/ZipEntryBase.cs +++ b/src/PSCompression/Abstractions/ZipEntryBase.IO.Compression.cs @@ -1,32 +1,13 @@ -using System; using System.IO; using System.IO.Compression; using PSCompression.Exceptions; -using PSCompression.Extensions; namespace PSCompression.Abstractions; -public abstract class ZipEntryBase(ZipArchiveEntry entry, string source) : EntryBase(source) +public abstract partial class ZipEntryBase { - public override string Name { get; protected set; } = entry.Name; - - public override string RelativePath { get; } = entry.FullName; - - public override DateTime LastWriteTime { get; } = entry.LastWriteTime.LocalDateTime; - - public override long Length { get; internal set; } = entry.Length; - - public long CompressedLength { get; internal set; } = entry.CompressedLength; - - protected ZipEntryBase(ZipArchiveEntry entry, Stream? stream) - : this(entry, $"InputStream.{Guid.NewGuid()}") - { - _stream = stream; - } - - public ZipArchive OpenRead() => FromStream - ? new ZipArchive(_stream) - : ZipFile.OpenRead(Source); + public ZipArchive OpenRead() => + FromStream ? new ZipArchive(_stream) : ZipFile.OpenRead(Source); public ZipArchive OpenWrite() { @@ -94,39 +75,4 @@ internal ZipArchive OpenZip(ZipArchiveMode mode) => FromStream ? new ZipArchive(_stream, mode, true) : ZipFile.Open(Source, mode); - - public FileSystemInfo ExtractTo(string destination, bool overwrite) - { - using ZipArchive zip = FromStream - ? new ZipArchive(_stream, ZipArchiveMode.Read, leaveOpen: true) - : ZipFile.OpenRead(Source); - - return ExtractTo(destination, overwrite, zip); - } - - internal FileSystemInfo ExtractTo( - string destination, - bool overwrite, - ZipArchive zip) - { - destination = Path.GetFullPath( - Path.Combine(destination, RelativePath)); - - if (Type == EntryType.Directory) - { - DirectoryInfo dir = new(destination); - dir.Create(overwrite); - return dir; - } - - FileInfo file = new(destination); - file.Directory?.Create(); - - if (zip.TryGetEntry(RelativePath, out ZipArchiveEntry? entry)) - { - entry.ExtractToFile(destination, overwrite); - } - - return file; - } } diff --git a/src/PSCompression/Abstractions/ZipEntryBase.SharpZipLib.Zip.cs b/src/PSCompression/Abstractions/ZipEntryBase.SharpZipLib.Zip.cs new file mode 100644 index 0000000..8374f03 --- /dev/null +++ b/src/PSCompression/Abstractions/ZipEntryBase.SharpZipLib.Zip.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; +using System.Security; +using ICSharpCode.SharpZipLib.Zip; +using PSCompression.Exceptions; +using PSCompression.Extensions; + +namespace PSCompression.Abstractions; + +public abstract partial class ZipEntryBase(ZipEntry entry, string source) + : EntryBase(source) +{ + public override string? Name { get; protected set; } + + public override string RelativePath { get; } = entry.Name; + + public override DateTime LastWriteTime { get; } = entry.DateTime; + + public override long Length { get; internal set; } = entry.Size; + + public long CompressedLength { get; internal set; } = entry.CompressedSize; + + public bool IsEncrypted { get; } = entry.IsCrypted; + + public int AESKeySize { get; } = entry.AESKeySize; + + public CompressionMethod CompressionMethod { get; } = entry.CompressionMethod; + + public string Comment { get; } = entry.Comment ?? string.Empty; + + public long Crc { get; } = entry.Crc; + + protected ZipEntryBase(ZipEntry entry, Stream? stream) + : this(entry, $"InputStream.{Guid.NewGuid()}") + { + _stream = stream; + } + + internal ZipFile OpenRead(SecureString? password) + { + ZipFile zip = FromStream ? new(_stream, leaveOpen: true) : new(Source); + if (password?.Length > 0) + { + zip.Password = password.AsPlainText(); + } + + return zip; + } + + public FileSystemInfo ExtractTo( + string destination, + bool overwrite, + SecureString? password = null) + { + using ZipFile zip = FromStream + ? new(_stream, leaveOpen: true) + : new(Source); + + if (password?.Length > 0) + { + zip.Password = password.AsPlainText(); + } + + return ExtractTo(destination, overwrite, zip); + } + + internal FileSystemInfo ExtractTo( + string destination, + bool overwrite, + ZipFile zip) + { + zip.ThrowIfNotFound( + RelativePath, + Source, + out ZipEntry? entry); + + destination = Path.GetFullPath( + Path.Combine(destination, RelativePath)); + + if (Type == EntryType.Directory) + { + DirectoryInfo dir = new(destination); + dir.Create(overwrite); + return dir; + } + + FileInfo file = new(destination); + file.Directory!.Create(); + + using Stream source = zip.GetInputStream(entry); + using FileStream fs = file.Open( + overwrite ? FileMode.Create : FileMode.CreateNew, + FileAccess.Write); + + source.CopyTo(fs); + + return file; + } +} diff --git a/src/PSCompression/AlgorithmMappings.cs b/src/PSCompression/AlgorithmMappings.cs index f9fc456..5144138 100644 --- a/src/PSCompression/AlgorithmMappings.cs +++ b/src/PSCompression/AlgorithmMappings.cs @@ -31,6 +31,5 @@ internal static class AlgorithmMappings }; internal static Algorithm Parse(string path) => - _mappings.TryGetValue(Path.GetExtension(path), out Algorithm value) - ? value : Algorithm.none; + _mappings.TryGetValue(Path.GetExtension(path), out Algorithm value) ? value : Algorithm.none; } diff --git a/src/PSCompression/Commands/ExpandZipEntryCommand.cs b/src/PSCompression/Commands/ExpandZipEntryCommand.cs index a981300..b7f28c7 100644 --- a/src/PSCompression/Commands/ExpandZipEntryCommand.cs +++ b/src/PSCompression/Commands/ExpandZipEntryCommand.cs @@ -1,7 +1,10 @@ using System; using System.IO; using System.Management.Automation; +using System.Security; +using ICSharpCode.SharpZipLib.Zip; using PSCompression.Abstractions; +using PSCompression.Extensions; namespace PSCompression.Commands; @@ -10,10 +13,23 @@ namespace PSCompression.Commands; [Alias("unzipentry")] public sealed class ExpandZipEntryCommand : ExpandEntryCommandBase, IDisposable { - private readonly ZipArchiveCache _cache = new(); + [Parameter] + public SecureString? Password { get; set; } - protected override FileSystemInfo Extract(ZipEntryBase entry) => - entry.ExtractTo(Destination!, Force, _cache.GetOrAdd(entry)); + private ZipArchiveCache? _cache; + + protected override FileSystemInfo Extract(ZipEntryBase entry) + { + _cache ??= new ZipArchiveCache(entry => entry.OpenRead(Password)); + ZipFile zip = _cache.GetOrCreate(entry); + + if (entry.IsEncrypted && Password is null && entry is ZipEntryFile fileEntry) + { + zip.Password = fileEntry.PromptForPassword(Host); + } + + return entry.ExtractTo(Destination!, Force, zip); + } public void Dispose() { diff --git a/src/PSCompression/Commands/GetTarEntryContentCommand.cs b/src/PSCompression/Commands/GetTarEntryContentCommand.cs index e997f9a..462864c 100644 --- a/src/PSCompression/Commands/GetTarEntryContentCommand.cs +++ b/src/PSCompression/Commands/GetTarEntryContentCommand.cs @@ -13,8 +13,6 @@ namespace PSCompression.Commands; [Alias("targec")] public sealed class GetTarEntryContentCommand : GetEntryContentCommandBase { - private byte[]? _buffer; - protected override void ProcessRecord() { foreach (TarEntryFile entry in Entry) @@ -37,7 +35,6 @@ protected override void ProcessRecord() private void ReadEntry(TarEntryFile entry) { using MemoryStream mem = new(); - if (!entry.GetContentStream(mem)) { return; @@ -51,32 +48,18 @@ private void ReadEntry(TarEntryFile entry) return; } - StreamBytes(mem); + using EntryByteReader byteReader = new(mem, Buffer!); + byteReader.StreamBytes(this); return; } using StreamReader reader = new(mem, Encoding); - - if (Raw.IsPresent) + if (Raw) { - reader.WriteAllTextToPipeline(this); + reader.ReadToEnd(this); return; } - reader.WriteLinesToPipeline(this); - } - - private void StreamBytes(Stream stream) - { - int bytesRead; - _buffer ??= new byte[BufferSize]; - - while ((bytesRead = stream.Read(_buffer, 0, BufferSize)) > 0) - { - for (int i = 0; i < bytesRead; i++) - { - WriteObject(_buffer[i]); - } - } + reader.ReadLines(this); } } diff --git a/src/PSCompression/Commands/GetZipEntryCommand.cs b/src/PSCompression/Commands/GetZipEntryCommand.cs index a00b9aa..051e1b8 100644 --- a/src/PSCompression/Commands/GetZipEntryCommand.cs +++ b/src/PSCompression/Commands/GetZipEntryCommand.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using System.IO.Compression; using System.Management.Automation; using System.IO; using PSCompression.Abstractions; +using ICSharpCode.SharpZipLib.Zip; namespace PSCompression.Commands; @@ -16,23 +16,21 @@ public sealed class GetZipEntryCommand : GetEntryCommandBase protected override IEnumerable GetEntriesFromFile(string path) { List entries = []; - using (ZipArchive zip = ZipFile.OpenRead(path)) + using (ZipFile zip = new(path)) { - foreach (ZipArchiveEntry entry in zip.Entries) + foreach (ZipEntry entry in zip) { - bool isDirectory = string.IsNullOrEmpty(entry.Name); - - if (ShouldSkipEntry(isDirectory)) + if (ShouldSkipEntry(entry.IsDirectory)) { continue; } - if (!ShouldInclude(entry.FullName) || ShouldExclude(entry.FullName)) + if (!ShouldInclude(entry.Name) || ShouldExclude(entry.Name)) { continue; } - entries.Add(isDirectory + entries.Add(entry.IsDirectory ? new ZipEntryDirectory(entry, path) : new ZipEntryFile(entry, path)); } @@ -44,23 +42,21 @@ protected override IEnumerable GetEntriesFromFile(string path) protected override IEnumerable GetEntriesFromStream(Stream stream) { List entries = []; - using (ZipArchive zip = new(stream, ZipArchiveMode.Read, true)) + using (ZipFile zip = new(stream, leaveOpen: true)) { - foreach (ZipArchiveEntry entry in zip.Entries) + foreach (ZipEntry entry in zip) { - bool isDirectory = string.IsNullOrEmpty(entry.Name); - - if (ShouldSkipEntry(isDirectory)) + if (ShouldSkipEntry(entry.IsDirectory)) { continue; } - if (!ShouldInclude(entry.FullName) || ShouldExclude(entry.FullName)) + if (!ShouldInclude(entry.Name) || ShouldExclude(entry.Name)) { continue; } - entries.Add(isDirectory + entries.Add(entry.IsDirectory ? new ZipEntryDirectory(entry, stream) : new ZipEntryFile(entry, stream)); } diff --git a/src/PSCompression/Commands/GetZipEntryContentCommand.cs b/src/PSCompression/Commands/GetZipEntryContentCommand.cs index 29e530e..3929277 100644 --- a/src/PSCompression/Commands/GetZipEntryContentCommand.cs +++ b/src/PSCompression/Commands/GetZipEntryContentCommand.cs @@ -1,7 +1,11 @@ using System; +using System.IO; using System.Management.Automation; +using System.Security; +using ICSharpCode.SharpZipLib.Zip; using PSCompression.Abstractions; using PSCompression.Exceptions; +using PSCompression.Extensions; namespace PSCompression.Commands; @@ -11,16 +15,26 @@ namespace PSCompression.Commands; [Alias("zipgec")] public sealed class GetZipEntryContentCommand : GetEntryContentCommandBase, IDisposable { - private readonly ZipArchiveCache _cache = new(); + [Parameter] + public SecureString? Password { get; set; } + + private ZipArchiveCache? _cache; protected override void ProcessRecord() { + _cache ??= new ZipArchiveCache(entry => entry.OpenRead(Password)); + foreach (ZipEntryFile entry in Entry) { try { - ZipContentReader reader = new(_cache.GetOrAdd(entry)); - ReadEntry(entry, reader); + ZipFile zip = _cache.GetOrCreate(entry); + if (entry.IsEncrypted && Password is null) + { + zip.Password = entry.PromptForPassword(Host); + } + + ReadEntry(entry.Open(zip)); } catch (Exception _) when (_ is PipelineStoppedException or FlowControlException) { @@ -33,27 +47,29 @@ protected override void ProcessRecord() } } - private void ReadEntry(ZipEntryFile entry, ZipContentReader reader) + private void ReadEntry(Stream stream) { if (AsByteStream) { + using EntryByteReader byteReader = new(stream, Buffer!); if (Raw) { - reader.ReadAllBytes(entry, this); + byteReader.ReadAllBytes(this); return; } - reader.StreamBytes(entry, BufferSize, this); + byteReader.StreamBytes(this); return; } - if (Raw.IsPresent) + using StreamReader stringReader = new(stream, Encoding); + if (Raw) { - reader.ReadToEnd(entry, Encoding, this); + stringReader.ReadToEnd(this); return; } - reader.StreamLines(entry, Encoding, this); + stringReader.ReadLines(this); } public void Dispose() diff --git a/src/PSCompression/Commands/NewZipEntryCommand.cs b/src/PSCompression/Commands/NewZipEntryCommand.cs index 770ee4a..1ba0a7d 100644 --- a/src/PSCompression/Commands/NewZipEntryCommand.cs +++ b/src/PSCompression/Commands/NewZipEntryCommand.cs @@ -8,6 +8,7 @@ using PSCompression.Extensions; using PSCompression.Exceptions; using PSCompression.Abstractions; +using ICSharpCode.SharpZipLib.Zip; namespace PSCompression.Commands; @@ -20,7 +21,7 @@ public sealed class NewZipEntryCommand : PSCmdlet, IDisposable private ZipArchive? _zip; - private ZipContentWriter[]? _writers; + private StreamWriter[]? _writers; private string[]? _entryPath; @@ -61,7 +62,7 @@ protected override void BeginProcessing() try { - _zip = ZipFile.Open(Destination, ZipArchiveMode.Update); + _zip = System.IO.Compression.ZipFile.Open(Destination, ZipArchiveMode.Update); if (ParameterSetName == "Value") { @@ -103,6 +104,7 @@ protected override void BeginProcessing() share: FileShare.ReadWrite); EntryPath ??= [SourcePath.NormalizePath()]; + foreach (string entry in EntryPath) { if (_zip.TryGetEntry(entry, out ZipArchiveEntry? zipentry)) @@ -147,11 +149,12 @@ protected override void ProcessRecord() try { - _writers ??= [.. _entries + _writers ??= _entries .Where(e => !string.IsNullOrEmpty(e.Name)) - .Select(e => new ZipContentWriter(_zip, e, Encoding))]; + .Select(e => new StreamWriter(e.Open(), Encoding)) + .ToArray(); - foreach (ZipContentWriter writer in _writers) + foreach (StreamWriter writer in _writers) { writer.WriteLines(Value); } @@ -166,16 +169,10 @@ protected override void EndProcessing() { try { - if (_writers is not null) - { - foreach (ZipContentWriter writer in _writers) - { - writer.Close(); - } - } - - _zip?.Dispose(); - WriteObject(GetResult(), enumerateCollection: true); + Dispose(); + WriteObject( + GetEntries().ToEntrySort(), + enumerateCollection: true); } catch (Exception _) when (_ is PipelineStoppedException or FlowControlException) { @@ -187,37 +184,27 @@ protected override void EndProcessing() } } - private IEnumerable GetResult() + private IEnumerable GetEntries() { - using ZipArchive zip = ZipFile.OpenRead(Destination); - List _result = new(_entries.Count); - + using ICSharpCode.SharpZipLib.Zip.ZipFile zip = new(Destination); foreach (ZipArchiveEntry entry in _entries) { - if (!zip.TryGetEntry(entry.FullName, out ZipArchiveEntry? zipEntry)) - { - continue; - } - - if (string.IsNullOrEmpty(entry.Name)) + if (zip.TryGetEntry(entry.FullName, out ZipEntry? zipEntry)) { - _result.Add(new ZipEntryDirectory(zipEntry, Destination)); - continue; + yield return zipEntry.IsDirectory + ? new ZipEntryDirectory(zipEntry, Destination) + : new ZipEntryFile(zipEntry, Destination); } - - _result.Add(new ZipEntryFile(zipEntry, Destination)); } - - return _result.ToEntrySort(); } public void Dispose() { if (_writers is not null) { - foreach (ZipContentWriter writer in _writers) + foreach (StreamWriter writer in _writers) { - writer?.Dispose(); + writer.Dispose(); } } diff --git a/src/PSCompression/Commands/RemoveZipEntryCommand.cs b/src/PSCompression/Commands/RemoveZipEntryCommand.cs index 4caba19..07001b2 100644 --- a/src/PSCompression/Commands/RemoveZipEntryCommand.cs +++ b/src/PSCompression/Commands/RemoveZipEntryCommand.cs @@ -11,7 +11,8 @@ namespace PSCompression.Commands; [Alias("ziprm")] public sealed class RemoveZipEntryCommand : PSCmdlet, IDisposable { - private readonly ZipArchiveCache _cache = new(ZipArchiveMode.Update); + private readonly ZipArchiveCache _cache = new( + entry => entry.OpenZip(ZipArchiveMode.Update)); [Parameter(Mandatory = true, ValueFromPipeline = true)] public ZipEntryBase[] InputObject { get; set; } = null!; @@ -24,7 +25,7 @@ protected override void ProcessRecord() { if (ShouldProcess(target: entry.ToString(), action: "Remove")) { - entry.Remove(_cache.GetOrAdd(entry)); + entry.Remove(_cache.GetOrCreate(entry)); } } catch (Exception _) when (_ is PipelineStoppedException or FlowControlException) diff --git a/src/PSCompression/Commands/RenameZipEntryCommand.cs b/src/PSCompression/Commands/RenameZipEntryCommand.cs index ef44eb2..1522bae 100644 --- a/src/PSCompression/Commands/RenameZipEntryCommand.cs +++ b/src/PSCompression/Commands/RenameZipEntryCommand.cs @@ -13,7 +13,8 @@ namespace PSCompression.Commands; [Alias("zipren")] public sealed class RenameZipEntryCommand : PSCmdlet, IDisposable { - private readonly ZipArchiveCache _zipArchiveCache = new(ZipArchiveMode.Update); + private readonly ZipArchiveCache _zipArchiveCache = new( + entry => entry.OpenZip(ZipArchiveMode.Update)); private ZipEntryCache? _zipEntryCache; diff --git a/src/PSCompression/Commands/SetZipEntryContentCommand.cs b/src/PSCompression/Commands/SetZipEntryContentCommand.cs index 5bc1bf7..29ee396 100644 --- a/src/PSCompression/Commands/SetZipEntryContentCommand.cs +++ b/src/PSCompression/Commands/SetZipEntryContentCommand.cs @@ -1,7 +1,10 @@ using System; +using System.IO; +using System.IO.Compression; using System.Management.Automation; using System.Text; using PSCompression.Exceptions; +using PSCompression.Extensions; namespace PSCompression.Commands; @@ -10,7 +13,11 @@ namespace PSCompression.Commands; [Alias("zipsc")] public sealed class SetZipEntryContentCommand : PSCmdlet, IDisposable { - private ZipContentWriter? _zipWriter; + private ZipArchive? _zip; + + private ZipEntryByteWriter? _byteWriter; + + private StreamWriter? _stringWriter; [Parameter(Mandatory = true, ValueFromPipeline = true)] public object[] Value { get; set; } = null!; @@ -40,19 +47,24 @@ protected override void BeginProcessing() { try { - if (AsByteStream.IsPresent) + _zip = SourceEntry.OpenWrite(); + Stream stream = SourceEntry.Open(_zip); + + if (AsByteStream) + { + _byteWriter = new ZipEntryByteWriter(stream, BufferSize, Append); + return; + } + + _stringWriter = new StreamWriter(stream, Encoding); + + if (Append) { - _zipWriter = new ZipContentWriter( - entry: SourceEntry, - append: Append.IsPresent, - bufferSize: BufferSize); + _stringWriter.BaseStream.Seek(0, SeekOrigin.End); return; } - _zipWriter = new ZipContentWriter( - entry: SourceEntry, - append: Append.IsPresent, - encoding: Encoding); + _stringWriter.BaseStream.SetLength(0); } catch (Exception exception) { @@ -64,16 +76,13 @@ protected override void ProcessRecord() { try { - Dbg.Assert(_zipWriter is not null); - - if (AsByteStream.IsPresent) + if (AsByteStream) { - _zipWriter.WriteBytes(LanguagePrimitives.ConvertTo(Value)); + _byteWriter!.WriteBytes(LanguagePrimitives.ConvertTo(Value)); return; } - _zipWriter.WriteLines( - LanguagePrimitives.ConvertTo(Value)); + _stringWriter!.WriteLines(LanguagePrimitives.ConvertTo(Value)); } catch (Exception exception) { @@ -83,16 +92,11 @@ protected override void ProcessRecord() protected override void EndProcessing() { - Dbg.Assert(_zipWriter is not null); - - if (!PassThru.IsPresent) - { - return; - } + if (!PassThru) return; try { - _zipWriter.Dispose(); + Dispose(); SourceEntry.Refresh(); WriteObject(SourceEntry); } @@ -104,7 +108,9 @@ protected override void EndProcessing() public void Dispose() { - _zipWriter?.Dispose(); + _byteWriter?.Dispose(); + _stringWriter?.Dispose(); + _zip?.Dispose(); GC.SuppressFinalize(this); } } diff --git a/src/PSCompression/EncodingCompleter.cs b/src/PSCompression/EncodingCompleter.cs index 992b472..aa294e6 100644 --- a/src/PSCompression/EncodingCompleter.cs +++ b/src/PSCompression/EncodingCompleter.cs @@ -13,8 +13,7 @@ public sealed class EncodingCompleter : IArgumentCompleter static EncodingCompleter() { - List set = new( - [ + List set = new([ "ascii", "bigendianUtf32", "unicode", diff --git a/src/PSCompression/EntryByteReader.cs b/src/PSCompression/EntryByteReader.cs new file mode 100644 index 0000000..59b9642 --- /dev/null +++ b/src/PSCompression/EntryByteReader.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.Management.Automation; +using PSCompression.Abstractions; + +namespace PSCompression; + +internal sealed class EntryByteReader : EntryStreamOpsBase +{ + private readonly byte[] _buffer; + + private readonly int _bufferSize; + + internal EntryByteReader(Stream stream, byte[] buffer) + : base(stream) + { + _buffer = buffer; + _bufferSize = buffer.Length; + } + + internal void StreamBytes(PSCmdlet cmdlet) + { + int bytes; + while ((bytes = Stream.Read(_buffer, 0, _bufferSize)) > 0) + { + for (int i = 0; i < bytes; i++) + { + cmdlet.WriteObject(_buffer[i]); + } + } + } + + internal void ReadAllBytes(PSCmdlet cmdlet) + { + using MemoryStream mem = new(); + Stream.CopyTo(mem); + cmdlet.WriteObject(mem.ToArray()); + } +} diff --git a/src/PSCompression/Exceptions/ExceptionHelper.cs b/src/PSCompression/Exceptions/ExceptionHelper.cs index 8ab9059..ce66834 100644 --- a/src/PSCompression/Exceptions/ExceptionHelper.cs +++ b/src/PSCompression/Exceptions/ExceptionHelper.cs @@ -3,6 +3,7 @@ using System.IO; using System.IO.Compression; using System.Management.Automation; +using ICSharpCode.SharpZipLib.Zip; using PSCompression.Abstractions; using PSCompression.Extensions; @@ -89,7 +90,7 @@ internal static ErrorRecord ToInvalidArchive( "might be compressed using an unsupported method, " + "or could be corrupted."; - if (type is ArchiveType.tar && isStream) + if (type == ArchiveType.tar && isStream) { basemsg += " When reading a tar archive from a stream, " + "use the -Algorithm parameter to specify the compression type."; @@ -113,6 +114,18 @@ internal static void ThrowIfNotFound( } } + internal static void ThrowIfNotFound( + this ICSharpCode.SharpZipLib.Zip.ZipFile zip, + string path, + string source, + [NotNull] out ZipEntry? entry) + { + if (!zip.TryGetEntry(path, out entry)) + { + throw EntryNotFoundException.Create(path, source); + } + } + internal static void ThrowIfDuplicate( this ZipArchive zip, string path, diff --git a/src/PSCompression/Extensions/CompressionExtensions.cs b/src/PSCompression/Extensions/CompressionExtensions.cs index 0480b14..830b991 100644 --- a/src/PSCompression/Extensions/CompressionExtensions.cs +++ b/src/PSCompression/Extensions/CompressionExtensions.cs @@ -3,26 +3,39 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; -using System.Management.Automation; using System.Text.RegularExpressions; using ICSharpCode.SharpZipLib.BZip2; using ICSharpCode.SharpZipLib.Tar; +using ICSharpCode.SharpZipLib.Zip; using SharpCompress.Compressors.LZMA; using ZstdSharp; using SharpCompressors = SharpCompress.Compressors; namespace PSCompression.Extensions; -internal static class CompressionExtensions +internal static partial class CompressionExtensions { + private const string DirectorySeparator = "/"; + +#if NETCOREAPP + [GeneratedRegex(@"[^/]+(?=/$)", + RegexOptions.Compiled | RegexOptions.RightToLeft)] + private static partial Regex GetDirectoryName(); + + private static readonly Regex s_reGetDirName = GetDirectoryName(); + + internal static string RelativeTo(this DirectoryInfo directory, int length) => + string.Concat(directory.FullName.AsSpan(length), DirectorySeparator) + .NormalizeEntryPath(); +#else private static readonly Regex s_reGetDirName = new( @"[^/]+(?=/$)", RegexOptions.Compiled | RegexOptions.RightToLeft); - private const string _directorySeparator = "/"; - internal static string RelativeTo(this DirectoryInfo directory, int length) => - (directory.FullName.Substring(length) + _directorySeparator).NormalizeEntryPath(); + $"{directory.FullName.Substring(length)}{DirectorySeparator}" + .NormalizeEntryPath(); +#endif internal static string RelativeTo(this FileInfo file, int length) => file.FullName.Substring(length).NormalizeFileEntryPath(); @@ -58,20 +71,29 @@ internal static bool TryGetEntry( return entry is not null; } + internal static bool TryGetEntry( + this ICSharpCode.SharpZipLib.Zip.ZipFile zip, + string path, + [NotNullWhen(true)] out ZipEntry? entry) + { + entry = zip.GetEntry(path); + return entry is not null; + } + internal static string ChangeName( this ZipEntryFile file, string newname) { string normalized = file.RelativePath.NormalizePath(); - if (normalized.IndexOf(_directorySeparator) == -1) + if (!normalized.Contains(DirectorySeparator)) { return newname; } return string.Join( - _directorySeparator, - normalized.Substring(0, normalized.Length - file.Name.Length - 1), + DirectorySeparator, + normalized.Substring(0, normalized.Length - file.Name!.Length - 1), newname); } @@ -82,40 +104,12 @@ internal static string ChangeName( directory.RelativePath.NormalizePath(), newname); - internal static string GetDirectoryName(this ZipArchiveEntry entry) - => s_reGetDirName.Match(entry.FullName).Value; + internal static string GetDirectoryName(this ZipEntry entry) + => s_reGetDirName.Match(entry.Name).Value; internal static string GetDirectoryName(this TarEntry entry) => s_reGetDirName.Match(entry.Name).Value; - internal static void WriteAllTextToPipeline(this StreamReader reader, PSCmdlet cmdlet) - => cmdlet.WriteObject(reader.ReadToEnd()); - - internal static void WriteLinesToPipeline(this StreamReader reader, PSCmdlet cmdlet) - { - string? line; - while ((line = reader.ReadLine()) is not null) - { - cmdlet.WriteObject(line); - } - } - - internal static void WriteLines(this StreamWriter writer, string[] lines) - { - foreach (string line in lines) - { - writer.WriteLine(line); - } - } - - internal static void WriteContent(this StreamWriter writer, string[] lines) - { - foreach (string line in lines) - { - writer.Write(line); - } - } - internal static BrotliSharpLib.BrotliStream AsBrotliCompressedStream( this Stream stream, CompressionLevel compressionLevel) diff --git a/src/PSCompression/Extensions/DictionaryExtensions.cs b/src/PSCompression/Extensions/DictionaryExtensions.cs deleted file mode 100644 index dc9c85e..0000000 --- a/src/PSCompression/Extensions/DictionaryExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace PSCompression.Extensions; - -internal static class DictionaryExtensions -{ - internal static void Deconstruct( - this KeyValuePair keyv, - out TKey key, - out TValue value) - { - key = keyv.Key; - value = keyv.Value; - } -} diff --git a/src/PSCompression/Extensions/MiscExtensions.cs b/src/PSCompression/Extensions/MiscExtensions.cs new file mode 100644 index 0000000..4c6ba23 --- /dev/null +++ b/src/PSCompression/Extensions/MiscExtensions.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Host; +using System.Net; +using System.Security; + +namespace PSCompression.Extensions; + +internal static class MiscExtensions +{ + internal static void Deconstruct( + this KeyValuePair keyv, + out TKey key, + out TValue value) + { + key = keyv.Key; + value = keyv.Value; + } + + internal static string AsPlainText(this SecureString secureString) => + new NetworkCredential(string.Empty, secureString).Password; + + [ExcludeFromCodeCoverage] + internal static string PromptForPassword(this ZipEntryFile entry, PSHost host) + { + host.UI.Write( + $"Encrypted entry '{entry.RelativePath}' in '{entry.Source}' requires a password.\n" + + "Tip: Use -Password to avoid this prompt in the future.\n" + + "Enter password: "); + + return host.UI.ReadLineAsSecureString().AsPlainText(); + } + + internal static void ReadToEnd(this StreamReader reader, PSCmdlet cmdlet) + => cmdlet.WriteObject(reader.ReadToEnd()); + + internal static void ReadLines(this StreamReader reader, PSCmdlet cmdlet) + { + string? line; + while ((line = reader.ReadLine()) is not null) + { + cmdlet.WriteObject(line); + } + } + + internal static void WriteLines(this StreamWriter writer, string[] lines) + { + foreach (string line in lines) + { + writer.WriteLine(line); + } + } + + internal static void WriteContent(this StreamWriter writer, string[] lines) + { + foreach (string line in lines) + { + writer.Write(line); + } + } +} diff --git a/src/PSCompression/Extensions/PathExtensions.cs b/src/PSCompression/Extensions/PathExtensions.cs index 1cb40fe..0b428ca 100644 --- a/src/PSCompression/Extensions/PathExtensions.cs +++ b/src/PSCompression/Extensions/PathExtensions.cs @@ -8,13 +8,22 @@ namespace PSCompression.Extensions; -public static class PathExtensions +public static partial class PathExtensions { +#if NETCOREAPP + [GeneratedRegex( + @"(?:^[a-z]:)?[\\/]+|(? path.EndsWith("/") || path.EndsWith("\\") @@ -64,11 +73,13 @@ internal static string AddExtensionIfMissing(this string path, string extension) return path; } - internal static string NormalizeEntryPath(this string path) => - s_reNormalize.Replace(path, _directorySeparator).TrimStart('/'); + internal static string NormalizeEntryPath(this string path) + => s_reNormalize + .Replace(path, DirectorySeparator) + .TrimStart('/'); - internal static string NormalizeFileEntryPath(this string path) => - NormalizeEntryPath(path).TrimEnd('/'); + internal static string NormalizeFileEntryPath(this string path) + => NormalizeEntryPath(path).TrimEnd('/'); internal static void Create(this DirectoryInfo dir, bool force) { @@ -92,10 +103,10 @@ internal static PSObject AppendPSProperties(this FileSystemInfo info) internal static PSObject AppendPSProperties(this FileSystemInfo info, string? parent) { - const string provider = @"Microsoft.PowerShell.Core\FileSystem::"; + const string Provider = @"Microsoft.PowerShell.Core\FileSystem::"; PSObject pso = PSObject.AsPSObject(info); - pso.Properties.Add(new PSNoteProperty("PSPath", $"{provider}{info.FullName}")); - pso.Properties.Add(new PSNoteProperty("PSParentPath", $"{provider}{parent}")); + pso.Properties.Add(new PSNoteProperty("PSPath", $"{Provider}{info.FullName}")); + pso.Properties.Add(new PSNoteProperty("PSParentPath", $"{Provider}{parent}")); return pso; } } diff --git a/src/PSCompression/SortingOps.cs b/src/PSCompression/SortingOps.cs index ae1d6ae..c846a0f 100644 --- a/src/PSCompression/SortingOps.cs +++ b/src/PSCompression/SortingOps.cs @@ -14,11 +14,9 @@ internal static class SortingOps private static int SortByLength(EntryBase entry) => entry.RelativePath.Count(e => e == '/'); - private static string SortByName(EntryBase entry) => - entry.Name; + private static string SortByName(EntryBase entry) => entry.Name!; - private static EntryType SortByType(EntryBase entry) => - entry.Type; + private static EntryType SortByType(EntryBase entry) => entry.Type; internal static IEnumerable ToEntrySort( this IEnumerable entryCollection) diff --git a/src/PSCompression/TarEntryFile.cs b/src/PSCompression/TarEntryFile.cs index fa847d3..063fc64 100644 --- a/src/PSCompression/TarEntryFile.cs +++ b/src/PSCompression/TarEntryFile.cs @@ -11,21 +11,27 @@ public sealed class TarEntryFile : TarEntryBase { private readonly Algorithm _algorithm; - public string BaseName => Path.GetFileNameWithoutExtension(Name); + public string BaseName { get; } - public string Extension => Path.GetExtension(RelativePath); + public string Extension { get; } public override EntryType Type => EntryType.Archive; internal TarEntryFile(TarEntry entry, string source, Algorithm algorithm) : base(entry, source) { + Name = Path.GetFileName(entry.Name); + BaseName = Path.GetFileNameWithoutExtension(Name); + Extension = Path.GetExtension(RelativePath); _algorithm = algorithm; } internal TarEntryFile(TarEntry entry, Stream? stream, Algorithm algorithm) : base(entry, stream) { + Name = Path.GetFileName(entry.Name); + BaseName = Path.GetFileNameWithoutExtension(Name); + Extension = Path.GetExtension(RelativePath); _algorithm = algorithm; } diff --git a/src/PSCompression/ZipArchiveCache.cs b/src/PSCompression/ZipArchiveCache.cs index 4cfd7bb..6fd4d2f 100644 --- a/src/PSCompression/ZipArchiveCache.cs +++ b/src/PSCompression/ZipArchiveCache.cs @@ -1,39 +1,37 @@ using System; using System.Collections.Generic; -using System.IO.Compression; using PSCompression.Abstractions; namespace PSCompression; -internal sealed class ZipArchiveCache : IDisposable +internal sealed class ZipArchiveCache : IDisposable + where TArchive : IDisposable { - private readonly Dictionary _cache; + private readonly Dictionary _cache; - private readonly ZipArchiveMode _mode = ZipArchiveMode.Read; + private readonly Func _factory; - internal ZipArchiveCache() => _cache = []; - - internal ZipArchiveCache(ZipArchiveMode mode) + internal ZipArchiveCache(Func factory) { - _cache = []; - _mode = mode; + _cache = new(StringComparer.OrdinalIgnoreCase); + _factory = factory; } - internal ZipArchive this[string source] => _cache[source]; + internal TArchive this[string source] => _cache[source]; internal void TryAdd(ZipEntryBase entry) { if (!_cache.ContainsKey(entry.Source)) { - _cache[entry.Source] = entry.OpenZip(_mode); + _cache[entry.Source] = _factory(entry); } } - internal ZipArchive GetOrAdd(ZipEntryBase entry) + internal TArchive GetOrCreate(ZipEntryBase entry) { if (!_cache.ContainsKey(entry.Source)) { - _cache[entry.Source] = entry.OpenZip(_mode); + _cache[entry.Source] = _factory(entry); } return _cache[entry.Source]; @@ -41,7 +39,7 @@ internal ZipArchive GetOrAdd(ZipEntryBase entry) public void Dispose() { - foreach (ZipArchive zip in _cache.Values) + foreach (TArchive zip in _cache.Values) { zip?.Dispose(); } diff --git a/src/PSCompression/ZipContentReader.cs b/src/PSCompression/ZipContentReader.cs deleted file mode 100644 index 500594a..0000000 --- a/src/PSCompression/ZipContentReader.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.IO; -using System.IO.Compression; -using System.Management.Automation; -using System.Text; -using PSCompression.Abstractions; -using PSCompression.Extensions; - -namespace PSCompression; - -internal sealed class ZipContentReader : ZipContentOpsBase -{ - internal ZipContentReader(ZipArchive zip) : base(zip) - { } - - internal void StreamBytes( - ZipEntryFile entry, - int bufferSize, - PSCmdlet cmdlet) - { - int bytes; - using Stream entryStream = entry.Open(_zip); - _buffer ??= new byte[bufferSize]; - - while ((bytes = entryStream.Read(_buffer, 0, bufferSize)) > 0) - { - for (int i = 0; i < bytes; i++) - { - cmdlet.WriteObject(_buffer[i]); - } - } - } - - internal void ReadAllBytes(ZipEntryFile entry, PSCmdlet cmdlet) - { - using Stream entryStream = entry.Open(_zip); - using MemoryStream mem = new(); - - entryStream.CopyTo(mem); - cmdlet.WriteObject(mem.ToArray()); - } - - internal void StreamLines( - ZipEntryFile entry, - Encoding encoding, - PSCmdlet cmdlet) - { - using Stream entryStream = entry.Open(_zip); - using StreamReader reader = new(entryStream, encoding); - reader.WriteLinesToPipeline(cmdlet); - } - - internal void ReadToEnd( - ZipEntryFile entry, - Encoding encoding, - PSCmdlet cmdlet) - { - using Stream entryStream = entry.Open(_zip); - using StreamReader reader = new(entryStream, encoding); - reader.WriteAllTextToPipeline(cmdlet); - } -} diff --git a/src/PSCompression/ZipContentWriter.cs b/src/PSCompression/ZipContentWriter.cs deleted file mode 100644 index 9a418f7..0000000 --- a/src/PSCompression/ZipContentWriter.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.IO; -using System.IO.Compression; -using System.Text; -using PSCompression.Abstractions; - -namespace PSCompression; - -internal sealed class ZipContentWriter : ZipContentOpsBase -{ - private int _index; - - private readonly StreamWriter? _writer; - - private readonly Stream _stream; - - internal ZipContentWriter(ZipEntryFile entry, bool append, int bufferSize) - : base(entry.OpenWrite()) - { - _stream = entry.Open(_zip); - _buffer = new byte[bufferSize]; - - if (append) - { - _stream.Seek(0, SeekOrigin.End); - return; - } - - _stream.SetLength(0); - } - - internal ZipContentWriter(ZipArchive zip, ZipArchiveEntry entry, Encoding encoding) - : base(zip) - { - _stream = entry.Open(); - _writer = new StreamWriter(_stream, encoding); - } - - internal ZipContentWriter(ZipEntryFile entry, bool append, Encoding encoding) - : base(entry.OpenWrite()) - { - _stream = entry.Open(_zip); - _writer = new StreamWriter(_stream, encoding); - - if (append) - { - _writer.BaseStream.Seek(0, SeekOrigin.End); - return; - } - - _writer.BaseStream.SetLength(0); - } - - internal void WriteLines(string[] lines) - { - if (_writer is null) - { - return; - } - - foreach (string line in lines) - { - _writer.WriteLine(line); - } - } - - internal void WriteBytes(byte[] bytes) - { - if (_buffer is null) - { - return; - } - - foreach (byte b in bytes) - { - if (_index == _buffer.Length) - { - _stream.Write(_buffer, 0, _index); - _index = 0; - } - - _buffer[_index++] = b; - } - } - - public void Flush() - { - if (_index > 0 && _buffer is not null) - { - _stream.Write(_buffer, 0, _index); - _index = 0; - _stream.Flush(); - } - - if (_writer is { BaseStream.CanWrite: true }) - { - _writer.Flush(); - } - } - - public void Close() - { - if (_writer is not null) - { - _writer.Close(); - return; - } - - _stream.Close(); - } - - protected override void Dispose(bool disposing) - { - try - { - if (disposing && !_disposed) - { - Flush(); - } - } - finally - { - _writer?.Dispose(); - _stream.Dispose(); - base.Dispose(disposing); - } - } -} diff --git a/src/PSCompression/ZipEntryByteWriter.cs b/src/PSCompression/ZipEntryByteWriter.cs new file mode 100644 index 0000000..58725c8 --- /dev/null +++ b/src/PSCompression/ZipEntryByteWriter.cs @@ -0,0 +1,61 @@ +using System.IO; +using PSCompression.Abstractions; + +namespace PSCompression; + +internal sealed class ZipEntryByteWriter : EntryStreamOpsBase +{ + private readonly byte[] _buffer; + + private readonly int _bufferSize; + + private int _index; + + internal ZipEntryByteWriter(Stream stream, int bufferSize, bool append = false) + : base(stream) + { + _buffer = new byte[bufferSize]; + _bufferSize = bufferSize; + + if (append) + { + Stream.Seek(0, SeekOrigin.End); + return; + } + + Stream.SetLength(0); + } + + internal void WriteBytes(byte[] bytes) + { + foreach (byte b in bytes) + { + if (_index == _bufferSize) + { + Stream.Write(_buffer, 0, _index); + _index = 0; + } + + _buffer[_index++] = b; + } + } + + private void Flush() + { + if (_index > 0) + { + Stream.Write(_buffer, 0, _index); + Stream.Flush(); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Flush(); + } + + base.Dispose(disposing); + } +} diff --git a/src/PSCompression/ZipEntryCache.cs b/src/PSCompression/ZipEntryCache.cs index 3c14c7a..798e60d 100644 --- a/src/PSCompression/ZipEntryCache.cs +++ b/src/PSCompression/ZipEntryCache.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.IO.Compression; +using ICSharpCode.SharpZipLib.Zip; using PSCompression.Abstractions; using PSCompression.Extensions; @@ -39,15 +39,15 @@ internal IEnumerable GetEntries() { foreach (var entry in _cache) { - using ZipArchive zip = ZipFile.OpenRead(entry.Key); + using ZipFile zip = new(entry.Key); foreach ((string path, EntryType type) in entry.Value) { - if (!zip.TryGetEntry(path, out ZipArchiveEntry? zipEntry)) + if (!zip.TryGetEntry(path, out ZipEntry? zipEntry)) { continue; } - if (type is EntryType.Archive) + if (type == EntryType.Archive) { yield return new ZipEntryFile(zipEntry, entry.Key); continue; diff --git a/src/PSCompression/ZipEntryDirectory.cs b/src/PSCompression/ZipEntryDirectory.cs index d70b21d..4cded83 100644 --- a/src/PSCompression/ZipEntryDirectory.cs +++ b/src/PSCompression/ZipEntryDirectory.cs @@ -3,6 +3,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using ICSharpCode.SharpZipLib.Zip; using PSCompression.Abstractions; using PSCompression.Extensions; @@ -10,17 +11,17 @@ namespace PSCompression; public sealed class ZipEntryDirectory : ZipEntryBase { - private const StringComparison _comparer = StringComparison.InvariantCultureIgnoreCase; + private const StringComparison Comparer = StringComparison.InvariantCultureIgnoreCase; public override EntryType Type => EntryType.Directory; - internal ZipEntryDirectory(ZipArchiveEntry entry, string source) + internal ZipEntryDirectory(ZipEntry entry, string source) : base(entry, source) { Name = entry.GetDirectoryName(); } - internal ZipEntryDirectory(ZipArchiveEntry entry, Stream? stream) + internal ZipEntryDirectory(ZipEntry entry, Stream? stream) : base(entry, stream) { Name = entry.GetDirectoryName(); @@ -28,8 +29,8 @@ internal ZipEntryDirectory(ZipArchiveEntry entry, Stream? stream) internal IEnumerable GetChilds(ZipArchive zip) => zip.Entries.Where(e => - !string.Equals(e.FullName, RelativePath, _comparer) - && e.FullName.StartsWith(RelativePath, _comparer)); + !string.Equals(e.FullName, RelativePath, Comparer) + && e.FullName.StartsWith(RelativePath, Comparer)); protected override string GetFormatDirectoryPath() => $"/{RelativePath.NormalizeEntryPath()}"; diff --git a/src/PSCompression/ZipEntryFile.cs b/src/PSCompression/ZipEntryFile.cs index f322517..a4f383e 100644 --- a/src/PSCompression/ZipEntryFile.cs +++ b/src/PSCompression/ZipEntryFile.cs @@ -1,5 +1,6 @@ using System.IO; using System.IO.Compression; +using ICSharpCode.SharpZipLib.Zip; using PSCompression.Abstractions; using PSCompression.Exceptions; using PSCompression.Extensions; @@ -8,21 +9,31 @@ namespace PSCompression; public sealed class ZipEntryFile : ZipEntryBase { - public string CompressionRatio => GetRatio(Length, CompressedLength); + public string CompressionRatio { get; } - public override EntryType Type => EntryType.Archive; + public override EntryType Type { get => EntryType.Archive; } - public string BaseName => Path.GetFileNameWithoutExtension(Name); + public string BaseName { get; } - public string Extension => Path.GetExtension(RelativePath); + public string Extension { get; } - internal ZipEntryFile(ZipArchiveEntry entry, string source) + internal ZipEntryFile(ZipEntry entry, string source) : base(entry, source) - { } + { + CompressionRatio = GetRatio(Length, CompressedLength); + Name = Path.GetFileName(entry.Name); + BaseName = Path.GetFileNameWithoutExtension(Name); + Extension = Path.GetExtension(RelativePath); + } - internal ZipEntryFile(ZipArchiveEntry entry, Stream? stream) + internal ZipEntryFile(ZipEntry entry, Stream? stream) : base(entry, stream) - { } + { + CompressionRatio = GetRatio(Length, CompressedLength); + Name = Path.GetFileName(entry.Name); + BaseName = Path.GetFileNameWithoutExtension(Name); + Extension = Path.GetExtension(RelativePath); + } private static string GetRatio(long size, long compressedSize) { @@ -41,11 +52,21 @@ internal Stream Open(ZipArchive zip) zip.ThrowIfNotFound( path: RelativePath, source: Source, - out ZipArchiveEntry entry); + out ZipArchiveEntry? entry); return entry.Open(); } + internal Stream Open(ICSharpCode.SharpZipLib.Zip.ZipFile zip) + { + zip.ThrowIfNotFound( + path: RelativePath, + source: Source, + out ZipEntry? entry); + + return zip.GetInputStream(entry); + } + internal void Refresh() { this.ThrowIfFromStream(); diff --git a/src/PSCompression/ZipEntryMoveCache.cs b/src/PSCompression/ZipEntryMoveCache.cs index 158800b..07ca5e3 100644 --- a/src/PSCompression/ZipEntryMoveCache.cs +++ b/src/PSCompression/ZipEntryMoveCache.cs @@ -53,7 +53,7 @@ internal void AddEntry(ZipEntryBase entry, string newname) => } internal Dictionary> GetMappings( - ZipArchiveCache cache) + ZipArchiveCache cache) { foreach (var source in _cache) { @@ -64,7 +64,7 @@ internal Dictionary> GetMappings( } private Dictionary GetChildMappings( - ZipArchiveCache cache, + ZipArchiveCache cache, Dictionary pathChanges) { string newpath; diff --git a/tests/ArchiveEntryManagementCommands.tests.ps1 b/tests/ArchiveEntryManagementCommands.tests.ps1 index 614ce13..8870e74 100644 --- a/tests/ArchiveEntryManagementCommands.tests.ps1 +++ b/tests/ArchiveEntryManagementCommands.tests.ps1 @@ -32,7 +32,8 @@ Describe 'Archive Entry Management Commands' { Compress-TarArchive @compressTarArchiveSplat } - $zip, $file, $uri, $tarArchives, $itemCounts, $totalCount | Out-Null + $encryptedZip = Get-Item $PSScriptRoot/../assets/helloworld.zip + $zip, $file, $uri, $tarArchives, $itemCounts, $totalCount, $encryptedZip | Out-Null } Context 'New-ZipEntry' -Tag 'New-ZipEntry' { @@ -356,6 +357,20 @@ Describe 'Archive Entry Management Commands' { { $zip | Get-ZipEntry -Type Directory | Get-ZipEntry } | Should -Throw } + + It 'Can read encrypted entries' { + $passw = ConvertTo-SecureString 'test' -AsPlainText -Force + + Use-Object ($stream = $encryptedZip.OpenRead()) { + $stream | Get-ZipEntry -Type Archive | + Get-ZipEntryContent -Password $passw | + Should -BeExactly 'hello world!' + } + + $encryptedZip | Get-ZipEntry -Type Archive | + Get-ZipEntryContent -Password $passw | + Should -BeExactly 'hello world!' + } } Context 'Get-TarEntryContent' -Tag 'Get-TarEntryContent' { @@ -680,6 +695,26 @@ Describe 'Archive Entry Management Commands' { Get-ChildItem -LiteralPath $destination -Recurse -File | ForEach-Object { $_ | Get-Content | Should -BeExactly $content } } + + It 'Can extract an encrypted entry' { + $passw = ConvertTo-SecureString 'test' -AsPlainText -Force + $dest = Join-Path $TestDrive encryptedTestFolder + Use-Object ($stream = $encryptedZip.OpenRead()) { + $info = $stream | Get-ZipEntry -Type Archive | + Expand-ZipEntry -Password $passw -Destination $dest -PassThru + + $info | Should -BeOfType ([FileInfo]) + Get-Content $info.FullName | Should -BeExactly 'hello world!' + $info.Delete() + } + + $info = $encryptedZip | Get-ZipEntry | + Expand-ZipEntry -Password $passw -Destination $dest -PassThru + + $info | Should -BeOfType ([FileInfo]) + Get-Content $info.FullName | Should -BeExactly 'hello world!' + $info.Delete() + } } Context 'Expand-TarEntry' -Tag 'Expand-TarEntry' { diff --git a/tests/FileEntryTypes.tests.ps1 b/tests/FileEntryTypes.tests.ps1 index e28eca7..720aecd 100644 --- a/tests/FileEntryTypes.tests.ps1 +++ b/tests/FileEntryTypes.tests.ps1 @@ -42,6 +42,25 @@ Describe 'File Entry Types' { ($tarArchive | Get-TarEntry).Extension | Should -BeExactly .txt } + It 'Should Have an IsEncrypted Property' { + ($zip | Get-ZipEntry).IsEncrypted | Should -BeOfType ([bool]) + ($zip | Get-ZipEntry).IsEncrypted | Should -BeFalse + } + + It 'Should Have an AESKeySize Property' { + ($zip | Get-ZipEntry).AESKeySize | Should -BeOfType ([int]) + ($zip | Get-ZipEntry).AESKeySize | Should -BeExactly 0 + } + + It 'Should Have a CompressionMethod Property' { + ($zip | Get-ZipEntry).CompressionMethod | Should -Be Deflated + } + + It 'Should Have a Comment Property' { + ($zip | Get-ZipEntry).Comment | Should -BeOfType ([string]) + ($zip | Get-ZipEntry).Comment | Should -BeExactly '' + } + It 'Should Open the source zip' { Use-Object ($stream = ($zip | Get-ZipEntry).OpenRead()) { $stream | Should -BeOfType ([ZipArchive]) diff --git a/tests/ZipEntryBase.tests.ps1 b/tests/ZipEntryBase.tests.ps1 index c23aac3..2da3da8 100644 --- a/tests/ZipEntryBase.tests.ps1 +++ b/tests/ZipEntryBase.tests.ps1 @@ -14,6 +14,8 @@ Describe 'ZipEntryBase Class' { $zip = New-Item (Join-Path $TestDrive test.zip) -ItemType File -Force 'hello world!' | New-ZipEntry $zip.FullName -EntryPath helloworld.txt New-ZipEntry $zip.FullName -EntryPath somefolder/ + $encryptedZip = Get-Item $PSScriptRoot/../assets/helloworld.zip + $encryptedZip | Out-Null } It 'Can extract an entry' { @@ -39,6 +41,22 @@ Describe 'ZipEntryBase Class' { $file.FullName | Should -BeExactly ([Path]::Combine($TestDrive, 'myTestFolder', $entry.Name)) } + It 'Can extract an encrypted entry' { + $passw = ConvertTo-SecureString 'test' -AsPlainText -Force + $dest = Join-Path $TestDrive encryptedTestFolder + Use-Object ($stream = $encryptedZip.OpenRead()) { + $info = ($stream | Get-ZipEntry -Type Archive).ExtractTo($dest, $false, $passw) + $info | Should -BeOfType ([FileInfo]) + Get-Content $info.FullName | Should -BeExactly 'hello world!' + $info.Delete() + } + + $info = ($encryptedZip | Get-ZipEntry).ExtractTo($dest, $false, $passw) + $info | Should -BeOfType ([FileInfo]) + Get-Content $info.FullName | Should -BeExactly 'hello world!' + $info.Delete() + } + It 'Can extract folders' { ($zip | Get-ZipEntry -Type Directory).ExtractTo($TestDrive, $false) | Should -BeOfType ([DirectoryInfo]) @@ -58,6 +76,10 @@ Describe 'ZipEntryBase Class' { ($zip | Get-ZipEntry).CompressionRatio | Should -BeOfType ([string]) } + It 'Has a Crc Property' { + ($zip | Get-ZipEntry).Crc | Should -BeOfType ([long]) + } + It 'Can remove an entry in the source zip' { { $zip | Get-ZipEntry | ForEach-Object Remove } | Should -Not -Throw @@ -98,4 +120,12 @@ Describe 'ZipEntryBase Class' { } | Should -Throw } } + + It 'Should throw if the entry to extract no longer exists in the source zip' { + $entry = New-ZipEntry $zip.FullName -EntryPath toberemoved.txt + $entry | Remove-ZipEntry + { + $entry.ExtractTo($TestDrive, $false) + } | Should -Throw + } }