mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-08 13:34:57 +00:00
Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03618a704b | ||
|
|
925842bc4b | ||
|
|
cead62704e | ||
|
|
cce97548a2 | ||
|
|
264aa6d366 | ||
|
|
a361d41e68 | ||
|
|
38766dac99 | ||
|
|
c30bc65281 | ||
|
|
296ebd942a | ||
|
|
afa19f7ad8 | ||
|
|
a193b2d3b1 | ||
|
|
be4a65e572 | ||
|
|
6832918e71 | ||
|
|
fd9a3ffbcc | ||
|
|
41added690 | ||
|
|
18641d4f9b | ||
|
|
4d0c5099d4 | ||
|
|
9d9d491245 | ||
|
|
7b81d18071 | ||
|
|
7d0acbc988 | ||
|
|
313c044c41 | ||
|
|
f6f8adf97e | ||
|
|
bc97d325ca | ||
|
|
0f2d325f20 | ||
|
|
63d5503e12 | ||
|
|
e53f2cac4a | ||
|
|
3b73464233 | ||
|
|
575f10f766 | ||
|
|
8d3fc3533b | ||
|
|
60370b8539 | ||
|
|
f6db114865 | ||
|
|
1c6c344b6b | ||
|
|
d0302898e0 | ||
|
|
057ac9b001 | ||
|
|
8be931bbcb | ||
|
|
3197ef289c | ||
|
|
631578c175 | ||
|
|
f1809163c7 | ||
|
|
60e1fe86f2 | ||
|
|
59d7de5bfc | ||
|
|
6e95c1d84a | ||
|
|
ee64670755 | ||
|
|
3f7d0f5b68 | ||
|
|
e3514c5c4b | ||
|
|
cc3a9cff88 | ||
|
|
15e821aa39 | ||
|
|
8dd1dbab5f | ||
|
|
65ce91ddf6 | ||
|
|
bf55595d6f | ||
|
|
2aa123ccd7 | ||
|
|
0990b06cc9 | ||
|
|
e05f9843ba | ||
|
|
683d2714d0 | ||
|
|
b8ef1ecafc | ||
|
|
467fc2d03d | ||
|
|
58b4fe4f28 | ||
|
|
97d5e0aac4 | ||
|
|
356c977cff | ||
|
|
99d6062376 | ||
|
|
f8538403e4 | ||
|
|
ba12019bc7 | ||
|
|
726b9c80f6 | ||
|
|
2894711c51 | ||
|
|
85280f6f4f | ||
|
|
d7f4c0ee32 | ||
|
|
1263c0d976 | ||
|
|
cd3cbd2b32 | ||
|
|
b3a4fed8be | ||
|
|
d0b4af6666 | ||
|
|
81ab5c189d | ||
|
|
6ef3be4b5c | ||
|
|
9f90a1d651 | ||
|
|
ce9a3fd1ef | ||
|
|
7c6f05058e | ||
|
|
a8c3a7439e | ||
|
|
839b3ab0cf | ||
|
|
44d54db80e | ||
|
|
a67d7bc429 | ||
|
|
079a818c6c | ||
|
|
6be6ef0b5c | ||
|
|
8e51d9d646 | ||
|
|
ea206f4f02 | ||
|
|
f175a2a252 | ||
|
|
3f7e559b86 | ||
|
|
2959b4d701 | ||
|
|
031286c5eb | ||
|
|
e181fa8c4a | ||
|
|
7b035bec5d | ||
|
|
f39d2bf53a | ||
|
|
7c8e407182 | ||
|
|
a09136d46b | ||
|
|
5fe1363ee1 | ||
|
|
b41823fc10 | ||
|
|
0a64fe28b0 | ||
|
|
e320ccfa9a | ||
|
|
9628ff9456 | ||
|
|
d540f78cfc | ||
|
|
66420cd299 | ||
|
|
dd0594471f | ||
|
|
844ba228ee | ||
|
|
7efc701b32 | ||
|
|
d7e29f7c4d | ||
|
|
f26ba91386 | ||
|
|
c73ac2039c | ||
|
|
671f9cd0cb | ||
|
|
131b5b9714 | ||
|
|
74af0889b9 | ||
|
|
e5ee399045 | ||
| deeb7a0f64 | |||
|
|
5af3bab1dc | ||
|
|
28be84d315 | ||
|
|
a0528c737d | ||
|
|
b506e488e8 | ||
|
|
58eb0e08d6 | ||
|
|
562701894f | ||
|
|
54a562273b | ||
|
|
3f8c9c4cb0 | ||
|
|
3e7d28b043 | ||
|
|
40b10d4a26 | ||
|
|
f367630a2a | ||
|
|
b9e4f00862 | ||
|
|
d6e74d6163 | ||
|
|
4a4522b842 | ||
|
|
710ba4423d | ||
|
|
2a5494a804 | ||
|
|
568909800c | ||
|
|
7513a608b1 | ||
|
|
911e9878bd | ||
|
|
899d7d6e61 | ||
|
|
260c0ee776 | ||
|
|
d71520808d | ||
|
|
177fc2a12c | ||
|
|
5dafcb02d4 | ||
|
|
c4fde80c5e | ||
|
|
06e3486ec4 | ||
|
|
bd7c783aaf | ||
|
|
d732e3cfa4 | ||
|
|
c24cdc66ed | ||
|
|
efa6f7a82e | ||
| ddbbc3b847 | |||
|
|
7037161c07 | ||
|
|
b0b62fcf91 | ||
|
|
bd8ba7b854 |
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,14 +1,16 @@
|
||||
**/bin/*
|
||||
**/obj/*
|
||||
_ReSharper.SharpCompress/
|
||||
bin/
|
||||
*.suo
|
||||
*.user
|
||||
TestArchives/Scratch/
|
||||
TestArchives/Scratch2/
|
||||
TestResults/
|
||||
*.nupkg
|
||||
packages/*/
|
||||
project.lock.json
|
||||
test/TestArchives/Scratch
|
||||
.vs
|
||||
**/bin/*
|
||||
**/obj/*
|
||||
_ReSharper.SharpCompress/
|
||||
bin/
|
||||
*.suo
|
||||
*.user
|
||||
TestArchives/Scratch/
|
||||
TestArchives/Scratch2/
|
||||
TestResults/
|
||||
*.nupkg
|
||||
packages/*/
|
||||
project.lock.json
|
||||
tests/TestArchives/Scratch
|
||||
.vs
|
||||
tools
|
||||
.vscode
|
||||
|
||||
10
.travis.yml
Normal file
10
.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
dist: trusty
|
||||
language: csharp
|
||||
solution: SharpCompress.sln
|
||||
matrix:
|
||||
include:
|
||||
- dotnet: 1.0.4
|
||||
mono: none
|
||||
env: DOTNETCORE=1
|
||||
script:
|
||||
- ./build.sh
|
||||
36
FORMATS.md
Normal file
36
FORMATS.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Archive Formats
|
||||
|
||||
## Accessing Archives
|
||||
|
||||
Archive classes allow random access to a seekable stream.
|
||||
Reader classes allow forward-only reading
|
||||
Writer classes allow forward-only Writing
|
||||
|
||||
## Supported Format Table
|
||||
|
||||
| Archive Format | Compression Format(s) | Compress/Decompress | Archive API | Reader API | Writer API |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Rar | Rar | Decompress (1) | RarArchive | RarReader | N/A |
|
||||
| Zip (2) | None, DEFLATE, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter |
|
||||
| Tar | None, BZip2, GZip, LZip | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| GZip (single file) | GZip | Both | GZipArchive | GZipReader | GZipWriter |
|
||||
| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A |
|
||||
|
||||
1. SOLID Rars are only supported in the RarReader API.
|
||||
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading is supported.
|
||||
3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown.
|
||||
4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API
|
||||
|
||||
## Compressors
|
||||
|
||||
For those who want to directly compress/decompress bits
|
||||
|
||||
| Compressor | Compress/Decompress |
|
||||
| --- | --- |
|
||||
| BZip2Stream | Both |
|
||||
| GZipStream | Both |
|
||||
| DeflateStream | Both |
|
||||
| LZMAStream | Both |
|
||||
| PPMdStream | Both |
|
||||
| ADCStream | Decompress |
|
||||
| LZipStream | Decompress |
|
||||
75
README.md
75
README.md
@@ -1,13 +1,19 @@
|
||||
# SharpCompress
|
||||
|
||||
SharpCompress is a compression library for .NET/Mono/Silverlight/WP7 that can unrar, un7zip, unzip, untar unbzip2 and ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip are implemented.
|
||||
SharpCompress is a compression library in pure C# for .NET 3.5, 4.5, .NET Standard 1.0, 1.3 that can unrar, un7zip, unzip, untar unbzip2 and ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip are implemented.
|
||||
|
||||
The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream).
|
||||
|
||||
AppVeyor Build -
|
||||
[](https://ci.appveyor.com/project/adamhathcock/sharpcompress/branch/master)
|
||||
|
||||
Travis CI Build -
|
||||
[](https://travis-ci.org/adamhathcock/sharpcompress)
|
||||
|
||||
## Need Help?
|
||||
Post Issues on Github!
|
||||
|
||||
Check the [Supported Formats](https://github.com/adamhathcock/sharpcompress/wiki/Supported-Formats) and [basic samples.](https://github.com/adamhathcock/sharpcompress/wiki/API-Examples)
|
||||
Check the [Supported Formats](FORMATS.md) and [Basic Usage.](USAGE.md)
|
||||
|
||||
## A Simple Request
|
||||
|
||||
@@ -23,28 +29,77 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
|
||||
|
||||
* RAR 5 support
|
||||
* 7Zip writing
|
||||
* Zip64
|
||||
* Zip64 (Need writing and extend Reading)
|
||||
* Multi-volume Zip support.
|
||||
|
||||
## In-Progress
|
||||
|
||||
* RAR5 support
|
||||
* DNX/NET Core support
|
||||
* xproj targeting
|
||||
|
||||
## Version Log
|
||||
|
||||
### Version 0.16.1
|
||||
|
||||
* Fix [Preserve compression method when getting a compressed stream](https://github.com/adamhathcock/sharpcompress/pull/235)
|
||||
* Fix [RAR entry key normalization fix](https://github.com/adamhathcock/sharpcompress/issues/201)
|
||||
|
||||
### Version 0.16.0
|
||||
|
||||
* Breaking - [Progress Event Tracking rethink](https://github.com/adamhathcock/sharpcompress/pull/226)
|
||||
* Update to VS2017 - [VS2017](https://github.com/adamhathcock/sharpcompress/pull/231) - Framework targets have been changed.
|
||||
* New - [Add Zip64 writing](https://github.com/adamhathcock/sharpcompress/pull/211)
|
||||
* [Fix invalid/mismatching Zip version flags.](https://github.com/adamhathcock/sharpcompress/issues/164) - This allows nuget/System.IO.Packaging to read zip files generated by SharpCompress
|
||||
* [Fix 7Zip directory hiding](https://github.com/adamhathcock/sharpcompress/pull/215/files)
|
||||
* [Verify RAR CRC headers](https://github.com/adamhathcock/sharpcompress/pull/220)
|
||||
|
||||
### Version 0.15.2
|
||||
|
||||
* [Fix invalid headers](https://github.com/adamhathcock/sharpcompress/pull/210) - fixes an issue creating large-ish zip archives that was introduced with zip64 reading.
|
||||
|
||||
### Version 0.15.1
|
||||
|
||||
* [Zip64 extending information and ZipReader](https://github.com/adamhathcock/sharpcompress/pull/206)
|
||||
|
||||
### Version 0.15.0
|
||||
|
||||
* [Add zip64 support for ZipArchive extraction](https://github.com/adamhathcock/sharpcompress/pull/205)
|
||||
|
||||
### Version 0.14.1
|
||||
|
||||
* [.NET Assemblies aren't strong named](https://github.com/adamhathcock/sharpcompress/issues/158)
|
||||
* [Pkware encryption for Zip files didn't allow for multiple reads of an entry](https://github.com/adamhathcock/sharpcompress/issues/197)
|
||||
* [GZip Entry couldn't be read multiple times](https://github.com/adamhathcock/sharpcompress/issues/198)
|
||||
|
||||
### Version 0.14.0
|
||||
|
||||
* [Support for LZip reading in for Tars](https://github.com/adamhathcock/sharpcompress/pull/191)
|
||||
|
||||
### Version 0.13.1
|
||||
|
||||
* [Fix null password on ReaderFactory. Fix null options on SevenZipArchive](https://github.com/adamhathcock/sharpcompress/pull/188)
|
||||
* [Make PpmdProperties lazy to avoid unnecessary allocations.](https://github.com/adamhathcock/sharpcompress/pull/185)
|
||||
|
||||
### Version 0.13.0
|
||||
|
||||
* Breaking change: Big refactor of Options on API.
|
||||
* 7Zip supports Deflate
|
||||
|
||||
### Version 0.12.4
|
||||
|
||||
* Forward only zip issue fix https://github.com/adamhathcock/sharpcompress/issues/160
|
||||
* Try to fix frameworks again by copying targets from JSON.NET
|
||||
|
||||
### Version 0.12.3
|
||||
|
||||
* 7Zip fixes https://github.com/adamhathcock/sharpcompress/issues/73
|
||||
* Maybe all profiles will work with project.json now
|
||||
|
||||
### Version 0.12.2
|
||||
|
||||
* Support Profile 259 again
|
||||
|
||||
### Version 0.12.1
|
||||
|
||||
* Support Silverlight 5
|
||||
|
||||
### Version 0.12.0
|
||||
|
||||
* .NET Core RTM!
|
||||
* Bug fix for Tar long paths
|
||||
|
||||
@@ -97,8 +152,6 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
|
||||
* Embedded some BouncyCastle crypto classes to allow RAR Decryption and Winzip AES Decryption in Portable and Windows Store DLLs
|
||||
* Built in Release (I think)
|
||||
|
||||
Some Help/Discussion: https://sharpcompress.codeplex.com/discussions
|
||||
|
||||
7Zip implementation based on: https://code.google.com/p/managed-lzma/
|
||||
|
||||
LICENSE
|
||||
|
||||
@@ -1,44 +1,38 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F18F1765-4A02-42FD-9BEF-F0E2FCBD9D17}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
global.json = global.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress", "src\SharpCompress\SharpCompress.xproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress.Test", "test\SharpCompress.Test\SharpCompress.Test.xproj", "{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C}
|
||||
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26430.6
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F18F1765-4A02-42FD-9BEF-F0E2FCBD9D17}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpCompress", "src\SharpCompress\SharpCompress.csproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpCompress.Test", "tests\SharpCompress.Test\SharpCompress.Test.csproj", "{F2B1A1EB-0FA6-40D0-8908-E13247C7226F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C}
|
||||
{F2B1A1EB-0FA6-40D0-8908-E13247C7226F} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,6 +1,119 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=181069325DAB1C4287CD564D6CDDEDB3/AbsolutePath/@EntryValue">D:\Git\sharpcompress\SharpCompress\sharpcompress.DotSettings</s:String>
|
||||
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=181069325DAB1C4287CD564D6CDDEDB3/RelativePath/@EntryValue">..\SharpCompress\sharpcompress.DotSettings</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=181069325DAB1C4287CD564D6CDDEDB3/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=File181069325DAB1C4287CD564D6CDDEDB3/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Double x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=File181069325DAB1C4287CD564D6CDDEDB3/RelativePriority/@EntryValue">1</s:Double></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArgumentsStyleNamedExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Fdowhile/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Ffixed/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Ffor/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Fforeach/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Fifelse/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Flock/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Fusing/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeBraces_005Fwhile/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodSupportsCancellation/@EntryIndexedValue">ERROR</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantExplicitParamsArrayCreation/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Basic_0020Clean/@EntryIndexedValue"><?xml version="1.0" encoding="utf-16"?><Profile name="Basic Clean"><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="False" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="True" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSReformatCode>True</CSReformatCode></Profile></s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">Basic Clean</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/APPLY_ON_COMPLETION/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ARGUMENTS_NAMED/@EntryValue">Named</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_FIRST_ARG_BY_PAREN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_LINQ_QUERY/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_ARGUMENT/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_ARRAY_AND_OBJECT_INITIALIZER/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXPRESSION/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_FOR_STMT/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTIPLE_DECLARATION/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_CONSTRAINS/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_START_COMMENT/@EntryValue">0</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_SINGLE_LINE_COMMENT/@EntryValue">1</s:Int64>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_ATTRIBUTE_STYLE/@EntryValue">SEPARATE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FIXED_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FOR_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FOREACH_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_IFELSE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_USING_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_WHILE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_ANONYMOUS_METHOD_BLOCK/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CONSTRUCTOR_INITIALIZER_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSORHOLDER_ON_SINGLE_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">True</s:Boolean>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_ARROW_OP/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_MULTIPLICATIVE_OP/@EntryValue">True</s:Boolean>
|
||||
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_SIZEOF_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_TYPEOF_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCONSTRUCTOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FGLOBAL_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLABEL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FCONSTRUCTOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FOBJECT_005FPROPERTY_005FOF_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FPARAMETER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM_005FMEMBER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FINTERFACE/@EntryIndexedValue"><Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FEXPORTED/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FLOCAL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FPARAMETER/@EntryIndexedValue"><Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FHTML_005FCONTROL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FNAME/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FPREFIX/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
||||
BIN
SharpCompress.snk
Normal file
BIN
SharpCompress.snk
Normal file
Binary file not shown.
131
USAGE.md
Normal file
131
USAGE.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# SharpCompress Usage
|
||||
|
||||
## Stream Rules
|
||||
When dealing with Streams, the rule should be that you don't close a stream you didn't create. This, in effect, should mean you should always put a Stream in a using block to dispose it.
|
||||
|
||||
However, the .NET Framework often has classes that will dispose streams by default to make things "easy" like the following:
|
||||
|
||||
```C#
|
||||
using (var reader = new StreamReader(File.Open("foo")))
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
In this example, reader should get disposed. However, stream rules should say the the `FileStream` created by `File.Open` should remain open. However, the .NET Framework closes it for you by default unless you override the constructor. In general, you should be writing Stream code like this:
|
||||
|
||||
```C#
|
||||
using (var fileStream = File.Open("foo"))
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
To deal with the "correct" rules as well as the expectations of users, I've decided on this:
|
||||
|
||||
* When writing, leave streams open.
|
||||
* When reading, close streams
|
||||
|
||||
To be explicit though, consider always using the overloads that use `ReaderOptions` or `WriterOptions` and explicitly set `LeaveStreamOpen` the way you want.
|
||||
|
||||
## Samples
|
||||
|
||||
Also, look over the tests for more thorough [examples](https://github.com/adamhathcock/sharpcompress/tree/master/test/SharpCompress.Test)
|
||||
|
||||
### Create Zip Archive from all files in a directory to a file
|
||||
|
||||
```C#
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
archive.AddAllFromDirectory("D:\\temp");
|
||||
archive.SaveTo("C:\\temp.zip", CompressionType.Deflate);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Create Zip Archive from all files in a directory and save in memory
|
||||
|
||||
```C#
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
archive.AddAllFromDirectory("D:\\temp");
|
||||
archive.SaveTo(memoryStream, new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
LeaveStreamOpen = true
|
||||
});
|
||||
}
|
||||
//reset memoryStream to be usable now
|
||||
memoryStream.Position = 0;
|
||||
```
|
||||
|
||||
### Extract all files from a Rar file to a directory using RarArchive
|
||||
|
||||
```C#
|
||||
using (var archive = RarArchive.Open("Test.rar"))
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
entry.WriteToDirectory("D:\\temp", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use ReaderFactory to autodetect archive type and Open the entry stream
|
||||
|
||||
```C#
|
||||
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
|
||||
using (var reader = ReaderFactory.Open(stream))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
Console.WriteLine(reader.Entry.Key);
|
||||
reader.WriteEntryToDirectory(@"C:\temp", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use ReaderFactory to autodetect archive type and Open the entry stream
|
||||
|
||||
```C#
|
||||
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
|
||||
using (var reader = ReaderFactory.Open(stream))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
using (var entryStream = reader.OpenEntryStream())
|
||||
{
|
||||
entryStream.CopyTo(...);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use WriterFactory to write all files from a directory in a streaming manner.
|
||||
|
||||
```C#
|
||||
using (Stream stream = File.OpenWrite("C:\\temp.tgz"))
|
||||
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)
|
||||
{
|
||||
LeaveOpenStream = true
|
||||
}))
|
||||
{
|
||||
writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories);
|
||||
}
|
||||
```
|
||||
20
appveyor.yml
Normal file
20
appveyor.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: '{build}'
|
||||
image: Visual Studio 2017
|
||||
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
nuget:
|
||||
disable_publish_on_pr: true
|
||||
|
||||
build_script:
|
||||
- ps: .\build.ps1
|
||||
|
||||
test: off
|
||||
|
||||
artifacts:
|
||||
- path: src\SharpCompress\bin\Release\*.nupkg
|
||||
93
build.cake
Normal file
93
build.cake
Normal file
@@ -0,0 +1,93 @@
|
||||
var target = Argument("target", "Default");
|
||||
var tag = Argument("tag", "cake");
|
||||
|
||||
Task("Restore")
|
||||
.Does(() =>
|
||||
{
|
||||
DotNetCoreRestore(".");
|
||||
});
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Restore")
|
||||
.Does(() =>
|
||||
{
|
||||
if (IsRunningOnWindows())
|
||||
{
|
||||
MSBuild("./sharpcompress.sln", c =>
|
||||
{
|
||||
c.SetConfiguration("Release")
|
||||
.SetVerbosity(Verbosity.Minimal)
|
||||
.UseToolVersion(MSBuildToolVersion.VS2017);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var settings = new DotNetCoreBuildSettings
|
||||
{
|
||||
Framework = "netstandard1.0",
|
||||
Configuration = "Release"
|
||||
};
|
||||
|
||||
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
|
||||
|
||||
settings.Framework = "netcoreapp1.1";
|
||||
DotNetCoreBuild("./tests/SharpCompress.Test/SharpCompress.Test.csproj", settings);
|
||||
}
|
||||
});
|
||||
|
||||
Task("Test")
|
||||
.IsDependentOn("Build")
|
||||
.Does(() =>
|
||||
{
|
||||
if (!bool.Parse(EnvironmentVariable("APPVEYOR") ?? "false")
|
||||
&& !bool.Parse(EnvironmentVariable("TRAVIS") ?? "false"))
|
||||
{
|
||||
var files = GetFiles("tests/**/*.csproj");
|
||||
foreach(var file in files)
|
||||
{
|
||||
var settings = new DotNetCoreTestSettings
|
||||
{
|
||||
Configuration = "Release"
|
||||
};
|
||||
|
||||
DotNetCoreTest(file.ToString(), settings);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Information("Skipping tests as this is AppVeyor or Travis CI");
|
||||
}
|
||||
});
|
||||
|
||||
Task("Pack")
|
||||
.IsDependentOn("Build")
|
||||
.Does(() =>
|
||||
{
|
||||
if (IsRunningOnWindows())
|
||||
{
|
||||
MSBuild("src/SharpCompress/SharpCompress.csproj", c => c
|
||||
.SetConfiguration("Release")
|
||||
.SetVerbosity(Verbosity.Minimal)
|
||||
.UseToolVersion(MSBuildToolVersion.VS2017)
|
||||
.WithProperty("NoBuild", "true")
|
||||
.WithTarget("Pack"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Information("Skipping Pack as this is not Windows");
|
||||
}
|
||||
});
|
||||
|
||||
Task("Default")
|
||||
.IsDependentOn("Restore")
|
||||
.IsDependentOn("Build")
|
||||
.IsDependentOn("Test")
|
||||
.IsDependentOn("Pack");
|
||||
|
||||
Task("RunTests")
|
||||
.IsDependentOn("Restore")
|
||||
.IsDependentOn("Build")
|
||||
.IsDependentOn("Test");
|
||||
|
||||
|
||||
RunTarget(target);
|
||||
228
build.ps1
Normal file
228
build.ps1
Normal file
@@ -0,0 +1,228 @@
|
||||
##########################################################################
|
||||
# This is the Cake bootstrapper script for PowerShell.
|
||||
# This file was downloaded from https://github.com/cake-build/resources
|
||||
# Feel free to change this file to fit your needs.
|
||||
##########################################################################
|
||||
|
||||
<#
|
||||
|
||||
.SYNOPSIS
|
||||
This is a Powershell script to bootstrap a Cake build.
|
||||
|
||||
.DESCRIPTION
|
||||
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
|
||||
and execute your Cake build script with the parameters you provide.
|
||||
|
||||
.PARAMETER Script
|
||||
The build script to execute.
|
||||
.PARAMETER Target
|
||||
The build script target to run.
|
||||
.PARAMETER Configuration
|
||||
The build configuration to use.
|
||||
.PARAMETER Verbosity
|
||||
Specifies the amount of information to be displayed.
|
||||
.PARAMETER Experimental
|
||||
Tells Cake to use the latest Roslyn release.
|
||||
.PARAMETER WhatIf
|
||||
Performs a dry run of the build script.
|
||||
No tasks will be executed.
|
||||
.PARAMETER Mono
|
||||
Tells Cake to use the Mono scripting engine.
|
||||
.PARAMETER SkipToolPackageRestore
|
||||
Skips restoring of packages.
|
||||
.PARAMETER ScriptArgs
|
||||
Remaining arguments are added here.
|
||||
|
||||
.LINK
|
||||
http://cakebuild.net
|
||||
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[string]$Script = "build.cake",
|
||||
[string]$Target = "Default",
|
||||
[ValidateSet("Release", "Debug")]
|
||||
[string]$Configuration = "Release",
|
||||
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
|
||||
[string]$Verbosity = "Verbose",
|
||||
[switch]$Experimental,
|
||||
[Alias("DryRun","Noop")]
|
||||
[switch]$WhatIf,
|
||||
[switch]$Mono,
|
||||
[switch]$SkipToolPackageRestore,
|
||||
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||
[string[]]$ScriptArgs
|
||||
)
|
||||
|
||||
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
|
||||
function MD5HashFile([string] $filePath)
|
||||
{
|
||||
if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
|
||||
{
|
||||
return $null
|
||||
}
|
||||
|
||||
[System.IO.Stream] $file = $null;
|
||||
[System.Security.Cryptography.MD5] $md5 = $null;
|
||||
try
|
||||
{
|
||||
$md5 = [System.Security.Cryptography.MD5]::Create()
|
||||
$file = [System.IO.File]::OpenRead($filePath)
|
||||
return [System.BitConverter]::ToString($md5.ComputeHash($file))
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ($file -ne $null)
|
||||
{
|
||||
$file.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Preparing to run build script..."
|
||||
|
||||
if(!$PSScriptRoot){
|
||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||
}
|
||||
|
||||
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
|
||||
$ADDINS_DIR = Join-Path $TOOLS_DIR "addins"
|
||||
$MODULES_DIR = Join-Path $TOOLS_DIR "modules"
|
||||
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
|
||||
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
|
||||
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
|
||||
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
|
||||
$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
|
||||
$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
|
||||
|
||||
# Should we use mono?
|
||||
$UseMono = "";
|
||||
if($Mono.IsPresent) {
|
||||
Write-Verbose -Message "Using the Mono based scripting engine."
|
||||
$UseMono = "-mono"
|
||||
}
|
||||
|
||||
# Should we use the new Roslyn?
|
||||
$UseExperimental = "";
|
||||
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
|
||||
Write-Verbose -Message "Using experimental version of Roslyn."
|
||||
$UseExperimental = "-experimental"
|
||||
}
|
||||
|
||||
# Is this a dry run?
|
||||
$UseDryRun = "";
|
||||
if($WhatIf.IsPresent) {
|
||||
$UseDryRun = "-dryrun"
|
||||
}
|
||||
|
||||
# Make sure tools folder exists
|
||||
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
|
||||
Write-Verbose -Message "Creating tools directory..."
|
||||
New-Item -Path $TOOLS_DIR -Type directory | out-null
|
||||
}
|
||||
|
||||
# Make sure that packages.config exist.
|
||||
if (!(Test-Path $PACKAGES_CONFIG)) {
|
||||
Write-Verbose -Message "Downloading packages.config..."
|
||||
try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
|
||||
Throw "Could not download packages.config."
|
||||
}
|
||||
}
|
||||
|
||||
# Try find NuGet.exe in path if not exists
|
||||
if (!(Test-Path $NUGET_EXE)) {
|
||||
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
|
||||
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
|
||||
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
|
||||
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
|
||||
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
|
||||
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
|
||||
}
|
||||
}
|
||||
|
||||
# Try download NuGet.exe if not exists
|
||||
if (!(Test-Path $NUGET_EXE)) {
|
||||
Write-Verbose -Message "Downloading NuGet.exe..."
|
||||
try {
|
||||
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
|
||||
} catch {
|
||||
Throw "Could not download NuGet.exe."
|
||||
}
|
||||
}
|
||||
|
||||
# Save nuget.exe path to environment to be available to child processed
|
||||
$ENV:NUGET_EXE = $NUGET_EXE
|
||||
|
||||
# Restore tools from NuGet?
|
||||
if(-Not $SkipToolPackageRestore.IsPresent) {
|
||||
Push-Location
|
||||
Set-Location $TOOLS_DIR
|
||||
|
||||
# Check for changes in packages.config and remove installed tools if true.
|
||||
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
|
||||
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
|
||||
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
|
||||
Write-Verbose -Message "Missing or changed package.config hash..."
|
||||
Remove-Item * -Recurse -Exclude packages.config,nuget.exe
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Restoring tools from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet tools."
|
||||
}
|
||||
else
|
||||
{
|
||||
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
|
||||
}
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Restore addins from NuGet
|
||||
if (Test-Path $ADDINS_PACKAGES_CONFIG) {
|
||||
Push-Location
|
||||
Set-Location $ADDINS_DIR
|
||||
|
||||
Write-Verbose -Message "Restoring addins from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet addins."
|
||||
}
|
||||
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Restore modules from NuGet
|
||||
if (Test-Path $MODULES_PACKAGES_CONFIG) {
|
||||
Push-Location
|
||||
Set-Location $MODULES_DIR
|
||||
|
||||
Write-Verbose -Message "Restoring modules from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet modules."
|
||||
}
|
||||
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Make sure that Cake has been installed.
|
||||
if (!(Test-Path $CAKE_EXE)) {
|
||||
Throw "Could not find Cake.exe at $CAKE_EXE"
|
||||
}
|
||||
|
||||
# Start Cake
|
||||
Write-Host "Running build script..."
|
||||
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
|
||||
exit $LASTEXITCODE
|
||||
42
build.sh
Executable file
42
build.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
##########################################################################
|
||||
# This is the Cake bootstrapper script for Linux and OS X.
|
||||
# This file was downloaded from https://github.com/cake-build/resources
|
||||
# Feel free to change this file to fit your needs.
|
||||
##########################################################################
|
||||
|
||||
# Define directories.
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
TOOLS_DIR=$SCRIPT_DIR/tools
|
||||
CAKE_VERSION=0.19.1
|
||||
CAKE_DLL=$TOOLS_DIR/Cake.CoreCLR.$CAKE_VERSION/Cake.dll
|
||||
|
||||
# Make sure the tools folder exist.
|
||||
if [ ! -d "$TOOLS_DIR" ]; then
|
||||
mkdir "$TOOLS_DIR"
|
||||
fi
|
||||
|
||||
###########################################################################
|
||||
# INSTALL CAKE
|
||||
###########################################################################
|
||||
|
||||
if [ ! -f "$CAKE_DLL" ]; then
|
||||
curl -Lsfo Cake.CoreCLR.zip "https://www.nuget.org/api/v2/package/Cake.CoreCLR/$CAKE_VERSION" && unzip -q Cake.CoreCLR.zip -d "$TOOLS_DIR/Cake.CoreCLR.$CAKE_VERSION" && rm -f Cake.CoreCLR.zip
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "An error occured while installing Cake."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Make sure that Cake has been installed.
|
||||
if [ ! -f "$CAKE_DLL" ]; then
|
||||
echo "Could not find Cake.exe at '$CAKE_DLL'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
###########################################################################
|
||||
# RUN BUILD SCRIPT
|
||||
###########################################################################
|
||||
|
||||
# Start Cake
|
||||
exec dotnet "$CAKE_DLL" "$@"
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"projects": ["src","test"]
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archive.GZip
|
||||
{
|
||||
internal class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly string path;
|
||||
private readonly long size;
|
||||
private readonly DateTime? lastModified;
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
|
||||
internal GZipWritableArchiveEntry(GZipArchive archive, Stream stream,
|
||||
string path, long size, DateTime? lastModified, bool closeStream)
|
||||
: base(archive, null)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.path = path;
|
||||
this.size = size;
|
||||
this.lastModified = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
}
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return path; }
|
||||
}
|
||||
|
||||
public override long CompressedSize
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get { return size; }
|
||||
}
|
||||
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return lastModified; }
|
||||
}
|
||||
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
Stream IWritableArchiveEntry.Stream
|
||||
{
|
||||
get
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return new NonDisposingStream(stream);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archive.Tar
|
||||
{
|
||||
internal class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly string path;
|
||||
private readonly long size;
|
||||
private readonly DateTime? lastModified;
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
|
||||
internal TarWritableArchiveEntry(TarArchive archive, Stream stream, CompressionType compressionType,
|
||||
string path, long size, DateTime? lastModified, bool closeStream)
|
||||
: base(archive, null, compressionType)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.path = path;
|
||||
this.size = size;
|
||||
this.lastModified = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
}
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return path; }
|
||||
}
|
||||
|
||||
public override long CompressedSize
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get { return size; }
|
||||
}
|
||||
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return lastModified; }
|
||||
}
|
||||
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
Stream IWritableArchiveEntry.Stream
|
||||
{
|
||||
get
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return new NonDisposingStream(stream);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archive.Zip
|
||||
{
|
||||
internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly string path;
|
||||
private readonly long size;
|
||||
private readonly DateTime? lastModified;
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
private bool isDisposed;
|
||||
|
||||
internal ZipWritableArchiveEntry(ZipArchive archive, Stream stream, string path, long size,
|
||||
DateTime? lastModified, bool closeStream)
|
||||
: base(archive, null)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.path = path;
|
||||
this.size = size;
|
||||
this.lastModified = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
}
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return path; }
|
||||
}
|
||||
|
||||
public override long CompressedSize
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get { return size; }
|
||||
}
|
||||
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return lastModified; }
|
||||
}
|
||||
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
Stream IWritableArchiveEntry.Stream
|
||||
{
|
||||
get
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return new NonDisposingStream(stream);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream && !isDisposed)
|
||||
{
|
||||
stream.Dispose();
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtractionListener
|
||||
where TEntry : IArchiveEntry
|
||||
@@ -20,31 +20,33 @@ namespace SharpCompress.Archive
|
||||
public event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
|
||||
public event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
|
||||
|
||||
protected string Password { get; private set; }
|
||||
protected ReaderOptions ReaderOptions { get; }
|
||||
|
||||
private bool disposed;
|
||||
|
||||
#if !NO_FILE
|
||||
internal AbstractArchive(ArchiveType type, FileInfo fileInfo, Options options, string password)
|
||||
internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
{
|
||||
Type = type;
|
||||
Password = password;
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
throw new ArgumentException("File does not exist: " + fileInfo.FullName);
|
||||
}
|
||||
options = (Options) FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(fileInfo, options));
|
||||
ReaderOptions = readerOptions;
|
||||
readerOptions.LeaveStreamOpen = false;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(fileInfo));
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
|
||||
}
|
||||
|
||||
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(FileInfo file, Options options);
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(FileInfo file);
|
||||
#endif
|
||||
|
||||
internal AbstractArchive(ArchiveType type, IEnumerable<Stream> streams, Options options, string password)
|
||||
internal AbstractArchive(ArchiveType type, IEnumerable<Stream> streams, ReaderOptions readerOptions)
|
||||
{
|
||||
Type = type;
|
||||
Password = password;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(streams.Select(CheckStreams), options));
|
||||
ReaderOptions = readerOptions;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(streams.Select(CheckStreams)));
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
|
||||
}
|
||||
|
||||
@@ -54,22 +56,17 @@ namespace SharpCompress.Archive
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
|
||||
}
|
||||
public ArchiveType Type { get; private set; }
|
||||
|
||||
public ArchiveType Type { get; }
|
||||
|
||||
void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry)
|
||||
{
|
||||
if (EntryExtractionBegin != null)
|
||||
{
|
||||
EntryExtractionBegin(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
|
||||
}
|
||||
EntryExtractionBegin?.Invoke(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
|
||||
}
|
||||
|
||||
void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry)
|
||||
{
|
||||
if (EntryExtractionEnd != null)
|
||||
{
|
||||
EntryExtractionEnd(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
|
||||
}
|
||||
EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
|
||||
}
|
||||
|
||||
private static Stream CheckStreams(Stream stream)
|
||||
@@ -84,49 +81,29 @@ namespace SharpCompress.Archive
|
||||
/// <summary>
|
||||
/// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
|
||||
/// </summary>
|
||||
public virtual ICollection<TEntry> Entries
|
||||
{
|
||||
get { return lazyEntries; }
|
||||
}
|
||||
public virtual ICollection<TEntry> Entries { get { return lazyEntries; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
|
||||
/// </summary>
|
||||
public ICollection<TVolume> Volumes
|
||||
{
|
||||
get { return lazyVolumes; }
|
||||
}
|
||||
public ICollection<TVolume> Volumes { get { return lazyVolumes; } }
|
||||
|
||||
/// <summary>
|
||||
/// The total size of the files compressed in the archive.
|
||||
/// </summary>
|
||||
public virtual long TotalSize
|
||||
{
|
||||
get { return Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize); }
|
||||
}
|
||||
public virtual long TotalSize { get { return Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize); } }
|
||||
|
||||
/// <summary>
|
||||
/// The total size of the files as uncompressed in the archive.
|
||||
/// </summary>
|
||||
public virtual long TotalUncompressSize
|
||||
{
|
||||
get { return Entries.Aggregate(0L, (total, cf) => total + cf.Size); }
|
||||
}
|
||||
public virtual long TotalUncompressSize { get { return Entries.Aggregate(0L, (total, cf) => total + cf.Size); } }
|
||||
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(IEnumerable<Stream> streams, Options options);
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(IEnumerable<Stream> streams);
|
||||
protected abstract IEnumerable<TEntry> LoadEntries(IEnumerable<TVolume> volumes);
|
||||
|
||||
IEnumerable<IArchiveEntry> IArchive.Entries
|
||||
{
|
||||
get { return Entries.Cast<IArchiveEntry>(); }
|
||||
}
|
||||
IEnumerable<IArchiveEntry> IArchive.Entries { get { return Entries.Cast<IArchiveEntry>(); } }
|
||||
|
||||
IEnumerable<IVolume> IArchive.Volumes
|
||||
{
|
||||
get { return lazyVolumes.Cast<IVolume>(); }
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
IEnumerable<IVolume> IArchive.Volumes { get { return lazyVolumes.Cast<IVolume>(); } }
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
@@ -146,27 +123,21 @@ namespace SharpCompress.Archive
|
||||
|
||||
void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes)
|
||||
{
|
||||
if (CompressedBytesRead != null)
|
||||
CompressedBytesRead?.Invoke(this, new CompressedBytesReadEventArgs
|
||||
{
|
||||
CompressedBytesRead(this, new CompressedBytesReadEventArgs()
|
||||
{
|
||||
CurrentFilePartCompressedBytesRead = currentPartCompressedBytes,
|
||||
CompressedBytesRead = compressedReadBytes
|
||||
});
|
||||
}
|
||||
CurrentFilePartCompressedBytesRead = currentPartCompressedBytes,
|
||||
CompressedBytesRead = compressedReadBytes
|
||||
});
|
||||
}
|
||||
|
||||
void IExtractionListener.FireFilePartExtractionBegin(string name, long size, long compressedSize)
|
||||
{
|
||||
if (FilePartExtractionBegin != null)
|
||||
FilePartExtractionBegin?.Invoke(this, new FilePartExtractionBeginEventArgs
|
||||
{
|
||||
FilePartExtractionBegin(this, new FilePartExtractionBeginEventArgs()
|
||||
{
|
||||
CompressedSize = compressedSize,
|
||||
Size = size,
|
||||
Name = name,
|
||||
});
|
||||
}
|
||||
CompressedSize = compressedSize,
|
||||
Size = size,
|
||||
Name = name
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,11 +162,7 @@ namespace SharpCompress.Archive
|
||||
/// <summary>
|
||||
/// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
|
||||
/// </summary>
|
||||
public virtual bool IsSolid
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual bool IsSolid { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
|
||||
@@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public abstract class AbstractWritableArchive<TEntry, TVolume> : AbstractArchive<TEntry, TVolume>, IWritableArchive
|
||||
where TEntry : IArchiveEntry
|
||||
@@ -21,14 +23,14 @@ namespace SharpCompress.Archive
|
||||
{
|
||||
}
|
||||
|
||||
internal AbstractWritableArchive(ArchiveType type, Stream stream, Options options)
|
||||
: base(type, stream.AsEnumerable(), options, null)
|
||||
internal AbstractWritableArchive(ArchiveType type, Stream stream, ReaderOptions readerFactoryOptions)
|
||||
: base(type, stream.AsEnumerable(), readerFactoryOptions)
|
||||
{
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, Options options)
|
||||
: base(type, fileInfo, options, null)
|
||||
internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerFactoryOptions)
|
||||
: base(type, fileInfo, readerFactoryOptions)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -53,10 +55,7 @@ namespace SharpCompress.Archive
|
||||
modifiedEntries.AddRange(OldEntries.Concat(newEntries));
|
||||
}
|
||||
|
||||
private IEnumerable<TEntry> OldEntries
|
||||
{
|
||||
get { return base.Entries.Where(x => !removedEntries.Contains(x)); }
|
||||
}
|
||||
private IEnumerable<TEntry> OldEntries { get { return base.Entries.Where(x => !removedEntries.Contains(x)); } }
|
||||
|
||||
public void RemoveEntry(TEntry entry)
|
||||
{
|
||||
@@ -66,25 +65,25 @@ namespace SharpCompress.Archive
|
||||
RebuildModifiedCollection();
|
||||
}
|
||||
}
|
||||
|
||||
void IWritableArchive.RemoveEntry(IArchiveEntry entry)
|
||||
{
|
||||
RemoveEntry((TEntry)entry);
|
||||
}
|
||||
|
||||
public TEntry AddEntry(string key, Stream source,
|
||||
long size = 0, DateTime? modified = null)
|
||||
long size = 0, DateTime? modified = null)
|
||||
{
|
||||
return AddEntry(key, source, false, size, modified);
|
||||
}
|
||||
|
||||
|
||||
IArchiveEntry IWritableArchive.AddEntry(string key, Stream source, bool closeStream, long size, DateTime? modified)
|
||||
{
|
||||
return AddEntry(key, source, closeStream, size, modified);
|
||||
}
|
||||
|
||||
public TEntry AddEntry(string key, Stream source, bool closeStream,
|
||||
long size = 0, DateTime? modified = null)
|
||||
long size = 0, DateTime? modified = null)
|
||||
{
|
||||
if (key.StartsWith("/")
|
||||
|| key.StartsWith("\\"))
|
||||
@@ -105,7 +104,7 @@ namespace SharpCompress.Archive
|
||||
{
|
||||
foreach (var path in Entries.Select(x => x.Key))
|
||||
{
|
||||
var p = path.Replace('/','\\');
|
||||
var p = path.Replace('/', '\\');
|
||||
if (p.StartsWith("\\"))
|
||||
{
|
||||
p = p.Substring(1);
|
||||
@@ -115,15 +114,15 @@ namespace SharpCompress.Archive
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SaveTo(Stream stream, CompressionInfo compressionType)
|
||||
public void SaveTo(Stream stream, WriterOptions options)
|
||||
{
|
||||
//reset streams of new entries
|
||||
newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
|
||||
SaveTo(stream, compressionType, OldEntries, newEntries);
|
||||
SaveTo(stream, options, OldEntries, newEntries);
|
||||
}
|
||||
|
||||
protected TEntry CreateEntry(string key, Stream source, long size, DateTime? modified,
|
||||
bool closeStream)
|
||||
bool closeStream)
|
||||
{
|
||||
if (!source.CanRead || !source.CanSeek)
|
||||
{
|
||||
@@ -133,10 +132,9 @@ namespace SharpCompress.Archive
|
||||
}
|
||||
|
||||
protected abstract TEntry CreateEntryInternal(string key, Stream source, long size, DateTime? modified,
|
||||
bool closeStream);
|
||||
bool closeStream);
|
||||
|
||||
protected abstract void SaveTo(Stream stream, CompressionInfo compressionType,
|
||||
IEnumerable<TEntry> oldEntries, IEnumerable<TEntry> newEntries);
|
||||
protected abstract void SaveTo(Stream stream, WriterOptions options, IEnumerable<TEntry> oldEntries, IEnumerable<TEntry> newEntries);
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
@@ -1,13 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Archive.GZip;
|
||||
using SharpCompress.Archive.Rar;
|
||||
using SharpCompress.Archive.SevenZip;
|
||||
using SharpCompress.Archive.Tar;
|
||||
using SharpCompress.Archive.Zip;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public class ArchiveFactory
|
||||
{
|
||||
@@ -15,44 +16,44 @@ namespace SharpCompress.Archive
|
||||
/// Opens an Archive for random access
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
/// <returns></returns>
|
||||
public static IArchive Open(Stream stream, Options options = Options.KeepStreamsOpen)
|
||||
public static IArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
if (!stream.CanRead || !stream.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("Stream should be readable and seekable");
|
||||
}
|
||||
|
||||
readerOptions = readerOptions ?? new ReaderOptions();
|
||||
if (ZipArchive.IsZipFile(stream, null))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return ZipArchive.Open(stream, options, null);
|
||||
return ZipArchive.Open(stream, readerOptions);
|
||||
}
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (SevenZipArchive.IsSevenZipFile(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return SevenZipArchive.Open(stream, options);
|
||||
return SevenZipArchive.Open(stream, readerOptions);
|
||||
}
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (GZipArchive.IsGZipFile(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return GZipArchive.Open(stream, options);
|
||||
return GZipArchive.Open(stream, readerOptions);
|
||||
}
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (RarArchive.IsRarFile(stream, options))
|
||||
if (RarArchive.IsRarFile(stream, readerOptions))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return RarArchive.Open(stream, options);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return RarArchive.Open(stream, readerOptions);
|
||||
}
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (TarArchive.IsTarFile(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return TarArchive.Open(stream, options);
|
||||
return TarArchive.Open(stream, readerOptions);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
|
||||
}
|
||||
@@ -62,52 +63,35 @@ namespace SharpCompress.Archive
|
||||
switch (type)
|
||||
{
|
||||
case ArchiveType.Zip:
|
||||
{
|
||||
return ZipArchive.Create();
|
||||
}
|
||||
{
|
||||
return ZipArchive.Create();
|
||||
}
|
||||
case ArchiveType.Tar:
|
||||
{
|
||||
return TarArchive.Create();
|
||||
}
|
||||
{
|
||||
return TarArchive.Create();
|
||||
}
|
||||
case ArchiveType.GZip:
|
||||
{
|
||||
return GZipArchive.Create();
|
||||
}
|
||||
{
|
||||
return GZipArchive.Create();
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("Cannot create Archives of type: " + type);
|
||||
}
|
||||
{
|
||||
throw new NotSupportedException("Cannot create Archives of type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
public static IArchive Open(string filePath)
|
||||
{
|
||||
return Open(filePath, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
public static IArchive Open(FileInfo fileInfo)
|
||||
{
|
||||
return Open(fileInfo, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
public static IArchive Open(string filePath, Options options)
|
||||
public static IArchive Open(string filePath, ReaderOptions options = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), options);
|
||||
return Open(new FileInfo(filePath), options ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,15 +99,16 @@ namespace SharpCompress.Archive
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
public static IArchive Open(FileInfo fileInfo, Options options)
|
||||
public static IArchive Open(FileInfo fileInfo, ReaderOptions options = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
options = options ?? new ReaderOptions();
|
||||
using (var stream = fileInfo.OpenRead())
|
||||
{
|
||||
if (ZipArchive.IsZipFile(stream, null))
|
||||
{
|
||||
stream.Dispose();
|
||||
return ZipArchive.Open(fileInfo, options, null);
|
||||
return ZipArchive.Open(fileInfo, options);
|
||||
}
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (SevenZipArchive.IsSevenZipFile(stream))
|
||||
@@ -157,7 +142,7 @@ namespace SharpCompress.Archive
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
public static void WriteToDirectory(string sourceArchive, string destinationDirectory,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
ExtractionOptions options = null)
|
||||
{
|
||||
using (IArchive archive = Open(sourceArchive))
|
||||
{
|
||||
@@ -4,11 +4,12 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Reader.GZip;
|
||||
using SharpCompress.Writer.GZip;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.GZip;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.GZip;
|
||||
|
||||
namespace SharpCompress.Archive.GZip
|
||||
namespace SharpCompress.Archives.GZip
|
||||
{
|
||||
public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
{
|
||||
@@ -17,62 +18,33 @@ namespace SharpCompress.Archive.GZip
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
public static GZipArchive Open(string filePath)
|
||||
{
|
||||
return Open(filePath, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
public static GZipArchive Open(FileInfo fileInfo)
|
||||
{
|
||||
return Open(fileInfo, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
public static GZipArchive Open(string filePath, Options options)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static GZipArchive Open(string filePath, ReaderOptions readerOptions = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), options);
|
||||
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
public static GZipArchive Open(FileInfo fileInfo, Options options)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static GZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new GZipArchive(fileInfo, options);
|
||||
return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
public static GZipArchive Open(Stream stream)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static GZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return Open(stream, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
public static GZipArchive Open(Stream stream, Options options)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new GZipArchive(stream, options);
|
||||
return new GZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
public static GZipArchive Create()
|
||||
@@ -81,19 +53,20 @@ namespace SharpCompress.Archive.GZip
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
internal GZipArchive(FileInfo fileInfo, Options options)
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
internal GZipArchive(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(ArchiveType.GZip, fileInfo, options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(FileInfo file, Options options)
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
return new GZipVolume(file, options).AsEnumerable();
|
||||
return new GZipVolume(file, ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
public static bool IsGZipFile(string filePath)
|
||||
@@ -122,7 +95,7 @@ namespace SharpCompress.Archive.GZip
|
||||
{
|
||||
using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
SaveTo(stream);
|
||||
SaveTo(stream, new WriterOptions(CompressionType.GZip));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -135,13 +108,19 @@ namespace SharpCompress.Archive.GZip
|
||||
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (n == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n != 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -151,7 +130,7 @@ namespace SharpCompress.Archive.GZip
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal GZipArchive(Stream stream, Options options)
|
||||
internal GZipArchive(Stream stream, ReaderOptions options)
|
||||
: base(ArchiveType.GZip, stream, options)
|
||||
{
|
||||
}
|
||||
@@ -161,13 +140,8 @@ namespace SharpCompress.Archive.GZip
|
||||
{
|
||||
}
|
||||
|
||||
public void SaveTo(Stream stream)
|
||||
{
|
||||
SaveTo(stream, CompressionType.GZip);
|
||||
}
|
||||
|
||||
protected override GZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
|
||||
bool closeStream)
|
||||
bool closeStream)
|
||||
{
|
||||
if (Entries.Any())
|
||||
{
|
||||
@@ -176,7 +150,7 @@ namespace SharpCompress.Archive.GZip
|
||||
return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
|
||||
}
|
||||
|
||||
protected override void SaveTo(Stream stream, CompressionInfo compressionInfo,
|
||||
protected override void SaveTo(Stream stream, WriterOptions options,
|
||||
IEnumerable<GZipArchiveEntry> oldEntries,
|
||||
IEnumerable<GZipArchiveEntry> newEntries)
|
||||
{
|
||||
@@ -197,9 +171,9 @@ namespace SharpCompress.Archive.GZip
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
return new GZipVolume(streams.First(), options).AsEnumerable();
|
||||
return new GZipVolume(streams.First(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipArchiveEntry> LoadEntries(IEnumerable<GZipVolume> volumes)
|
||||
@@ -2,11 +2,10 @@
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.GZip;
|
||||
|
||||
namespace SharpCompress.Archive.GZip
|
||||
namespace SharpCompress.Archives.GZip
|
||||
{
|
||||
public class GZipArchiveEntry : GZipEntry, IArchiveEntry
|
||||
{
|
||||
|
||||
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part)
|
||||
: base(part)
|
||||
{
|
||||
@@ -15,16 +14,20 @@ namespace SharpCompress.Archive.GZip
|
||||
|
||||
public virtual Stream OpenEntryStream()
|
||||
{
|
||||
//this is to reset the stream to be read multiple times
|
||||
var part = Parts.Single() as GZipFilePart;
|
||||
if (part.GetRawStream().Position != part.EntryStartPosition)
|
||||
{
|
||||
part.GetRawStream().Position = part.EntryStartPosition;
|
||||
}
|
||||
return Parts.Single().GetCompressedStream();
|
||||
}
|
||||
|
||||
#region IArchiveEntry Members
|
||||
public IArchive Archive { get; private set; }
|
||||
|
||||
public bool IsComplete
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public IArchive Archive { get; }
|
||||
|
||||
public bool IsComplete => true;
|
||||
|
||||
#endregion
|
||||
}
|
||||
66
src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
Normal file
66
src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archives.GZip
|
||||
{
|
||||
internal class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
|
||||
internal GZipWritableArchiveEntry(GZipArchive archive, Stream stream,
|
||||
string path, long size, DateTime? lastModified, bool closeStream)
|
||||
: base(archive, null)
|
||||
{
|
||||
this.stream = stream;
|
||||
Key = path;
|
||||
Size = size;
|
||||
LastModifiedTime = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
}
|
||||
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string Key { get; }
|
||||
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size { get; }
|
||||
|
||||
public override DateTime? LastModifiedTime { get; }
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
|
||||
public override bool IsSplit => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => throw new NotImplementedException();
|
||||
|
||||
Stream IWritableArchiveEntry.Stream => stream;
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return new NonDisposingStream(stream);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public interface IArchive : IDisposable
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public interface IArchiveEntry : IEntry
|
||||
{
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public static class IArchiveEntryExtensions
|
||||
{
|
||||
@@ -28,25 +29,33 @@ namespace SharpCompress.Archive
|
||||
return;
|
||||
}
|
||||
using (entryStream)
|
||||
using (Stream s = new ListeningStream(streamListener, entryStream))
|
||||
{
|
||||
s.TransferTo(streamToWriteTo);
|
||||
using (Stream s = new ListeningStream(streamListener, entryStream))
|
||||
{
|
||||
s.TransferTo(streamToWriteTo);
|
||||
}
|
||||
}
|
||||
streamListener.FireEntryExtractionEnd(archiveEntry);
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
public static void WriteToDirectory(this IArchiveEntry entry, string destinationDirectory,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
ExtractionOptions options = null)
|
||||
{
|
||||
string destinationFileName;
|
||||
string file = Path.GetFileName(entry.Key);
|
||||
|
||||
options = options ?? new ExtractionOptions()
|
||||
{
|
||||
Overwrite = true
|
||||
};
|
||||
|
||||
if (options.HasFlag(ExtractOptions.ExtractFullPath))
|
||||
|
||||
if (options.ExtractFullPath)
|
||||
{
|
||||
string folder = Path.GetDirectoryName(entry.Key);
|
||||
string destdir = Path.Combine(destinationDirectory, folder);
|
||||
@@ -70,11 +79,16 @@ namespace SharpCompress.Archive
|
||||
/// Extract to specific file
|
||||
/// </summary>
|
||||
public static void WriteToFile(this IArchiveEntry entry, string destinationFileName,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
ExtractionOptions options = null)
|
||||
{
|
||||
FileMode fm = FileMode.Create;
|
||||
options = options ?? new ExtractionOptions()
|
||||
{
|
||||
Overwrite = true
|
||||
};
|
||||
|
||||
if (!options.HasFlag(ExtractOptions.Overwrite))
|
||||
|
||||
if (!options.Overwrite)
|
||||
{
|
||||
fm = FileMode.CreateNew;
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
#if !NO_FILE
|
||||
using System.Linq;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
#endif
|
||||
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public static class IArchiveExtensions
|
||||
{
|
||||
#if !NO_FILE
|
||||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
public static void WriteToDirectory(this IArchive archive, string destinationDirectory,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
ExtractionOptions options = null)
|
||||
{
|
||||
foreach (IArchiveEntry entry in archive.Entries.Where(x => !x.IsDirectory))
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
internal interface IArchiveExtractionListener : IExtractionListener
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public interface IWritableArchive : IArchive
|
||||
{
|
||||
@@ -10,6 +10,6 @@ namespace SharpCompress.Archive
|
||||
|
||||
IArchiveEntry AddEntry(string key, Stream source, bool closeStream, long size = 0, DateTime? modified = null);
|
||||
|
||||
void SaveTo(Stream stream, CompressionInfo compressionType);
|
||||
void SaveTo(Stream stream, WriterOptions options);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
internal interface IWritableArchiveEntry
|
||||
{
|
||||
@@ -1,17 +1,13 @@
|
||||
using System;
|
||||
#if !NO_FILE
|
||||
using System;
|
||||
#endif
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Archive
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
public static class IWritableArchiveExtensions
|
||||
{
|
||||
public static void SaveTo(this IWritableArchive writableArchive,
|
||||
Stream stream, CompressionType compressionType)
|
||||
{
|
||||
writableArchive.SaveTo(stream, new CompressionInfo {Type = compressionType});
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
|
||||
public static void AddEntry(this IWritableArchive writableArchive,
|
||||
@@ -26,33 +22,16 @@ namespace SharpCompress.Archive
|
||||
fileInfo.LastWriteTime);
|
||||
}
|
||||
|
||||
public static void SaveTo(this IWritableArchive writableArchive,
|
||||
string filePath, CompressionType compressionType)
|
||||
public static void SaveTo(this IWritableArchive writableArchive, string filePath, WriterOptions options)
|
||||
{
|
||||
writableArchive.SaveTo(new FileInfo(filePath), new CompressionInfo {Type = compressionType});
|
||||
writableArchive.SaveTo(new FileInfo(filePath), options);
|
||||
}
|
||||
|
||||
public static void SaveTo(this IWritableArchive writableArchive,
|
||||
FileInfo fileInfo, CompressionType compressionType)
|
||||
public static void SaveTo(this IWritableArchive writableArchive, FileInfo fileInfo, WriterOptions options)
|
||||
{
|
||||
using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
writableArchive.SaveTo(stream, new CompressionInfo {Type = compressionType});
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveTo(this IWritableArchive writableArchive,
|
||||
string filePath, CompressionInfo compressionInfo)
|
||||
{
|
||||
writableArchive.SaveTo(new FileInfo(filePath), compressionInfo);
|
||||
}
|
||||
|
||||
public static void SaveTo(this IWritableArchive writableArchive,
|
||||
FileInfo fileInfo, CompressionInfo compressionInfo)
|
||||
{
|
||||
using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
writableArchive.SaveTo(stream, compressionInfo);
|
||||
writableArchive.SaveTo(stream, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,40 @@
|
||||
#if !NO_FILE
|
||||
|
||||
#if !NO_FILE
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
/// <summary>
|
||||
/// A rar part based on a FileInfo object
|
||||
/// </summary>
|
||||
internal class FileInfoRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal FileInfoRarArchiveVolume(FileInfo fileInfo, string password, Options options)
|
||||
: base(StreamingMode.Seekable, fileInfo.OpenRead(), password, FixOptions(options))
|
||||
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options))
|
||||
{
|
||||
FileInfo = fileInfo;
|
||||
FileParts = base.GetVolumeFileParts().ToReadOnly();
|
||||
FileParts = GetVolumeFileParts().ToReadOnly();
|
||||
}
|
||||
|
||||
private static Options FixOptions(Options options)
|
||||
private static ReaderOptions FixOptions(ReaderOptions options)
|
||||
{
|
||||
//make sure we're closing streams with fileinfo
|
||||
if (options.HasFlag(Options.KeepStreamsOpen))
|
||||
{
|
||||
options = (Options) FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
|
||||
}
|
||||
options.LeaveStreamOpen = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
internal ReadOnlyCollection<RarFilePart> FileParts { get; private set; }
|
||||
internal ReadOnlyCollection<RarFilePart> FileParts { get; }
|
||||
|
||||
internal FileInfo FileInfo { get; private set; }
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal override RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader)
|
||||
{
|
||||
return new FileInfoRarFilePart(this, markHeader, fileHeader, FileInfo);
|
||||
return new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo);
|
||||
}
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts()
|
||||
@@ -1,19 +1,19 @@
|
||||
#if !NO_FILE
|
||||
|
||||
#if !NO_FILE
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
internal class FileInfoRarFilePart : SeekableFilePart
|
||||
{
|
||||
internal FileInfoRarFilePart(FileInfoRarArchiveVolume volume, MarkHeader mh, FileHeader fh, FileInfo fi)
|
||||
: base(mh, fh, volume.Stream, volume.Password)
|
||||
internal FileInfoRarFilePart(FileInfoRarArchiveVolume volume, string password, MarkHeader mh, FileHeader fh, FileInfo fi)
|
||||
: base(mh, fh, volume.Stream, password)
|
||||
{
|
||||
FileInfo = fi;
|
||||
}
|
||||
|
||||
internal FileInfo FileInfo { get; private set; }
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal override string FilePartName
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
public static class RarArchiveExtensions
|
||||
{
|
||||
@@ -1,41 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.Compressor.Rar;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Reader.Rar;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Rar;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
{
|
||||
private readonly Unpack unpack = new Unpack();
|
||||
|
||||
internal Unpack Unpack
|
||||
{
|
||||
get { return unpack; }
|
||||
}
|
||||
internal Unpack Unpack { get; } = new Unpack();
|
||||
|
||||
#if !NO_FILE
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
internal RarArchive(FileInfo fileInfo, Options options, string password)
|
||||
: base(ArchiveType.Rar, fileInfo, options, password)
|
||||
internal RarArchive(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(ArchiveType.Rar, fileInfo, options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(FileInfo file, Options options)
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
return RarArchiveVolumeFactory.GetParts(file, Password, options);
|
||||
return RarArchiveVolumeFactory.GetParts(file, ReaderOptions);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -44,9 +38,8 @@ namespace SharpCompress.Archive.Rar
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
internal RarArchive(IEnumerable<Stream> streams, Options options, string password)
|
||||
: base(ArchiveType.Rar, streams, options, password)
|
||||
internal RarArchive(IEnumerable<Stream> streams, ReaderOptions options)
|
||||
: base(ArchiveType.Rar, streams, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -55,36 +48,33 @@ namespace SharpCompress.Archive.Rar
|
||||
return RarArchiveEntryFactory.GetEntries(this, volumes);
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
return RarArchiveVolumeFactory.GetParts(streams, Password, options);
|
||||
return RarArchiveVolumeFactory.GetParts(streams, ReaderOptions);
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
{
|
||||
var stream = Volumes.First().Stream;
|
||||
stream.Position = 0;
|
||||
return RarReader.Open(stream, Password);
|
||||
return RarReader.Open(stream, ReaderOptions);
|
||||
}
|
||||
|
||||
public override bool IsSolid
|
||||
{
|
||||
get { return Volumes.First().IsSolidArchive; }
|
||||
}
|
||||
public override bool IsSolid => Volumes.First().IsSolidArchive;
|
||||
|
||||
#region Creation
|
||||
|
||||
#if !NO_FILE
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static RarArchive Open(string filePath, Options options = Options.None, string password = null)
|
||||
public static RarArchive Open(string filePath, ReaderOptions options = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), options, password);
|
||||
return new RarArchive(new FileInfo(filePath), options ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,23 +82,22 @@ namespace SharpCompress.Archive.Rar
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static RarArchive Open(FileInfo fileInfo, Options options = Options.None, string password = null)
|
||||
public static RarArchive Open(FileInfo fileInfo, ReaderOptions options = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new RarArchive(fileInfo, options, password);
|
||||
return new RarArchive(fileInfo, options ?? new ReaderOptions());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static RarArchive Open(Stream stream, Options options = Options.KeepStreamsOpen, string password = null)
|
||||
public static RarArchive Open(Stream stream, ReaderOptions options = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return Open(stream.AsEnumerable(), options, password);
|
||||
return Open(stream.AsEnumerable(), options ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,11 +105,10 @@ namespace SharpCompress.Archive.Rar
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static RarArchive Open(IEnumerable<Stream> streams, Options options = Options.KeepStreamsOpen, string password = null)
|
||||
public static RarArchive Open(IEnumerable<Stream> streams, ReaderOptions options = null)
|
||||
{
|
||||
streams.CheckNotNull("streams");
|
||||
return new RarArchive(streams, options, password);
|
||||
return new RarArchive(streams, options ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
@@ -141,17 +129,12 @@ namespace SharpCompress.Archive.Rar
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool IsRarFile(Stream stream)
|
||||
{
|
||||
return IsRarFile(stream, Options.None);
|
||||
}
|
||||
|
||||
public static bool IsRarFile(Stream stream, Options options)
|
||||
|
||||
public static bool IsRarFile(Stream stream, ReaderOptions options = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var headerFactory = new RarHeaderFactory(StreamingMode.Seekable, options);
|
||||
var headerFactory = new RarHeaderFactory(StreamingMode.Seekable, options ?? new ReaderOptions());
|
||||
var markHeader = headerFactory.ReadHeaders(stream).FirstOrDefault() as MarkHeader;
|
||||
return markHeader != null && markHeader.IsValid();
|
||||
}
|
||||
@@ -5,9 +5,9 @@ using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.Compressor.Rar;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
{
|
||||
@@ -20,28 +20,13 @@ namespace SharpCompress.Archive.Rar
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType
|
||||
{
|
||||
get { return CompressionType.Rar; }
|
||||
}
|
||||
public override CompressionType CompressionType => CompressionType.Rar;
|
||||
|
||||
public IArchive Archive
|
||||
{
|
||||
get
|
||||
{
|
||||
return archive;
|
||||
}
|
||||
}
|
||||
public IArchive Archive => archive;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { return parts.Cast<FilePart>(); }
|
||||
}
|
||||
internal override IEnumerable<FilePart> Parts => parts.Cast<FilePart>();
|
||||
|
||||
internal override FileHeader FileHeader
|
||||
{
|
||||
get { return parts.First().FileHeader; }
|
||||
}
|
||||
internal override FileHeader FileHeader => parts.First().FileHeader;
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
@@ -49,11 +34,10 @@ namespace SharpCompress.Archive.Rar
|
||||
{
|
||||
CheckIncomplete();
|
||||
return parts.Select(fp => fp.FileHeader)
|
||||
.Single(fh => !fh.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)).FileCRC;
|
||||
.Single(fh => !fh.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)).FileCRC;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get
|
||||
@@ -81,10 +65,7 @@ namespace SharpCompress.Archive.Rar
|
||||
return new RarStream(archive.Unpack, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive));
|
||||
}
|
||||
|
||||
public bool IsComplete
|
||||
{
|
||||
get { return parts.Select(fp => fp.FileHeader).Any(fh => !fh.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)); }
|
||||
}
|
||||
public bool IsComplete { get { return parts.Select(fp => fp.FileHeader).Any(fh => !fh.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)); } }
|
||||
|
||||
private void CheckIncomplete()
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
internal static class RarArchiveEntryFactory
|
||||
{
|
||||
@@ -25,7 +25,7 @@ namespace SharpCompress.Archive.Rar
|
||||
{
|
||||
groupedParts.Add(fp);
|
||||
|
||||
if (!FlagUtility.HasFlag((long) fp.FileHeader.FileFlags, (long) FileFlags.SPLIT_AFTER))
|
||||
if (!FlagUtility.HasFlag((long)fp.FileHeader.FileFlags, (long)FileFlags.SPLIT_AFTER))
|
||||
{
|
||||
yield return groupedParts;
|
||||
groupedParts = new List<RarFilePart>();
|
||||
@@ -1,17 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Readers;
|
||||
#if !NO_FILE
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
#endif
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
internal static class RarArchiveVolumeFactory
|
||||
{
|
||||
internal static IEnumerable<RarVolume> GetParts(IEnumerable<Stream> streams, string password, Options options)
|
||||
internal static IEnumerable<RarVolume> GetParts(IEnumerable<Stream> streams, ReaderOptions options)
|
||||
{
|
||||
foreach (Stream s in streams)
|
||||
{
|
||||
@@ -19,15 +22,15 @@ namespace SharpCompress.Archive.Rar
|
||||
{
|
||||
throw new ArgumentException("Stream is not readable and seekable");
|
||||
}
|
||||
StreamRarArchiveVolume part = new StreamRarArchiveVolume(s, password, options);
|
||||
StreamRarArchiveVolume part = new StreamRarArchiveVolume(s, options);
|
||||
yield return part;
|
||||
}
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
internal static IEnumerable<RarVolume> GetParts(FileInfo fileInfo, string password, Options options)
|
||||
internal static IEnumerable<RarVolume> GetParts(FileInfo fileInfo, ReaderOptions options)
|
||||
{
|
||||
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, password, options);
|
||||
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options);
|
||||
yield return part;
|
||||
|
||||
if (!part.ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME))
|
||||
@@ -39,7 +42,7 @@ namespace SharpCompress.Archive.Rar
|
||||
//we use fileinfo because rar is dumb and looks at file names rather than archive info for another volume
|
||||
while (fileInfo != null && fileInfo.Exists)
|
||||
{
|
||||
part = new FileInfoRarArchiveVolume(fileInfo, password, options);
|
||||
part = new FileInfoRarArchiveVolume(fileInfo, options);
|
||||
|
||||
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart);
|
||||
yield return part;
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
internal class SeekableFilePart : RarFilePart
|
||||
{
|
||||
@@ -29,9 +28,6 @@ namespace SharpCompress.Archive.Rar
|
||||
return stream;
|
||||
}
|
||||
|
||||
internal override string FilePartName
|
||||
{
|
||||
get { return "Unknown Stream - File Entry: " + FileHeader.FileName; }
|
||||
}
|
||||
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive.Rar
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
internal class StreamRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal StreamRarArchiveVolume(Stream stream, string password, Options options)
|
||||
: base(StreamingMode.Seekable, stream, password, options)
|
||||
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options)
|
||||
: base(StreamingMode.Seekable, stream, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SharpCompress.Archive.Rar
|
||||
|
||||
internal override RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader)
|
||||
{
|
||||
return new SeekableFilePart(markHeader, fileHeader, Stream, Password);
|
||||
return new SeekableFilePart(markHeader, fileHeader, Stream, ReaderOptions.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,89 +5,57 @@ using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archive.SevenZip
|
||||
namespace SharpCompress.Archives.SevenZip
|
||||
{
|
||||
public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVolume>
|
||||
{
|
||||
private ArchiveDatabase database;
|
||||
#if !NO_FILE
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
public static SevenZipArchive Open(string filePath)
|
||||
{
|
||||
return Open(filePath, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
public static SevenZipArchive Open(FileInfo fileInfo)
|
||||
{
|
||||
return Open(fileInfo, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
public static SevenZipArchive Open(string filePath, Options options)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), options);
|
||||
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, Options options)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new SevenZipArchive(fileInfo, options);
|
||||
return new SevenZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
public static SevenZipArchive Open(Stream stream)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return Open(stream, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
public static SevenZipArchive Open(Stream stream, Options options)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SevenZipArchive(stream, options);
|
||||
return new SevenZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
internal SevenZipArchive(FileInfo fileInfo, Options options)
|
||||
: base(ArchiveType.SevenZip, fileInfo, options, null)
|
||||
internal SevenZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.SevenZip, fileInfo, readerOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(FileInfo file, Options options)
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
if (FlagUtility.HasFlag(options, Options.KeepStreamsOpen))
|
||||
{
|
||||
options = (Options)FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
|
||||
}
|
||||
return new SevenZipVolume(file.OpenRead(), options).AsEnumerable();
|
||||
return new SevenZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
public static bool IsSevenZipFile(string filePath)
|
||||
@@ -108,8 +76,8 @@ namespace SharpCompress.Archive.SevenZip
|
||||
}
|
||||
#endif
|
||||
|
||||
internal SevenZipArchive(Stream stream, Options options)
|
||||
: base(ArchiveType.SevenZip, stream.AsEnumerable(), options, null)
|
||||
internal SevenZipArchive(Stream stream, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.SevenZip, stream.AsEnumerable(), readerOptions)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -118,7 +86,7 @@ namespace SharpCompress.Archive.SevenZip
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
foreach (Stream s in streams)
|
||||
{
|
||||
@@ -126,7 +94,7 @@ namespace SharpCompress.Archive.SevenZip
|
||||
{
|
||||
throw new ArgumentException("Stream is not readable and seekable");
|
||||
}
|
||||
SevenZipVolume volume = new SevenZipVolume(s, options);
|
||||
SevenZipVolume volume = new SevenZipVolume(s, ReaderOptions);
|
||||
yield return volume;
|
||||
}
|
||||
}
|
||||
@@ -138,10 +106,7 @@ namespace SharpCompress.Archive.SevenZip
|
||||
for (int i = 0; i < database.Files.Count; i++)
|
||||
{
|
||||
var file = database.Files[i];
|
||||
if (!file.IsDir)
|
||||
{
|
||||
yield return new SevenZipArchiveEntry(this, new SevenZipFilePart(stream, database, i, file));
|
||||
}
|
||||
yield return new SevenZipArchiveEntry(this, new SevenZipFilePart(stream, database, i, file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +121,6 @@ namespace SharpCompress.Archive.SevenZip
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool IsSevenZipFile(Stream stream)
|
||||
{
|
||||
try
|
||||
@@ -169,7 +133,7 @@ namespace SharpCompress.Archive.SevenZip
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly byte[] SIGNATURE = new byte[] {(byte) '7', (byte) 'z', 0xBC, 0xAF, 0x27, 0x1C};
|
||||
private static readonly byte[] SIGNATURE = {(byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C};
|
||||
|
||||
private static bool SignatureMatch(Stream stream)
|
||||
{
|
||||
@@ -183,10 +147,7 @@ namespace SharpCompress.Archive.SevenZip
|
||||
return new SevenZipReader(this);
|
||||
}
|
||||
|
||||
public override bool IsSolid
|
||||
{
|
||||
get { return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; }
|
||||
}
|
||||
public override bool IsSolid { get { return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; } }
|
||||
|
||||
public override long TotalSize
|
||||
{
|
||||
@@ -205,16 +166,12 @@ namespace SharpCompress.Archive.SevenZip
|
||||
private CFileItem currentItem;
|
||||
|
||||
internal SevenZipReader(SevenZipArchive archive)
|
||||
: base(Options.KeepStreamsOpen, ArchiveType.SevenZip)
|
||||
: base(new ReaderOptions(), ArchiveType.SevenZip)
|
||||
{
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
|
||||
public override SevenZipVolume Volume
|
||||
{
|
||||
get { return archive.Volumes.Single(); }
|
||||
}
|
||||
public override SevenZipVolume Volume => archive.Volumes.Single();
|
||||
|
||||
internal override IEnumerable<SevenZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
@@ -249,4 +206,4 @@ namespace SharpCompress.Archive.SevenZip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
|
||||
namespace SharpCompress.Archive.SevenZip
|
||||
namespace SharpCompress.Archives.SevenZip
|
||||
{
|
||||
public class SevenZipArchiveEntry : SevenZipEntry, IArchiveEntry
|
||||
{
|
||||
@@ -16,19 +15,14 @@ namespace SharpCompress.Archive.SevenZip
|
||||
{
|
||||
return FilePart.GetCompressedStream();
|
||||
}
|
||||
public IArchive Archive { get; private set; }
|
||||
|
||||
public bool IsComplete
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public IArchive Archive { get; }
|
||||
|
||||
public bool IsComplete => true;
|
||||
|
||||
/// <summary>
|
||||
/// This is a 7Zip Anti item
|
||||
/// </summary>
|
||||
public bool IsAnti
|
||||
{
|
||||
get { return FilePart.Header.IsAnti; }
|
||||
}
|
||||
public bool IsAnti => FilePart.Header.IsAnti;
|
||||
}
|
||||
}
|
||||
@@ -4,77 +4,50 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Reader.Tar;
|
||||
using SharpCompress.Writer.Tar;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Tar;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Tar;
|
||||
|
||||
namespace SharpCompress.Archive.Tar
|
||||
namespace SharpCompress.Archives.Tar
|
||||
{
|
||||
public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
{
|
||||
#if !NO_FILE
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
public static TarArchive Open(string filePath)
|
||||
{
|
||||
return Open(filePath, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
public static TarArchive Open(FileInfo fileInfo)
|
||||
{
|
||||
return Open(fileInfo, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
public static TarArchive Open(string filePath, Options options)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static TarArchive Open(string filePath, ReaderOptions readerOptions = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), options);
|
||||
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
public static TarArchive Open(FileInfo fileInfo, Options options)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static TarArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new TarArchive(fileInfo, options);
|
||||
return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
public static TarArchive Open(Stream stream)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static TarArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return Open(stream, Options.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
public static TarArchive Open(Stream stream, Options options)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new TarArchive(stream, options);
|
||||
return new TarArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
@@ -100,9 +73,9 @@ namespace SharpCompress.Archive.Tar
|
||||
{
|
||||
try
|
||||
{
|
||||
TarHeader tar = new TarHeader();
|
||||
tar.Read(new BinaryReader(stream));
|
||||
return tar.Name.Length > 0 && Enum.IsDefined(typeof (EntryType), tar.EntryType);
|
||||
var input = new TarInputStream(stream);
|
||||
var header = input.GetNextEntry();
|
||||
return header.Name.Length > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -111,23 +84,21 @@ namespace SharpCompress.Archive.Tar
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
internal TarArchive(FileInfo fileInfo, Options options)
|
||||
: base(ArchiveType.Tar, fileInfo, options)
|
||||
/// <param name="readerOptions"></param>
|
||||
internal TarArchive(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Tar, fileInfo, readerOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(FileInfo file, Options options)
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
if (FlagUtility.HasFlag(options, Options.KeepStreamsOpen))
|
||||
{
|
||||
options = (Options)FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
|
||||
}
|
||||
return new TarVolume(file.OpenRead(), options).AsEnumerable();
|
||||
|
||||
return new TarVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -135,9 +106,9 @@ namespace SharpCompress.Archive.Tar
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal TarArchive(Stream stream, Options options)
|
||||
: base(ArchiveType.Tar, stream, options)
|
||||
/// <param name="readerOptions"></param>
|
||||
internal TarArchive(Stream stream, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Tar, stream, readerOptions)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -146,9 +117,9 @@ namespace SharpCompress.Archive.Tar
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
return new TarVolume(streams.First(), options).AsEnumerable();
|
||||
return new TarVolume(streams.First(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
|
||||
@@ -159,7 +130,7 @@ namespace SharpCompress.Archive.Tar
|
||||
{
|
||||
if (header != null)
|
||||
{
|
||||
if (header.EntryType == EntryType.LongName)
|
||||
if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME)
|
||||
{
|
||||
previousHeader = header;
|
||||
}
|
||||
@@ -172,14 +143,16 @@ namespace SharpCompress.Archive.Tar
|
||||
|
||||
var oldStreamPos = stream.Position;
|
||||
|
||||
using(var entryStream = entry.OpenEntryStream())
|
||||
using(var memoryStream = new MemoryStream())
|
||||
using (var entryStream = entry.OpenEntryStream())
|
||||
{
|
||||
entryStream.TransferTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
var bytes = memoryStream.ToArray();
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
entryStream.TransferTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
header.Name = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length).TrimNulls();
|
||||
header.Name = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length).TrimNulls();
|
||||
}
|
||||
}
|
||||
|
||||
stream.Position = oldStreamPos;
|
||||
@@ -198,17 +171,17 @@ namespace SharpCompress.Archive.Tar
|
||||
}
|
||||
|
||||
protected override TarArchiveEntry CreateEntryInternal(string filePath, Stream source,
|
||||
long size, DateTime? modified, bool closeStream)
|
||||
long size, DateTime? modified, bool closeStream)
|
||||
{
|
||||
return new TarWritableArchiveEntry(this, source, CompressionType.Unknown, filePath, size, modified,
|
||||
closeStream);
|
||||
}
|
||||
|
||||
protected override void SaveTo(Stream stream, CompressionInfo compressionInfo,
|
||||
protected override void SaveTo(Stream stream, WriterOptions options,
|
||||
IEnumerable<TarArchiveEntry> oldEntries,
|
||||
IEnumerable<TarArchiveEntry> newEntries)
|
||||
{
|
||||
using (var writer = new TarWriter(stream, compressionInfo))
|
||||
using (var writer = new TarWriter(stream, options))
|
||||
{
|
||||
foreach (var entry in oldEntries.Concat(newEntries)
|
||||
.Where(x => !x.IsDirectory))
|
||||
@@ -3,7 +3,7 @@ using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar;
|
||||
|
||||
namespace SharpCompress.Archive.Tar
|
||||
namespace SharpCompress.Archives.Tar
|
||||
{
|
||||
public class TarArchiveEntry : TarEntry, IArchiveEntry
|
||||
{
|
||||
@@ -19,12 +19,10 @@ namespace SharpCompress.Archive.Tar
|
||||
}
|
||||
|
||||
#region IArchiveEntry Members
|
||||
public IArchive Archive { get; private set; }
|
||||
|
||||
public bool IsComplete
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public IArchive Archive { get; }
|
||||
|
||||
public bool IsComplete => true;
|
||||
|
||||
#endregion
|
||||
}
|
||||
65
src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs
Normal file
65
src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archives.Tar
|
||||
{
|
||||
internal class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
|
||||
internal TarWritableArchiveEntry(TarArchive archive, Stream stream, CompressionType compressionType,
|
||||
string path, long size, DateTime? lastModified, bool closeStream)
|
||||
: base(archive, null, compressionType)
|
||||
{
|
||||
this.stream = stream;
|
||||
Key = path;
|
||||
Size = size;
|
||||
LastModifiedTime = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
}
|
||||
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string Key { get; }
|
||||
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size { get; }
|
||||
|
||||
public override DateTime? LastModifiedTime { get; }
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
|
||||
public override bool IsSplit => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => throw new NotImplementedException();
|
||||
Stream IWritableArchiveEntry.Stream => stream;
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return new NonDisposingStream(stream);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressor.Deflate;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Reader.Zip;
|
||||
using SharpCompress.Writer.Zip;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Zip;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Zip;
|
||||
|
||||
namespace SharpCompress.Archive.Zip
|
||||
namespace SharpCompress.Archives.Zip
|
||||
{
|
||||
public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
{
|
||||
@@ -27,68 +28,33 @@ namespace SharpCompress.Archive.Zip
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="password"></param>
|
||||
public static ZipArchive Open(string filePath, string password = null)
|
||||
{
|
||||
return Open(filePath, Options.None, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="password"></param>
|
||||
public static ZipArchive Open(FileInfo fileInfo, string password = null)
|
||||
{
|
||||
return Open(fileInfo, Options.None, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static ZipArchive Open(string filePath, Options options, string password = null)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static ZipArchive Open(string filePath, ReaderOptions readerOptions = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), options, password);
|
||||
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static ZipArchive Open(FileInfo fileInfo, Options options, string password = null)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static ZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new ZipArchive(fileInfo, options, password);
|
||||
return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="password"></param>
|
||||
public static ZipArchive Open(Stream stream, string password = null)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static ZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return Open(stream, Options.None, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
public static ZipArchive Open(Stream stream, Options options, string password = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new ZipArchive(stream, options, password);
|
||||
return new ZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
@@ -121,7 +87,7 @@ namespace SharpCompress.Archive.Zip
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Enum.IsDefined(typeof (ZipHeaderType), header.ZipHeaderType);
|
||||
return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{
|
||||
@@ -134,25 +100,21 @@ namespace SharpCompress.Archive.Zip
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
internal ZipArchive(FileInfo fileInfo, Options options, string password = null)
|
||||
: base(ArchiveType.Zip, fileInfo, options)
|
||||
/// <param name="readerOptions"></param>
|
||||
internal ZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Zip, fileInfo, readerOptions)
|
||||
{
|
||||
headerFactory = new SeekableZipHeaderFactory(password);
|
||||
headerFactory = new SeekableZipHeaderFactory(readerOptions.Password);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(FileInfo file, Options options)
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
if (FlagUtility.HasFlag(options, Options.KeepStreamsOpen))
|
||||
{
|
||||
options = (Options)FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
|
||||
}
|
||||
return new ZipVolume(file.OpenRead(), options).AsEnumerable();
|
||||
return new ZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -165,17 +127,16 @@ namespace SharpCompress.Archive.Zip
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
internal ZipArchive(Stream stream, Options options, string password = null)
|
||||
: base(ArchiveType.Zip, stream, options)
|
||||
/// <param name="readerOptions"></param>
|
||||
internal ZipArchive(Stream stream, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Zip, stream, readerOptions)
|
||||
{
|
||||
headerFactory = new SeekableZipHeaderFactory(password);
|
||||
headerFactory = new SeekableZipHeaderFactory(readerOptions.Password);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
return new ZipVolume(streams.First(), options).AsEnumerable();
|
||||
return new ZipVolume(streams.First(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
|
||||
@@ -189,43 +150,48 @@ namespace SharpCompress.Archive.Zip
|
||||
switch (h.ZipHeaderType)
|
||||
{
|
||||
case ZipHeaderType.DirectoryEntry:
|
||||
{
|
||||
yield return new ZipArchiveEntry(this,
|
||||
new SeekableZipFilePart(headerFactory,
|
||||
h as DirectoryEntryHeader,
|
||||
stream));
|
||||
}
|
||||
{
|
||||
yield return new ZipArchiveEntry(this,
|
||||
new SeekableZipFilePart(headerFactory,
|
||||
h as DirectoryEntryHeader,
|
||||
stream));
|
||||
}
|
||||
break;
|
||||
case ZipHeaderType.DirectoryEnd:
|
||||
{
|
||||
byte[] bytes = (h as DirectoryEndHeader).Comment;
|
||||
volume.Comment = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length);
|
||||
yield break;
|
||||
}
|
||||
{
|
||||
byte[] bytes = (h as DirectoryEndHeader).Comment;
|
||||
volume.Comment = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveTo(Stream stream, CompressionInfo compressionInfo,
|
||||
public void SaveTo(Stream stream)
|
||||
{
|
||||
SaveTo(stream, new WriterOptions(CompressionType.Deflate));
|
||||
}
|
||||
|
||||
protected override void SaveTo(Stream stream, WriterOptions options,
|
||||
IEnumerable<ZipArchiveEntry> oldEntries,
|
||||
IEnumerable<ZipArchiveEntry> newEntries)
|
||||
{
|
||||
using (var writer = new ZipWriter(stream, compressionInfo, string.Empty))
|
||||
using (var writer = new ZipWriter(stream, new ZipWriterOptions(options)))
|
||||
{
|
||||
foreach (var entry in oldEntries.Concat(newEntries)
|
||||
.Where(x => !x.IsDirectory))
|
||||
{
|
||||
using (var entryStream = entry.OpenEntryStream())
|
||||
{
|
||||
writer.Write(entry.Key, entryStream, entry.LastModifiedTime, string.Empty);
|
||||
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override ZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
|
||||
bool closeStream)
|
||||
bool closeStream)
|
||||
{
|
||||
return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Zip;
|
||||
|
||||
namespace SharpCompress.Archive.Zip
|
||||
namespace SharpCompress.Archives.Zip
|
||||
{
|
||||
public class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
{
|
||||
@@ -19,18 +19,12 @@ namespace SharpCompress.Archive.Zip
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
public IArchive Archive { get; private set; }
|
||||
public IArchive Archive { get; }
|
||||
|
||||
public bool IsComplete
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public bool IsComplete => true;
|
||||
|
||||
#endregion
|
||||
|
||||
public string Comment
|
||||
{
|
||||
get { return (Parts.Single() as SeekableZipFilePart).Comment; }
|
||||
}
|
||||
public string Comment => (Parts.Single() as SeekableZipFilePart).Comment;
|
||||
}
|
||||
}
|
||||
68
src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs
Normal file
68
src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archives.Zip
|
||||
{
|
||||
internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
private bool isDisposed;
|
||||
|
||||
internal ZipWritableArchiveEntry(ZipArchive archive, Stream stream, string path, long size,
|
||||
DateTime? lastModified, bool closeStream)
|
||||
: base(archive, null)
|
||||
{
|
||||
this.stream = stream;
|
||||
Key = path;
|
||||
Size = size;
|
||||
LastModifiedTime = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
}
|
||||
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string Key { get; }
|
||||
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size { get; }
|
||||
|
||||
public override DateTime? LastModifiedTime { get; }
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
|
||||
public override bool IsSplit => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => throw new NotImplementedException();
|
||||
|
||||
Stream IWritableArchiveEntry.Stream => stream;
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return new NonDisposingStream(stream);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream && !isDisposed)
|
||||
{
|
||||
stream.Dispose();
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,24 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
||||
[assembly: AssemblyTitle("SharpCompress")]
|
||||
[assembly: AssemblyProduct("SharpCompress")]
|
||||
|
||||
|
||||
[assembly: InternalsVisibleTo("SharpCompress.Test")]
|
||||
[assembly: InternalsVisibleTo("SharpCompress.Test.Portable")]
|
||||
|
||||
[assembly: InternalsVisibleTo("SharpCompress.Test" + SharpCompress.AssemblyInfo.PublicKeySuffix)]
|
||||
[assembly: InternalsVisibleTo("SharpCompress.Test.Portable" + SharpCompress.AssemblyInfo.PublicKeySuffix)]
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
namespace SharpCompress
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a static class to house the public key, to avoid repetition.
|
||||
/// </summary>
|
||||
internal static class AssemblyInfo
|
||||
{
|
||||
internal const string PublicKeySuffix =
|
||||
",PublicKey=002400000480000094000000060200000024000052534131000400000100010059acfa17d26c44" +
|
||||
"7a4d03f16eaa72c9187c04f16e6569dd168b080e39a6f5c9fd00f28c768cd8e9a089d5a0e1b34c" +
|
||||
"cd971488e7afe030ce5ce8df2053cf12ec89f6d38065c434c09ee6af3ee284c5dc08f44774b679" +
|
||||
"bf39298e57efe30d4b00aecf9e4f6f8448b2cb0146d8956dfcab606cc64a0ac38c60a7d78b0d65" +
|
||||
"d3b98dc0";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ namespace SharpCompress.Common
|
||||
Item = entry;
|
||||
}
|
||||
|
||||
public T Item { get; private set; }
|
||||
public T Item { get; }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@
|
||||
Zip,
|
||||
Tar,
|
||||
SevenZip,
|
||||
GZip,
|
||||
GZip
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using SharpCompress.Compressor.Deflate;
|
||||
|
||||
namespace SharpCompress.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Detailed compression properties when saving.
|
||||
/// </summary>
|
||||
public class CompressionInfo
|
||||
{
|
||||
public CompressionInfo()
|
||||
{
|
||||
DeflateCompressionLevel = CompressionLevel.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The algorthm to use. Must be valid for the format type.
|
||||
/// </summary>
|
||||
public CompressionType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default.
|
||||
/// </summary>
|
||||
public CompressionLevel DeflateCompressionLevel { get; set; }
|
||||
|
||||
public static implicit operator CompressionInfo(CompressionType compressionType)
|
||||
{
|
||||
return new CompressionInfo() {Type = compressionType};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
LZMA,
|
||||
BCJ,
|
||||
BCJ2,
|
||||
Unknown,
|
||||
LZip,
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
@@ -70,16 +70,11 @@ namespace SharpCompress.Common
|
||||
|
||||
internal virtual void Close()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry file attribute.
|
||||
/// </summary>
|
||||
public virtual int? Attrib
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public virtual int? Attrib => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Reader;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common
|
||||
{
|
||||
public class EntryStream : Stream
|
||||
{
|
||||
public IReader Reader { get; private set; }
|
||||
private Stream stream;
|
||||
public IReader Reader { get; }
|
||||
private readonly Stream stream;
|
||||
private bool completed;
|
||||
private bool isDisposed;
|
||||
|
||||
internal EntryStream(IReader reader, Stream stream)
|
||||
{
|
||||
this.Reader = reader;
|
||||
Reader = reader;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@@ -44,36 +44,20 @@ namespace SharpCompress.Common
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace SharpCompress.Common
|
||||
{
|
||||
return ((bitField & flag) == flag);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the flag is set on the specified bit field.
|
||||
/// Currently only works with 32-bit bitfields.
|
||||
@@ -75,7 +75,6 @@ namespace SharpCompress.Common
|
||||
return ((bitField & flag) == flag);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets a bit-field to either on or off for the specified flag.
|
||||
/// </summary>
|
||||
@@ -83,9 +82,9 @@ namespace SharpCompress.Common
|
||||
/// <param name="flag">Flag to change</param>
|
||||
/// <param name="on">bool</param>
|
||||
/// <returns>The flagged variable with the flag changed</returns>
|
||||
public static long SetFlag(long bitField, long flag, bool @on)
|
||||
public static long SetFlag(long bitField, long flag, bool on)
|
||||
{
|
||||
if (@on)
|
||||
if (on)
|
||||
{
|
||||
return bitField | flag;
|
||||
}
|
||||
@@ -100,10 +99,10 @@ namespace SharpCompress.Common
|
||||
/// <param name="flag">Flag to change</param>
|
||||
/// <param name="on">bool</param>
|
||||
/// <returns>The flagged variable with the flag changed</returns>
|
||||
public static long SetFlag<T>(T bitField, T flag, bool @on)
|
||||
public static long SetFlag<T>(T bitField, T flag, bool on)
|
||||
where T : struct
|
||||
{
|
||||
return SetFlag(Convert.ToInt64(bitField), Convert.ToInt64(flag), @on);
|
||||
return SetFlag(Convert.ToInt64(bitField), Convert.ToInt64(flag), on);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,70 +13,31 @@ namespace SharpCompress.Common.GZip
|
||||
this.filePart = filePart;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType
|
||||
{
|
||||
get { return CompressionType.GZip; }
|
||||
}
|
||||
public override CompressionType CompressionType => CompressionType.GZip;
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return filePart.FilePartName; }
|
||||
}
|
||||
public override string Key => filePart.FilePartName;
|
||||
|
||||
public override long CompressedSize
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
public override long Size => 0;
|
||||
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return filePart.DateModified; }
|
||||
}
|
||||
public override DateTime? LastModifiedTime => filePart.DateModified;
|
||||
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsDirectory => false;
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsSplit => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { return filePart.AsEnumerable<FilePart>(); }
|
||||
}
|
||||
internal override IEnumerable<FilePart> Parts => filePart.AsEnumerable<FilePart>();
|
||||
|
||||
internal static IEnumerable<GZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.Compressor;
|
||||
using SharpCompress.Compressor.Deflate;
|
||||
using SharpCompress.Converter;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Converters;
|
||||
|
||||
namespace SharpCompress.Common.GZip
|
||||
{
|
||||
@@ -15,15 +15,15 @@ namespace SharpCompress.Common.GZip
|
||||
internal GZipFilePart(Stream stream)
|
||||
{
|
||||
ReadAndValidateGzipHeader(stream);
|
||||
EntryStartPosition = stream.Position;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal long EntryStartPosition { get; }
|
||||
|
||||
internal DateTime? DateModified { get; private set; }
|
||||
|
||||
internal override string FilePartName
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
internal override string FilePartName => name;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
@@ -43,13 +43,19 @@ namespace SharpCompress.Common.GZip
|
||||
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (n == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (n != 10)
|
||||
{
|
||||
throw new ZlibException("Not a valid GZIP stream.");
|
||||
}
|
||||
|
||||
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
|
||||
{
|
||||
throw new ZlibException("Bad GZIP header.");
|
||||
}
|
||||
|
||||
Int32 timet = DataConverter.LittleEndian.GetInt32(header, 4);
|
||||
DateModified = TarHeader.Epoch.AddSeconds(timet);
|
||||
@@ -58,7 +64,7 @@ namespace SharpCompress.Common.GZip
|
||||
// read and discard extra field
|
||||
n = stream.Read(header, 0, 2); // 2-byte length field
|
||||
|
||||
Int16 extraLength = (Int16) (header[0] + header[1]*256);
|
||||
Int16 extraLength = (Int16)(header[0] + header[1] * 256);
|
||||
byte[] extra = new byte[extraLength];
|
||||
n = stream.Read(extra, 0, extra.Length);
|
||||
if (n != extraLength)
|
||||
@@ -67,18 +73,23 @@ namespace SharpCompress.Common.GZip
|
||||
}
|
||||
}
|
||||
if ((header[3] & 0x08) == 0x08)
|
||||
{
|
||||
name = ReadZeroTerminatedString(stream);
|
||||
}
|
||||
if ((header[3] & 0x10) == 0x010)
|
||||
{
|
||||
ReadZeroTerminatedString(stream);
|
||||
}
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
{
|
||||
stream.ReadByte(); // CRC16, ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string ReadZeroTerminatedString(Stream stream)
|
||||
{
|
||||
byte[] buf1 = new byte[1];
|
||||
var list = new System.Collections.Generic.List<byte>();
|
||||
var list = new List<byte>();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
@@ -96,7 +107,8 @@ namespace SharpCompress.Common.GZip
|
||||
{
|
||||
list.Add(buf1[0]);
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
while (!done);
|
||||
byte[] a = list.ToArray();
|
||||
return ArchiveEncoding.Default.GetString(a, 0, a.Length);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.GZip
|
||||
{
|
||||
public class GZipVolume : Volume
|
||||
{
|
||||
public GZipVolume(Stream stream, Options options)
|
||||
public GZipVolume(Stream stream, ReaderOptions options)
|
||||
: base(stream, options)
|
||||
{
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
public GZipVolume(FileInfo fileInfo, Options options)
|
||||
public GZipVolume(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(fileInfo.OpenRead(), options)
|
||||
{
|
||||
options.LeaveStreamOpen = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
public override bool IsFirstVolume
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool IsFirstVolume => true;
|
||||
|
||||
public override bool IsMultiVolume
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool IsMultiVolume => true;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
#if !NO_FILE
|
||||
|
||||
#if !NO_FILE
|
||||
using System.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common
|
||||
{
|
||||
internal static class IEntryExtensions
|
||||
{
|
||||
internal static void PreserveExtractionOptions(this IEntry entry, string destinationFileName,
|
||||
ExtractOptions options)
|
||||
ExtractionOptions options)
|
||||
{
|
||||
if (options.HasFlag(ExtractOptions.PreserveFileTime) || options.HasFlag(ExtractOptions.PreserveAttributes))
|
||||
if (options.PreserveFileTime || options.PreserveAttributes)
|
||||
{
|
||||
FileInfo nf = new FileInfo(destinationFileName);
|
||||
if (!nf.Exists)
|
||||
@@ -17,7 +19,7 @@ namespace SharpCompress.Common
|
||||
}
|
||||
|
||||
// update file time to original packed time
|
||||
if (options.HasFlag(ExtractOptions.PreserveFileTime))
|
||||
if (options.PreserveFileTime)
|
||||
{
|
||||
if (entry.CreatedTime.HasValue)
|
||||
{
|
||||
@@ -35,7 +37,7 @@ namespace SharpCompress.Common
|
||||
}
|
||||
}
|
||||
|
||||
if (options.HasFlag(ExtractOptions.PreserveAttributes))
|
||||
if (options.PreserveAttributes)
|
||||
{
|
||||
if (entry.Attrib.HasValue)
|
||||
{
|
||||
@@ -46,4 +48,4 @@ namespace SharpCompress.Common
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
|
||||
#if !NO_FILE
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common
|
||||
{
|
||||
[Flags]
|
||||
public enum Options
|
||||
{
|
||||
/// <summary>
|
||||
/// No options specified
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// SharpCompress will keep the supplied streams open
|
||||
/// </summary>
|
||||
KeepStreamsOpen = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
|
||||
/// </summary>
|
||||
LookForHeader = 2,
|
||||
}
|
||||
}
|
||||
10
src/SharpCompress/Common/OptionsBase.cs
Normal file
10
src/SharpCompress/Common/OptionsBase.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace SharpCompress.Common
|
||||
{
|
||||
public class OptionsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// SharpCompress will keep the supplied streams open. Default is true.
|
||||
/// </summary>
|
||||
public bool LeaveStreamOpen { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
}
|
||||
}
|
||||
|
||||
internal ArchiveFlags ArchiveHeaderFlags
|
||||
{
|
||||
get { return (ArchiveFlags) base.Flags; }
|
||||
}
|
||||
internal ArchiveFlags ArchiveHeaderFlags => (ArchiveFlags)Flags;
|
||||
|
||||
internal short HighPosAv { get; private set; }
|
||||
|
||||
@@ -28,9 +25,6 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
|
||||
internal byte EncryptionVersion { get; private set; }
|
||||
|
||||
public bool HasPassword
|
||||
{
|
||||
get { return ArchiveHeaderFlags.HasFlag(ArchiveFlags.PASSWORD); }
|
||||
}
|
||||
public bool HasPassword => ArchiveHeaderFlags.HasFlag(ArchiveFlags.PASSWORD);
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
}
|
||||
}
|
||||
|
||||
internal EndArchiveFlags EndArchiveFlags
|
||||
{
|
||||
get { return (EndArchiveFlags) base.Flags; }
|
||||
}
|
||||
internal EndArchiveFlags EndArchiveFlags => (EndArchiveFlags)Flags;
|
||||
|
||||
internal int? ArchiveCRC { get; private set; }
|
||||
|
||||
|
||||
@@ -52,50 +52,50 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
switch (HeaderType)
|
||||
{
|
||||
case HeaderType.FileHeader:
|
||||
{
|
||||
if (FileFlags.HasFlag(FileFlags.UNICODE))
|
||||
{
|
||||
if (FileFlags.HasFlag(FileFlags.UNICODE))
|
||||
int length = 0;
|
||||
while (length < fileNameBytes.Length
|
||||
&& fileNameBytes[length] != 0)
|
||||
{
|
||||
int length = 0;
|
||||
while (length < fileNameBytes.Length
|
||||
&& fileNameBytes[length] != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
if (length != nameSize)
|
||||
{
|
||||
length++;
|
||||
FileName = FileNameDecoder.Decode(fileNameBytes, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileName = DecodeDefault(fileNameBytes);
|
||||
}
|
||||
length++;
|
||||
}
|
||||
if (length != nameSize)
|
||||
{
|
||||
length++;
|
||||
FileName = FileNameDecoder.Decode(fileNameBytes, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileName = DecodeDefault(fileNameBytes);
|
||||
}
|
||||
FileName = ConvertPath(FileName, HostOS);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileName = DecodeDefault(fileNameBytes);
|
||||
}
|
||||
FileName = ConvertPath(FileName, HostOS);
|
||||
}
|
||||
break;
|
||||
case HeaderType.NewSubHeader:
|
||||
{
|
||||
int datasize = HeaderSize - NEWLHD_SIZE - nameSize;
|
||||
if (FileFlags.HasFlag(FileFlags.SALT))
|
||||
{
|
||||
int datasize = HeaderSize - NEWLHD_SIZE - nameSize;
|
||||
if (FileFlags.HasFlag(FileFlags.SALT))
|
||||
{
|
||||
datasize -= SALT_SIZE;
|
||||
}
|
||||
if (datasize > 0)
|
||||
{
|
||||
SubData = reader.ReadBytes(datasize);
|
||||
}
|
||||
|
||||
if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes))
|
||||
{
|
||||
RecoverySectors = SubData[8] + (SubData[9] << 8)
|
||||
+ (SubData[10] << 16) + (SubData[11] << 24);
|
||||
}
|
||||
datasize -= SALT_SIZE;
|
||||
}
|
||||
if (datasize > 0)
|
||||
{
|
||||
SubData = reader.ReadBytes(datasize);
|
||||
}
|
||||
|
||||
if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes))
|
||||
{
|
||||
RecoverySectors = SubData[8] + (SubData[9] << 8)
|
||||
+ (SubData[10] << 16) + (SubData[11] << 24);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
byte b = reader.ReadByte();
|
||||
nanosecondHundreds |= (((uint)b) << ((j + 3 - count) * 8));
|
||||
}
|
||||
|
||||
//10^-7 to 10^-3
|
||||
return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4));
|
||||
}
|
||||
@@ -164,25 +165,13 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
#if NO_FILE
|
||||
return path.Replace('\\', '/');
|
||||
#else
|
||||
switch (os)
|
||||
if (Path.DirectorySeparatorChar == '/')
|
||||
{
|
||||
case HostOS.MacOS:
|
||||
case HostOS.Unix:
|
||||
{
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
{
|
||||
return path.Replace('/', '\\');
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (Path.DirectorySeparatorChar == '/')
|
||||
{
|
||||
return path.Replace('\\', '/');
|
||||
}
|
||||
}
|
||||
break;
|
||||
return path.Replace('\\', '/');
|
||||
}
|
||||
else if (Path.DirectorySeparatorChar == '\\')
|
||||
{
|
||||
return path.Replace('/', '\\');
|
||||
}
|
||||
return path;
|
||||
#endif
|
||||
@@ -207,10 +196,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
|
||||
internal int FileAttributes { get; private set; }
|
||||
|
||||
internal FileFlags FileFlags
|
||||
{
|
||||
get { return (FileFlags)base.Flags; }
|
||||
}
|
||||
internal FileFlags FileFlags => (FileFlags)Flags;
|
||||
|
||||
internal long CompressedSize { get; private set; }
|
||||
internal long UncompressedSize { get; private set; }
|
||||
|
||||
@@ -32,19 +32,19 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
switch (flags >> 6)
|
||||
{
|
||||
case 0:
|
||||
buf.Append((char) (GetChar(name, encPos++)));
|
||||
buf.Append((char)(GetChar(name, encPos++)));
|
||||
++decPos;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
buf.Append((char) (GetChar(name, encPos++) + (highByte << 8)));
|
||||
buf.Append((char)(GetChar(name, encPos++) + (highByte << 8)));
|
||||
++decPos;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
low = GetChar(name, encPos);
|
||||
high = GetChar(name, encPos + 1);
|
||||
buf.Append((char) ((high << 8) + low));
|
||||
buf.Append((char)((high << 8) + low));
|
||||
++decPos;
|
||||
encPos += 2;
|
||||
break;
|
||||
@@ -57,14 +57,14 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
for (length = (length & 0x7f) + 2; length > 0 && decPos < name.Length; length--, decPos++)
|
||||
{
|
||||
low = (GetChar(name, decPos) + correction) & 0xff;
|
||||
buf.Append((char) ((highByte << 8) + low));
|
||||
buf.Append((char)((highByte << 8) + low));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (length += 2; length > 0 && decPos < name.Length; length--, decPos++)
|
||||
{
|
||||
buf.Append((char) (GetChar(name, decPos)));
|
||||
buf.Append((char)(GetChar(name, decPos)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -13,12 +13,12 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
ProtectHeader = 0x78,
|
||||
SignHeader = 0x79,
|
||||
NewSubHeader = 0x7a,
|
||||
EndArchiveHeader = 0x7b,
|
||||
EndArchiveHeader = 0x7b
|
||||
}
|
||||
|
||||
internal enum HeaderFlags : short
|
||||
{
|
||||
LONG_BLOCK = -0x8000,
|
||||
LONG_BLOCK = -0x8000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@@ -33,7 +33,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
PROTECT = 0x0040,
|
||||
PASSWORD = 0x0080,
|
||||
FIRSTVOLUME = 0x0100,
|
||||
ENCRYPTVER = 0x0200,
|
||||
ENCRYPTVER = 0x0200
|
||||
}
|
||||
|
||||
internal enum HostOS
|
||||
@@ -70,16 +70,15 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
SALT = 0x0400,
|
||||
VERSION = 0x0800,
|
||||
EXTTIME = 0x1000,
|
||||
EXTFLAGS = 0x2000,
|
||||
EXTFLAGS = 0x2000
|
||||
}
|
||||
|
||||
|
||||
[Flags]
|
||||
internal enum EndArchiveFlags
|
||||
{
|
||||
EARC_NEXT_VOLUME = 0x0001,
|
||||
EARC_DATACRC = 0x0002,
|
||||
EARC_REVSPACE = 0x0004,
|
||||
EARC_VOLNUMBER = 0x0008,
|
||||
EARC_VOLNUMBER = 0x0008
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,14 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
|
||||
//internal static final NewSubHeaderType SUBHEAD_TYPE_BEOSEA = new NewSubHeaderType(new byte[]{'E','A','B','E'});
|
||||
|
||||
private byte[] bytes;
|
||||
private readonly byte[] bytes;
|
||||
|
||||
private NewSubHeaderType(params char[] chars)
|
||||
{
|
||||
bytes = new byte[chars.Length];
|
||||
for (int i = 0; i < chars.Length; ++i)
|
||||
{
|
||||
bytes[i] = (byte) chars[i];
|
||||
bytes[i] = (byte)chars[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
Mark = reader.ReadBytes(8);
|
||||
}
|
||||
|
||||
internal uint DataSize { get { return AdditionalSize; } }
|
||||
internal uint DataSize => AdditionalSize;
|
||||
internal byte Version { get; private set; }
|
||||
internal ushort RecSectors { get; private set; }
|
||||
internal uint TotalBlocks { get; private set; }
|
||||
internal byte[] Mark { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers
|
||||
@@ -18,14 +19,14 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
ReadBytes = baseHeader.ReadBytes;
|
||||
}
|
||||
|
||||
internal static RarHeader Create(MarkingBinaryReader reader)
|
||||
internal static RarHeader Create(RarCrcBinaryReader reader)
|
||||
{
|
||||
try
|
||||
{
|
||||
RarHeader header = new RarHeader();
|
||||
|
||||
reader.Mark();
|
||||
header.ReadFromReader(reader);
|
||||
header.ReadStartFromReader(reader);
|
||||
header.ReadBytes += reader.CurrentReadByteCount;
|
||||
|
||||
return header;
|
||||
@@ -35,9 +36,11 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
protected virtual void ReadFromReader(MarkingBinaryReader reader)
|
||||
|
||||
private void ReadStartFromReader(RarCrcBinaryReader reader)
|
||||
{
|
||||
HeadCRC = reader.ReadInt16();
|
||||
HeadCRC = reader.ReadUInt16();
|
||||
reader.ResetCrc();
|
||||
HeaderType = (HeaderType)(reader.ReadByte() & 0xff);
|
||||
Flags = reader.ReadInt16();
|
||||
HeaderSize = reader.ReadInt16();
|
||||
@@ -47,7 +50,11 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
}
|
||||
}
|
||||
|
||||
internal T PromoteHeader<T>(MarkingBinaryReader reader)
|
||||
protected virtual void ReadFromReader(MarkingBinaryReader reader) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal T PromoteHeader<T>(RarCrcBinaryReader reader)
|
||||
where T : RarHeader, new()
|
||||
{
|
||||
T header = new T();
|
||||
@@ -64,9 +71,21 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
reader.ReadBytes(headerSizeDiff);
|
||||
}
|
||||
|
||||
VerifyHeaderCrc(reader.GetCrc());
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private void VerifyHeaderCrc(ushort crc) {
|
||||
if (HeaderType != HeaderType.MarkHeader)
|
||||
{
|
||||
if (crc != HeadCRC)
|
||||
{
|
||||
throw new InvalidFormatException("rar header crc mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void PostReadingBytes(MarkingBinaryReader reader)
|
||||
{
|
||||
}
|
||||
@@ -76,7 +95,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
/// </summary>
|
||||
protected long ReadBytes { get; private set; }
|
||||
|
||||
protected short HeadCRC { get; private set; }
|
||||
protected ushort HeadCRC { get; private set; }
|
||||
|
||||
internal HeaderType HeaderType { get; private set; }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
@@ -9,21 +10,19 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136
|
||||
|
||||
internal RarHeaderFactory(StreamingMode mode, Options options, string password = null)
|
||||
internal RarHeaderFactory(StreamingMode mode, ReaderOptions options)
|
||||
{
|
||||
StreamingMode = mode;
|
||||
Options = options;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
private Options Options { get; set; }
|
||||
public string Password { get; private set; }
|
||||
internal StreamingMode StreamingMode { get; private set; }
|
||||
private ReaderOptions Options { get; }
|
||||
internal StreamingMode StreamingMode { get; }
|
||||
internal bool IsEncrypted { get; private set; }
|
||||
|
||||
|
||||
internal IEnumerable<RarHeader> ReadHeaders(Stream stream)
|
||||
{
|
||||
if (Options.HasFlag(Options.LookForHeader))
|
||||
if (Options.LookForHeader)
|
||||
{
|
||||
stream = CheckSFX(stream);
|
||||
}
|
||||
@@ -91,7 +90,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!Options.HasFlag(Options.KeepStreamsOpen))
|
||||
if (!Options.LeaveStreamOpen)
|
||||
{
|
||||
#if NET35
|
||||
reader.Close();
|
||||
@@ -114,15 +113,14 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
return rewindableStream;
|
||||
}
|
||||
|
||||
|
||||
private RarHeader ReadNextHeader(Stream stream)
|
||||
{
|
||||
#if !NO_CRYPTO
|
||||
var reader = new RarCryptoBinaryReader(stream, Password);
|
||||
var reader = new RarCryptoBinaryReader(stream, Options.Password);
|
||||
|
||||
if (IsEncrypted)
|
||||
{
|
||||
if (Password == null)
|
||||
if (Options.Password == null)
|
||||
{
|
||||
throw new CryptographicException("Encrypted Rar archive has no password specified.");
|
||||
}
|
||||
@@ -131,7 +129,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
reader.InitializeAes(salt);
|
||||
}
|
||||
#else
|
||||
var reader = new MarkingBinaryReader(stream);
|
||||
var reader = new RarCrcBinaryReader(stream);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -143,110 +141,110 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
switch (header.HeaderType)
|
||||
{
|
||||
case HeaderType.ArchiveHeader:
|
||||
{
|
||||
var ah = header.PromoteHeader<ArchiveHeader>(reader);
|
||||
IsEncrypted = ah.HasPassword;
|
||||
return ah;
|
||||
}
|
||||
{
|
||||
var ah = header.PromoteHeader<ArchiveHeader>(reader);
|
||||
IsEncrypted = ah.HasPassword;
|
||||
return ah;
|
||||
}
|
||||
case HeaderType.MarkHeader:
|
||||
{
|
||||
return header.PromoteHeader<MarkHeader>(reader);
|
||||
}
|
||||
{
|
||||
return header.PromoteHeader<MarkHeader>(reader);
|
||||
}
|
||||
|
||||
case HeaderType.ProtectHeader:
|
||||
{
|
||||
ProtectHeader ph = header.PromoteHeader<ProtectHeader>(reader);
|
||||
|
||||
// skip the recovery record data, we do not use it.
|
||||
switch (StreamingMode)
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
reader.BaseStream.Position += ph.DataSize;
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
reader.BaseStream.Skip(ph.DataSize);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
}
|
||||
{
|
||||
ProtectHeader ph = header.PromoteHeader<ProtectHeader>(reader);
|
||||
|
||||
return ph;
|
||||
// skip the recovery record data, we do not use it.
|
||||
switch (StreamingMode)
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
reader.BaseStream.Position += ph.DataSize;
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
reader.BaseStream.Skip(ph.DataSize);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
}
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
case HeaderType.NewSubHeader:
|
||||
{
|
||||
FileHeader fh = header.PromoteHeader<FileHeader>(reader);
|
||||
switch (StreamingMode)
|
||||
{
|
||||
FileHeader fh = header.PromoteHeader<FileHeader>(reader);
|
||||
switch (StreamingMode)
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
fh.DataStartPosition = reader.BaseStream.Position;
|
||||
reader.BaseStream.Position += fh.CompressedSize;
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
//skip the data because it's useless?
|
||||
reader.BaseStream.Skip(fh.CompressedSize);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
fh.DataStartPosition = reader.BaseStream.Position;
|
||||
reader.BaseStream.Position += fh.CompressedSize;
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
//skip the data because it's useless?
|
||||
reader.BaseStream.Skip(fh.CompressedSize);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
return fh;
|
||||
}
|
||||
return fh;
|
||||
}
|
||||
case HeaderType.FileHeader:
|
||||
{
|
||||
FileHeader fh = header.PromoteHeader<FileHeader>(reader);
|
||||
switch (StreamingMode)
|
||||
{
|
||||
FileHeader fh = header.PromoteHeader<FileHeader>(reader);
|
||||
switch (StreamingMode)
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
fh.DataStartPosition = reader.BaseStream.Position;
|
||||
reader.BaseStream.Position += fh.CompressedSize;
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
|
||||
if (fh.Salt == null)
|
||||
{
|
||||
fh.PackedStream = ms;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !NO_CRYPTO
|
||||
fh.PackedStream = new RarCryptoWrapper(ms, Password, fh.Salt);
|
||||
#else
|
||||
throw new NotSupportedException("RarCrypto not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
fh.DataStartPosition = reader.BaseStream.Position;
|
||||
reader.BaseStream.Position += fh.CompressedSize;
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
|
||||
if (fh.Salt == null)
|
||||
{
|
||||
fh.PackedStream = ms;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !NO_CRYPTO
|
||||
fh.PackedStream = new RarCryptoWrapper(ms, Options.Password, fh.Salt);
|
||||
#else
|
||||
throw new NotSupportedException("RarCrypto not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
return fh;
|
||||
}
|
||||
return fh;
|
||||
}
|
||||
case HeaderType.EndArchiveHeader:
|
||||
{
|
||||
return header.PromoteHeader<EndArchiveHeader>(reader);
|
||||
}
|
||||
{
|
||||
return header.PromoteHeader<EndArchiveHeader>(reader);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderType.ToString());
|
||||
}
|
||||
{
|
||||
throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs
Normal file
40
src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar {
|
||||
internal class RarCrcBinaryReader : MarkingBinaryReader {
|
||||
private uint currentCrc;
|
||||
|
||||
public RarCrcBinaryReader(Stream stream) : base(stream)
|
||||
{
|
||||
}
|
||||
|
||||
public ushort GetCrc()
|
||||
{
|
||||
return (ushort)~currentCrc;
|
||||
}
|
||||
|
||||
public void ResetCrc()
|
||||
{
|
||||
currentCrc = 0xffffffff;
|
||||
}
|
||||
|
||||
protected void UpdateCrc(byte b)
|
||||
{
|
||||
currentCrc = RarCRC.CheckCrc(currentCrc, b);
|
||||
}
|
||||
|
||||
protected byte[] ReadBytesNoCrc(int count)
|
||||
{
|
||||
return base.ReadBytes(count);
|
||||
}
|
||||
|
||||
public override byte[] ReadBytes(int count)
|
||||
{
|
||||
var result = base.ReadBytes(count);
|
||||
currentCrc = RarCRC.CheckCrc(currentCrc, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
#if !NO_CRYPTO
|
||||
|
||||
#if !NO_CRYPTO
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar
|
||||
{
|
||||
internal class RarCryptoBinaryReader : MarkingBinaryReader
|
||||
internal class RarCryptoBinaryReader : RarCrcBinaryReader
|
||||
{
|
||||
private RarRijndael rijndael;
|
||||
private byte[] salt;
|
||||
private readonly string password;
|
||||
private readonly Queue<byte> data = new Queue<byte>();
|
||||
private long readCount;
|
||||
|
||||
public RarCryptoBinaryReader(Stream stream, string password )
|
||||
: base(stream)
|
||||
@@ -18,6 +20,22 @@ namespace SharpCompress.Common.Rar
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
// track read count ourselves rather than using the underlying stream since we buffer
|
||||
public override long CurrentReadByteCount {
|
||||
get
|
||||
{
|
||||
return this.readCount;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public override void Mark() {
|
||||
this.readCount = 0;
|
||||
}
|
||||
|
||||
protected bool UseEncryption
|
||||
{
|
||||
get { return salt != null; }
|
||||
@@ -35,6 +53,7 @@ namespace SharpCompress.Common.Rar
|
||||
{
|
||||
return ReadAndDecryptBytes(count);
|
||||
}
|
||||
this.readCount += count;
|
||||
return base.ReadBytes(count);
|
||||
}
|
||||
|
||||
@@ -49,7 +68,7 @@ namespace SharpCompress.Common.Rar
|
||||
for (int i = 0; i < alignedSize / 16; i++)
|
||||
{
|
||||
//long ax = System.currentTimeMillis();
|
||||
byte[] cipherText = base.ReadBytes(16);
|
||||
byte[] cipherText = base.ReadBytesNoCrc(16);
|
||||
var readBytes = rijndael.ProcessBlock(cipherText);
|
||||
foreach (var readByte in readBytes)
|
||||
data.Enqueue(readByte);
|
||||
@@ -62,8 +81,11 @@ namespace SharpCompress.Common.Rar
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
decryptedBytes[i] = data.Dequeue();
|
||||
var b = data.Dequeue();
|
||||
decryptedBytes[i] = b;
|
||||
UpdateCrc(b);
|
||||
}
|
||||
this.readCount += count;
|
||||
return decryptedBytes;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
#if !NO_CRYPTO
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -10,71 +10,44 @@ namespace SharpCompress.Common.Rar
|
||||
/// <summary>
|
||||
/// The File's 32 bit CRC Hash
|
||||
/// </summary>
|
||||
public override long Crc
|
||||
{
|
||||
get { return FileHeader.FileCRC; }
|
||||
}
|
||||
public override long Crc => FileHeader.FileCRC;
|
||||
|
||||
/// <summary>
|
||||
/// The path of the file internal to the Rar Archive.
|
||||
/// </summary>
|
||||
public override string Key
|
||||
{
|
||||
get { return FileHeader.FileName; }
|
||||
}
|
||||
public override string Key => FileHeader.FileName;
|
||||
|
||||
/// <summary>
|
||||
/// The entry last modified time in the archive, if recorded
|
||||
/// </summary>
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return FileHeader.FileLastModifiedTime; }
|
||||
}
|
||||
public override DateTime? LastModifiedTime => FileHeader.FileLastModifiedTime;
|
||||
|
||||
/// <summary>
|
||||
/// The entry create time in the archive, if recorded
|
||||
/// </summary>
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return FileHeader.FileCreatedTime; }
|
||||
}
|
||||
public override DateTime? CreatedTime => FileHeader.FileCreatedTime;
|
||||
|
||||
/// <summary>
|
||||
/// The entry last accessed time in the archive, if recorded
|
||||
/// </summary>
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return FileHeader.FileLastAccessedTime; }
|
||||
}
|
||||
public override DateTime? LastAccessedTime => FileHeader.FileLastAccessedTime;
|
||||
|
||||
/// <summary>
|
||||
/// The entry time whend archived, if recorded
|
||||
/// </summary>
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return FileHeader.FileArchivedTime; }
|
||||
}
|
||||
public override DateTime? ArchivedTime => FileHeader.FileArchivedTime;
|
||||
|
||||
/// <summary>
|
||||
/// Entry is password protected and encrypted and cannot be extracted.
|
||||
/// </summary>
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return FileHeader.FileFlags.HasFlag(FileFlags.PASSWORD); }
|
||||
}
|
||||
public override bool IsEncrypted => FileHeader.FileFlags.HasFlag(FileFlags.PASSWORD);
|
||||
|
||||
/// <summary>
|
||||
/// Entry is password protected and encrypted and cannot be extracted.
|
||||
/// </summary>
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return FileHeader.FileFlags.HasFlag(FileFlags.DIRECTORY); }
|
||||
}
|
||||
public override bool IsDirectory => FileHeader.FileFlags.HasFlag(FileFlags.DIRECTORY);
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return FileHeader.FileFlags.HasFlag(FileFlags.SPLIT_AFTER); }
|
||||
}
|
||||
public override bool IsSplit => FileHeader.FileFlags.HasFlag(FileFlags.SPLIT_AFTER);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace SharpCompress.Common.Rar
|
||||
FileHeader = fh;
|
||||
}
|
||||
|
||||
internal MarkHeader MarkHeader { get; private set; }
|
||||
internal MarkHeader MarkHeader { get; }
|
||||
|
||||
internal FileHeader FileHeader { get; private set; }
|
||||
internal FileHeader FileHeader { get; }
|
||||
|
||||
internal override Stream GetRawStream()
|
||||
{
|
||||
|
||||
@@ -74,13 +74,16 @@ namespace SharpCompress.Common.Rar
|
||||
|
||||
byte[] aesKey = new byte[CRYPTO_BLOCK_SIZE];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
aesKey[i*4 + j] = (byte)
|
||||
(((digest[i*4]*0x1000000) & 0xff000000 |
|
||||
(uint) ((digest[i*4 + 1]*0x10000) & 0xff0000) |
|
||||
(uint) ((digest[i*4 + 2]*0x100) & 0xff00) |
|
||||
(uint) (digest[i*4 + 3] & 0xff)) >> (j*8));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rijndael.Init(false, new KeyParameter(aesKey));
|
||||
|
||||
@@ -100,10 +103,14 @@ namespace SharpCompress.Common.Rar
|
||||
rijndael.ProcessBlock(cipherText, 0, plainText, 0);
|
||||
|
||||
for (int j = 0; j < plainText.Length; j++)
|
||||
{
|
||||
decryptedBytes.Add((byte) (plainText[j] ^ aesInitializationVector[j%16])); //32:114, 33:101
|
||||
}
|
||||
|
||||
for (int j = 0; j < aesInitializationVector.Length; j++)
|
||||
{
|
||||
aesInitializationVector[j] = cipherText[j];
|
||||
}
|
||||
return decryptedBytes.ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.Rar
|
||||
{
|
||||
@@ -13,20 +14,14 @@ namespace SharpCompress.Common.Rar
|
||||
public abstract class RarVolume : Volume
|
||||
{
|
||||
private readonly RarHeaderFactory headerFactory;
|
||||
|
||||
internal RarVolume(StreamingMode mode, Stream stream, string password, Options options)
|
||||
|
||||
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options)
|
||||
: base(stream, options)
|
||||
{
|
||||
headerFactory = new RarHeaderFactory(mode, options, password);
|
||||
Password = password;
|
||||
headerFactory = new RarHeaderFactory(mode, options);
|
||||
}
|
||||
|
||||
internal string Password { get; private set; }
|
||||
|
||||
internal StreamingMode Mode
|
||||
{
|
||||
get { return headerFactory.StreamingMode; }
|
||||
}
|
||||
internal StreamingMode Mode => headerFactory.StreamingMode;
|
||||
|
||||
internal abstract IEnumerable<RarFilePart> ReadFileParts();
|
||||
|
||||
@@ -35,26 +30,26 @@ namespace SharpCompress.Common.Rar
|
||||
internal IEnumerable<RarFilePart> GetVolumeFileParts()
|
||||
{
|
||||
MarkHeader previousMarkHeader = null;
|
||||
foreach (RarHeader header in headerFactory.ReadHeaders(this.Stream))
|
||||
foreach (RarHeader header in headerFactory.ReadHeaders(Stream))
|
||||
{
|
||||
switch (header.HeaderType)
|
||||
{
|
||||
case HeaderType.ArchiveHeader:
|
||||
{
|
||||
ArchiveHeader = header as ArchiveHeader;
|
||||
}
|
||||
{
|
||||
ArchiveHeader = header as ArchiveHeader;
|
||||
}
|
||||
break;
|
||||
case HeaderType.MarkHeader:
|
||||
{
|
||||
previousMarkHeader = header as MarkHeader;
|
||||
}
|
||||
{
|
||||
previousMarkHeader = header as MarkHeader;
|
||||
}
|
||||
break;
|
||||
case HeaderType.FileHeader:
|
||||
{
|
||||
FileHeader fh = header as FileHeader;
|
||||
RarFilePart fp = CreateFilePart(fh, previousMarkHeader);
|
||||
yield return fp;
|
||||
}
|
||||
{
|
||||
FileHeader fh = header as FileHeader;
|
||||
RarFilePart fp = CreateFilePart(fh, previousMarkHeader);
|
||||
yield return fp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -70,6 +65,7 @@ namespace SharpCompress.Common.Rar
|
||||
{
|
||||
throw new InvalidOperationException("ArchiveHeader should never been null in a streaming read.");
|
||||
}
|
||||
|
||||
//we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream
|
||||
GetVolumeFileParts().First();
|
||||
Stream.Position = 0;
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using System;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common
|
||||
{
|
||||
public class ReaderExtractionEventArgs<T> : EventArgs
|
||||
{
|
||||
internal ReaderExtractionEventArgs(T entry)
|
||||
internal ReaderExtractionEventArgs(T entry, ReaderProgress readerProgress = null)
|
||||
{
|
||||
Item = entry;
|
||||
ReaderProgress = readerProgress;
|
||||
}
|
||||
public T Item { get; private set; }
|
||||
|
||||
public T Item { get; }
|
||||
public ReaderProgress ReaderProgress { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Compressor.LZMA;
|
||||
using SharpCompress.Compressor.LZMA.Utilites;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.LZMA.Utilites;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
@@ -80,15 +80,19 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
// v3.13 incorrectly worked with empty folders
|
||||
// v4.07: Loop for skipping empty folders
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
if (folderIndex >= Folders.Count)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
FolderStartFileIndex.Add(i); // check it
|
||||
|
||||
if (NumUnpackStreamsVector[folderIndex] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
folderIndex++;
|
||||
}
|
||||
@@ -97,7 +101,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
FileIndexToFolderIndexMap.Add(folderIndex);
|
||||
|
||||
if (emptyStream)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
indexInFolder++;
|
||||
|
||||
@@ -128,7 +134,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
long size = 0;
|
||||
for (int i = 0; i < folder.PackStreams.Count; i++)
|
||||
{
|
||||
size += PackSizes[packStreamIndex + i];
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -139,7 +147,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
long folderStartPackPos = GetFolderStreamPos(folder, 0);
|
||||
List<long> packSizes = new List<long>();
|
||||
for (int j = 0; j < folder.PackStreams.Count; j++)
|
||||
{
|
||||
packSizes.Add(PackSizes[packStreamIndex + j]);
|
||||
}
|
||||
|
||||
return DecoderStreamHelper.CreateDecoderStream(stream, folderStartPackPos, packSizes.ToArray(), folder, pw);
|
||||
}
|
||||
@@ -153,8 +163,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
int folderIndex = FileIndexToFolderIndexMap[fileIndex];
|
||||
if (folderIndex != -1)
|
||||
{
|
||||
if (FolderStartFileIndex[folderIndex] == fileIndex)
|
||||
{
|
||||
return GetFolderFullPackSize(folderIndex);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Compressor.LZMA;
|
||||
using SharpCompress.Compressor.LZMA.Utilites;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.LZMA.Utilites;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip
|
||||
@@ -17,7 +18,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
internal long _streamEnding;
|
||||
internal byte[] _header;
|
||||
|
||||
private Dictionary<int, Stream> _cachedStreams = new Dictionary<int, Stream>();
|
||||
private readonly Dictionary<int, Stream> _cachedStreams = new Dictionary<int, Stream>();
|
||||
|
||||
internal void AddByteStream(byte[] buffer, int offset, int length)
|
||||
{
|
||||
@@ -66,7 +67,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
ulong id = _currentReader.ReadNumber();
|
||||
if (id > 25)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#if DEBUG
|
||||
Log.WriteLine("ReadId: {0}", (BlockType)id);
|
||||
#endif
|
||||
@@ -85,13 +88,17 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
private void WaitAttribute(BlockType attribute)
|
||||
{
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
BlockType? type = ReadId();
|
||||
if (type == attribute)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (type == BlockType.End)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
SkipData();
|
||||
}
|
||||
}
|
||||
@@ -99,7 +106,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
private void ReadArchiveProperties()
|
||||
{
|
||||
while (ReadId() != BlockType.End)
|
||||
{
|
||||
SkipData();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -122,7 +131,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
if ((data & mask) != 0)
|
||||
{
|
||||
bits.SetBit(i);
|
||||
}
|
||||
|
||||
mask >>= 1;
|
||||
}
|
||||
@@ -134,7 +145,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
byte allTrue = ReadByte();
|
||||
if (allTrue != 0)
|
||||
{
|
||||
return new BitVector(length, true);
|
||||
}
|
||||
|
||||
return ReadBitVector(length);
|
||||
}
|
||||
@@ -150,9 +163,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
for (int i = 0; i < numFiles; i++)
|
||||
{
|
||||
if (defined[i])
|
||||
{
|
||||
action(i, checked((long)ReadUInt64()));
|
||||
}
|
||||
else
|
||||
{
|
||||
action(i, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,9 +183,10 @@ namespace SharpCompress.Common.SevenZip
|
||||
private DateTime? TranslateTime(long? time)
|
||||
{
|
||||
if (time.HasValue)
|
||||
{
|
||||
return TranslateTime(time.Value);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ReadDateTimeVector(List<byte[]> dataVector, int numFiles, Action<int, DateTime?> action)
|
||||
@@ -185,9 +203,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
for (int i = 0; i < numFiles; i++)
|
||||
{
|
||||
if (boolVector[i])
|
||||
{
|
||||
action(i, ReadUInt32());
|
||||
}
|
||||
else
|
||||
{
|
||||
action(i, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,10 +252,14 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.WriteLine("MethodId: " + String.Join("", Enumerable.Range(0, idSize).Select(x => longID[x].ToString("x2")).ToArray()));
|
||||
#endif
|
||||
if (idSize > 8)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
ulong id = 0;
|
||||
for (int j = 0; j < idSize; j++)
|
||||
{
|
||||
id |= (ulong)longID[idSize - 1 - j] << (8 * j);
|
||||
}
|
||||
coder.MethodId = new CMethodId(id);
|
||||
|
||||
if ((mainByte & 0x10) != 0)
|
||||
@@ -264,7 +290,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
if ((mainByte & 0x80) != 0)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
numInStreams += coder.NumInStreams;
|
||||
numOutStreams += coder.NumOutStreams;
|
||||
@@ -298,9 +326,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
#endif
|
||||
|
||||
if (numInStreams < numBindPairs)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
int numPackStreams = numInStreams - numBindPairs;
|
||||
|
||||
//folder.PackStreams.Reserve(numPackStreams);
|
||||
if (numPackStreams == 1)
|
||||
{
|
||||
@@ -317,7 +348,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
if (folder.PackStreams.Count != 1)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -417,11 +450,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
#endif
|
||||
|
||||
BlockType? type;
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
type = ReadId();
|
||||
if (type == BlockType.End)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (type == BlockType.CRC)
|
||||
{
|
||||
packCRCs = ReadHashDigests(numPackStreams);
|
||||
@@ -434,7 +469,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
packCRCs = new List<uint?>(numPackStreams);
|
||||
for (int i = 0; i < numPackStreams; i++)
|
||||
{
|
||||
packCRCs.Add(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -462,13 +499,14 @@ namespace SharpCompress.Common.SevenZip
|
||||
using (CStreamSwitch streamSwitch = new CStreamSwitch())
|
||||
{
|
||||
streamSwitch.Set(this, dataVector);
|
||||
|
||||
//folders.Clear();
|
||||
//folders.Reserve(numFolders);
|
||||
folders = new List<CFolder>(numFolders);
|
||||
int index = 0;
|
||||
for (int i = 0; i < numFolders; i++)
|
||||
{
|
||||
var f = new CFolder { FirstPackStreamId = index };
|
||||
var f = new CFolder {FirstPackStreamId = index};
|
||||
folders.Add(f);
|
||||
GetNextFolderItem(f);
|
||||
index += f.PackStreams.Count;
|
||||
@@ -499,17 +537,21 @@ namespace SharpCompress.Common.SevenZip
|
||||
#endif
|
||||
}
|
||||
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
BlockType? type = ReadId();
|
||||
if (type == BlockType.End)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == BlockType.CRC)
|
||||
{
|
||||
List<uint?> crcs = ReadHashDigests(numFolders);
|
||||
for (int i = 0; i < numFolders; i++)
|
||||
{
|
||||
folders[i].UnpackCRC = crcs[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -536,7 +578,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
numUnpackStreamsInFolders = null;
|
||||
|
||||
BlockType? type;
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
type = ReadId();
|
||||
if (type == BlockType.NumUnpackStream)
|
||||
@@ -559,9 +601,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
continue;
|
||||
}
|
||||
if (type == BlockType.CRC || type == BlockType.Size)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (type == BlockType.End)
|
||||
{
|
||||
break;
|
||||
}
|
||||
SkipData();
|
||||
}
|
||||
|
||||
@@ -569,7 +615,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
numUnpackStreamsInFolders = new List<int>(folders.Count);
|
||||
for (int i = 0; i < folders.Count; i++)
|
||||
{
|
||||
numUnpackStreamsInFolders.Add(1);
|
||||
}
|
||||
}
|
||||
|
||||
unpackSizes = new List<long>(folders.Count);
|
||||
@@ -579,7 +627,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
// v4.07: we check that folder is empty
|
||||
int numSubstreams = numUnpackStreamsInFolders[i];
|
||||
if (numSubstreams == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#if DEBUG
|
||||
Log.Write("#{0} StreamSizes:", i);
|
||||
#endif
|
||||
@@ -602,7 +652,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
#endif
|
||||
}
|
||||
if (type == BlockType.Size)
|
||||
{
|
||||
type = ReadId();
|
||||
}
|
||||
|
||||
int numDigests = 0;
|
||||
int numDigestsTotal = 0;
|
||||
@@ -610,13 +662,15 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
int numSubstreams = numUnpackStreamsInFolders[i];
|
||||
if (numSubstreams != 1 || !folders[i].UnpackCRCDefined)
|
||||
{
|
||||
numDigests += numSubstreams;
|
||||
}
|
||||
numDigestsTotal += numSubstreams;
|
||||
}
|
||||
|
||||
digests = null;
|
||||
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
if (type == BlockType.CRC)
|
||||
{
|
||||
@@ -636,12 +690,16 @@ namespace SharpCompress.Common.SevenZip
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < numSubstreams; j++, digestIndex++)
|
||||
{
|
||||
digests.Add(digests2[digestIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (digestIndex != numDigests || numDigestsTotal != digests.Count)
|
||||
System.Diagnostics.Debugger.Break();
|
||||
{
|
||||
Debugger.Break();
|
||||
}
|
||||
}
|
||||
else if (type == BlockType.End)
|
||||
{
|
||||
@@ -649,7 +707,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
digests = new List<uint?>(numDigestsTotal);
|
||||
for (int i = 0; i < numDigestsTotal; i++)
|
||||
{
|
||||
digests.Add(null);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -693,7 +753,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
unpackSizes = null;
|
||||
digests = null;
|
||||
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
switch (ReadId())
|
||||
{
|
||||
@@ -768,12 +828,18 @@ namespace SharpCompress.Common.SevenZip
|
||||
byte[] data = new byte[unpackSize];
|
||||
outStream.ReadExact(data, 0, data.Length);
|
||||
if (outStream.ReadByte() >= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Decoded stream is longer than expected.");
|
||||
}
|
||||
dataVector.Add(data);
|
||||
|
||||
if (folder.UnpackCRCDefined)
|
||||
{
|
||||
if (CRC.Finish(CRC.Update(CRC.kInitCRC, data, 0, unpackSize)) != folder.UnpackCRC)
|
||||
{
|
||||
throw new InvalidOperationException("Decoded stream does not match expected CRC.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataVector;
|
||||
}
|
||||
@@ -842,10 +908,14 @@ namespace SharpCompress.Common.SevenZip
|
||||
db.Files.Clear();
|
||||
|
||||
if (type == BlockType.End)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != BlockType.FilesInfo)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
int numFiles = ReadNum();
|
||||
#if DEBUG
|
||||
@@ -853,18 +923,22 @@ namespace SharpCompress.Common.SevenZip
|
||||
#endif
|
||||
db.Files = new List<CFileItem>(numFiles);
|
||||
for (int i = 0; i < numFiles; i++)
|
||||
{
|
||||
db.Files.Add(new CFileItem());
|
||||
}
|
||||
|
||||
BitVector emptyStreamVector = new BitVector(numFiles);
|
||||
BitVector emptyFileVector = null;
|
||||
BitVector antiFileVector = null;
|
||||
int numEmptyStreams = 0;
|
||||
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
type = ReadId();
|
||||
if (type == BlockType.End)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
long size = checked((long)ReadNumber()); // TODO: throw invalid data on negative
|
||||
int oldPos = _currentReader.Offset;
|
||||
@@ -894,32 +968,34 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.Write("WinAttributes:");
|
||||
#endif
|
||||
ReadAttributeVector(dataVector, numFiles, delegate(int i, uint? attr)
|
||||
{
|
||||
// Some third party implementations established an unofficial extension
|
||||
// of the 7z archive format by placing posix file attributes in the high
|
||||
// bits of the windows file attributes. This makes use of the fact that
|
||||
// the official implementation does not perform checks on this value.
|
||||
//
|
||||
// Newer versions of the official 7z GUI client will try to parse this
|
||||
// extension, thus acknowledging the unofficial use of these bits.
|
||||
//
|
||||
// For us it is safe to just discard the upper bits if they are set and
|
||||
// keep the windows attributes from the lower bits (which should be set
|
||||
// properly even if posix file attributes are present, in order to be
|
||||
// compatible with older 7z archive readers)
|
||||
//
|
||||
// Note that the 15th bit is used by some implementations to indicate
|
||||
// presence of the extension, but not all implementations do that so
|
||||
// we can't trust that bit and must ignore it.
|
||||
//
|
||||
if (attr.HasValue && (attr.Value >> 16) != 0)
|
||||
{
|
||||
// Some third party implementations established an unofficial extension
|
||||
// of the 7z archive format by placing posix file attributes in the high
|
||||
// bits of the windows file attributes. This makes use of the fact that
|
||||
// the official implementation does not perform checks on this value.
|
||||
//
|
||||
// Newer versions of the official 7z GUI client will try to parse this
|
||||
// extension, thus acknowledging the unofficial use of these bits.
|
||||
//
|
||||
// For us it is safe to just discard the upper bits if they are set and
|
||||
// keep the windows attributes from the lower bits (which should be set
|
||||
// properly even if posix file attributes are present, in order to be
|
||||
// compatible with older 7z archive readers)
|
||||
//
|
||||
// Note that the 15th bit is used by some implementations to indicate
|
||||
// presence of the extension, but not all implementations do that so
|
||||
// we can't trust that bit and must ignore it.
|
||||
//
|
||||
if (attr.HasValue && (attr.Value >> 16) != 0)
|
||||
attr = attr.Value & 0x7FFFu;
|
||||
attr = attr.Value & 0x7FFFu;
|
||||
}
|
||||
|
||||
db.Files[i].Attrib = attr;
|
||||
db.Files[i].Attrib = attr;
|
||||
#if DEBUG
|
||||
Log.Write(" " + (attr.HasValue ? attr.Value.ToString("x8") : "n/a"));
|
||||
Log.Write(" " + (attr.HasValue ? attr.Value.ToString("x8") : "n/a"));
|
||||
#endif
|
||||
});
|
||||
});
|
||||
#if DEBUG
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
@@ -958,7 +1034,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
#if DEBUG
|
||||
Log.Write("EmptyFile: ");
|
||||
for (int i = 0; i < numEmptyStreams; i++)
|
||||
{
|
||||
Log.Write(emptyFileVector[i] ? "x" : ".");
|
||||
}
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
break;
|
||||
@@ -967,7 +1045,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
#if DEBUG
|
||||
Log.Write("Anti: ");
|
||||
for (int i = 0; i < numEmptyStreams; i++)
|
||||
{
|
||||
Log.Write(antiFileVector[i] ? "x" : ".");
|
||||
}
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
break;
|
||||
@@ -976,12 +1056,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.Write("StartPos:");
|
||||
#endif
|
||||
ReadNumberVector(dataVector, numFiles, delegate(int i, long? startPos)
|
||||
{
|
||||
db.Files[i].StartPos = startPos;
|
||||
{
|
||||
db.Files[i].StartPos = startPos;
|
||||
#if DEBUG
|
||||
Log.Write(" " + (startPos.HasValue ? startPos.Value.ToString() : "n/a"));
|
||||
Log.Write(" " + (startPos.HasValue ? startPos.Value.ToString() : "n/a"));
|
||||
#endif
|
||||
});
|
||||
});
|
||||
#if DEBUG
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
@@ -991,12 +1071,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.Write("CTime:");
|
||||
#endif
|
||||
ReadDateTimeVector(dataVector, numFiles, delegate(int i, DateTime? time)
|
||||
{
|
||||
db.Files[i].CTime = time;
|
||||
{
|
||||
db.Files[i].CTime = time;
|
||||
#if DEBUG
|
||||
Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a"));
|
||||
Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a"));
|
||||
#endif
|
||||
});
|
||||
});
|
||||
#if DEBUG
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
@@ -1006,12 +1086,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.Write("ATime:");
|
||||
#endif
|
||||
ReadDateTimeVector(dataVector, numFiles, delegate(int i, DateTime? time)
|
||||
{
|
||||
db.Files[i].ATime = time;
|
||||
{
|
||||
db.Files[i].ATime = time;
|
||||
#if DEBUG
|
||||
Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a"));
|
||||
Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a"));
|
||||
#endif
|
||||
});
|
||||
});
|
||||
#if DEBUG
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
@@ -1021,12 +1101,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.Write("MTime:");
|
||||
#endif
|
||||
ReadDateTimeVector(dataVector, numFiles, delegate(int i, DateTime? time)
|
||||
{
|
||||
db.Files[i].MTime = time;
|
||||
{
|
||||
db.Files[i].MTime = time;
|
||||
#if DEBUG
|
||||
Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a"));
|
||||
Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a"));
|
||||
#endif
|
||||
});
|
||||
});
|
||||
#if DEBUG
|
||||
Log.WriteLine();
|
||||
#endif
|
||||
@@ -1036,8 +1116,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.Write("Dummy: " + size);
|
||||
#endif
|
||||
for (long j = 0; j < size; j++)
|
||||
{
|
||||
if (ReadByte() != 0)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SkipData(size);
|
||||
@@ -1047,7 +1131,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
// since 0.3 record sizes must be correct
|
||||
bool checkRecordsSize = (db.MajorVersion > 0 || db.MinorVersion > 2);
|
||||
if (checkRecordsSize && _currentReader.Offset - oldPos != size)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
int emptyFileIndex = 0;
|
||||
@@ -1095,11 +1181,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
// TODO: Check Signature!
|
||||
_header = new byte[0x20];
|
||||
for (int offset = 0; offset < 0x20; )
|
||||
for (int offset = 0; offset < 0x20;)
|
||||
{
|
||||
int delta = stream.Read(_header, offset, 0x20 - offset);
|
||||
if (delta == 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
offset += delta;
|
||||
}
|
||||
|
||||
@@ -1109,10 +1197,14 @@ namespace SharpCompress.Common.SevenZip
|
||||
public void Close()
|
||||
{
|
||||
if (_stream != null)
|
||||
{
|
||||
_stream.Dispose();
|
||||
}
|
||||
|
||||
foreach (var stream in _cachedStreams.Values)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
_cachedStreams.Clear();
|
||||
}
|
||||
@@ -1126,7 +1218,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
db.MinorVersion = _header[7];
|
||||
|
||||
if (db.MajorVersion != 0)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
uint crcFromArchive = DataReader.Get32(_header, 8);
|
||||
long nextHeaderOffset = (long)DataReader.Get64(_header, 0xC);
|
||||
@@ -1140,7 +1234,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
crc = CRC.Finish(crc);
|
||||
|
||||
if (crc != crcFromArchive)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
db.StartPositionAfterHeader = _streamOrigin + 0x20;
|
||||
|
||||
@@ -1151,12 +1247,15 @@ namespace SharpCompress.Common.SevenZip
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
if (nextHeaderOffset < 0 || nextHeaderSize < 0 || nextHeaderSize > Int32.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (nextHeaderOffset > _streamEnding - db.StartPositionAfterHeader)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
_stream.Seek(nextHeaderOffset, SeekOrigin.Current);
|
||||
|
||||
@@ -1164,7 +1263,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
_stream.ReadExact(header, 0, header.Length);
|
||||
|
||||
if (CRC.Finish(CRC.Update(CRC.kInitCRC, header, 0, header.Length)) != nextHeaderCrc)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
using (CStreamSwitch streamSwitch = new CStreamSwitch())
|
||||
{
|
||||
@@ -1174,7 +1275,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
if (type != BlockType.Header)
|
||||
{
|
||||
if (type != BlockType.EncodedHeader)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, pass);
|
||||
|
||||
@@ -1186,12 +1289,16 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
if (dataVector.Count != 1)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
streamSwitch.Set(this, dataVector[0]);
|
||||
|
||||
if (ReadId() != BlockType.Header)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
ReadHeader(db, pass);
|
||||
@@ -1211,55 +1318,41 @@ namespace SharpCompress.Common.SevenZip
|
||||
FileIndex = fileIndex;
|
||||
FolderIndex = folderIndex;
|
||||
if (fileIndex != -1)
|
||||
{
|
||||
ExtractStatuses.Add(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FolderUnpackStream : Stream
|
||||
{
|
||||
private ArchiveDatabase _db;
|
||||
private int _startIndex;
|
||||
private List<bool> _extractStatuses;
|
||||
private readonly ArchiveDatabase _db;
|
||||
private readonly int _startIndex;
|
||||
private readonly List<bool> _extractStatuses;
|
||||
|
||||
public FolderUnpackStream(ArchiveDatabase db, int p, int startIndex, List<bool> list)
|
||||
{
|
||||
this._db = db;
|
||||
this._startIndex = startIndex;
|
||||
this._extractStatuses = list;
|
||||
_db = db;
|
||||
_startIndex = startIndex;
|
||||
_extractStatuses = list;
|
||||
}
|
||||
|
||||
#region Stream
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
@@ -1298,9 +1391,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
Log.WriteLine(_db.Files[index].Name);
|
||||
#endif
|
||||
if (_db.Files[index].CrcDefined)
|
||||
{
|
||||
_stream = new CrcCheckStream(_db.Files[index].Crc.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream = new MemoryStream();
|
||||
}
|
||||
_rem = _db.Files[index].Size;
|
||||
}
|
||||
|
||||
@@ -1312,7 +1409,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
int write = count;
|
||||
if (write > _rem)
|
||||
{
|
||||
write = (int)_rem;
|
||||
}
|
||||
_stream.Write(buffer, offset, write);
|
||||
count -= write;
|
||||
_rem -= write;
|
||||
@@ -1331,7 +1430,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
if (_currentIndex == _extractStatuses.Count)
|
||||
{
|
||||
// we support partial extracting
|
||||
System.Diagnostics.Debugger.Break();
|
||||
Debugger.Break();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
OpenFile();
|
||||
@@ -1352,7 +1451,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
long folderStartPackPos = _db.GetFolderStreamPos(folderInfo, 0);
|
||||
List<long> packSizes = new List<long>();
|
||||
for (int j = 0; j < folderInfo.PackStreams.Count; j++)
|
||||
{
|
||||
packSizes.Add(_db.PackSizes[packStreamIndex + j]);
|
||||
}
|
||||
|
||||
s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(), folderInfo,
|
||||
pw);
|
||||
@@ -1367,12 +1468,16 @@ namespace SharpCompress.Common.SevenZip
|
||||
int numFilesInFolder = _db.NumUnpackStreamsVector[folderIndex];
|
||||
int firstFileIndex = _db.FolderStartFileIndex[folderIndex];
|
||||
if (firstFileIndex > fileIndex || fileIndex - firstFileIndex >= numFilesInFolder)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
int skipCount = fileIndex - firstFileIndex;
|
||||
long skipSize = 0;
|
||||
for (int i = 0; i < skipCount; i++)
|
||||
{
|
||||
skipSize += _db.Files[firstFileIndex + i].Size;
|
||||
}
|
||||
|
||||
Stream s = GetCachedDecoderStream(_db, folderIndex, pw);
|
||||
s.Position = skipSize;
|
||||
@@ -1384,12 +1489,18 @@ namespace SharpCompress.Common.SevenZip
|
||||
int numItems;
|
||||
bool allFilesMode = (indices == null);
|
||||
if (allFilesMode)
|
||||
{
|
||||
numItems = _db.Files.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
numItems = indices.Length;
|
||||
}
|
||||
|
||||
if (numItems == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CExtractFolderInfo> extractFolderInfoVector = new List<CExtractFolderInfo>();
|
||||
for (int i = 0; i < numItems; i++)
|
||||
@@ -1404,27 +1515,37 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
if (extractFolderInfoVector.Count == 0 || folderIndex != extractFolderInfoVector.Last().FolderIndex)
|
||||
{
|
||||
extractFolderInfoVector.Add(new CExtractFolderInfo(-1, folderIndex));
|
||||
}
|
||||
|
||||
CExtractFolderInfo efi = extractFolderInfoVector.Last();
|
||||
|
||||
int startIndex = _db.FolderStartFileIndex[folderIndex];
|
||||
for (int index = efi.ExtractStatuses.Count; index <= fileIndex - startIndex; index++)
|
||||
{
|
||||
efi.ExtractStatuses.Add(index == fileIndex - startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CExtractFolderInfo efi in extractFolderInfoVector)
|
||||
{
|
||||
int startIndex;
|
||||
if (efi.FileIndex != -1)
|
||||
{
|
||||
startIndex = efi.FileIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex = _db.FolderStartFileIndex[efi.FolderIndex];
|
||||
}
|
||||
|
||||
var outStream = new FolderUnpackStream(_db, 0, startIndex, efi.ExtractStatuses);
|
||||
|
||||
if (efi.FileIndex != -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int folderIndex = efi.FolderIndex;
|
||||
CFolder folderInfo = _db.Folders[folderIndex];
|
||||
@@ -1434,18 +1555,22 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
List<long> packSizes = new List<long>();
|
||||
for (int j = 0; j < folderInfo.PackStreams.Count; j++)
|
||||
{
|
||||
packSizes.Add(_db.PackSizes[packStreamIndex + j]);
|
||||
}
|
||||
|
||||
// TODO: If the decoding fails the last file may be extracted incompletely. Delete it?
|
||||
|
||||
Stream s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(),
|
||||
folderInfo, pw);
|
||||
byte[] buffer = new byte[4 << 10];
|
||||
for (; ; )
|
||||
for (;;)
|
||||
{
|
||||
int processed = s.Read(buffer, 0, buffer.Length);
|
||||
if (processed == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
outStream.Write(buffer, 0, processed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
public bool HasStream { get; internal set; }
|
||||
public bool IsDir { get; internal set; }
|
||||
|
||||
public bool CrcDefined
|
||||
{
|
||||
get { return Crc != null; }
|
||||
}
|
||||
public bool CrcDefined => Crc != null;
|
||||
|
||||
public bool AttribDefined
|
||||
{
|
||||
get { return Attrib != null; }
|
||||
}
|
||||
public bool AttribDefined => Attrib != null;
|
||||
|
||||
public void SetAttrib(uint attrib)
|
||||
{
|
||||
this.Attrib = attrib;
|
||||
Attrib = attrib;
|
||||
}
|
||||
|
||||
public DateTime? CTime { get; internal set; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SharpCompress.Compressor.LZMA;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
@@ -13,19 +13,22 @@ namespace SharpCompress.Common.SevenZip
|
||||
internal List<long> UnpackSizes = new List<long>();
|
||||
internal uint? UnpackCRC;
|
||||
|
||||
internal bool UnpackCRCDefined
|
||||
{
|
||||
get { return UnpackCRC != null; }
|
||||
}
|
||||
internal bool UnpackCRCDefined => UnpackCRC != null;
|
||||
|
||||
public long GetUnpackSize()
|
||||
{
|
||||
if (UnpackSizes.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = UnpackSizes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (FindBindPairForOutStream(i) < 0)
|
||||
{
|
||||
return UnpackSizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
}
|
||||
@@ -34,7 +37,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < Coders.Count; i++)
|
||||
{
|
||||
count += Coders[i].NumOutStreams;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -42,8 +47,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
public int FindBindPairForInStream(int inStreamIndex)
|
||||
{
|
||||
for (int i = 0; i < BindPairs.Count; i++)
|
||||
{
|
||||
if (BindPairs[i].InIndex == inStreamIndex)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -51,8 +60,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
public int FindBindPairForOutStream(int outStreamIndex)
|
||||
{
|
||||
for (int i = 0; i < BindPairs.Count; i++)
|
||||
{
|
||||
if (BindPairs[i].OutIndex == outStreamIndex)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -60,8 +73,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
public int FindPackStreamArrayIndex(int inStreamIndex)
|
||||
{
|
||||
for (int i = 0; i < PackStreams.Count; i++)
|
||||
{
|
||||
if (PackStreams[i] == inStreamIndex)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -69,8 +86,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
public bool IsEncrypted()
|
||||
{
|
||||
for (int i = Coders.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (Coders[i].MethodId == CMethodId.kAES)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -82,25 +103,39 @@ namespace SharpCompress.Common.SevenZip
|
||||
const int kNumBindsMax = 32;
|
||||
|
||||
if (Coders.Count > kNumCodersMax || BindPairs.Count > kNumBindsMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
var v = new BitVector(BindPairs.Count + PackStreams.Count);
|
||||
|
||||
for (int i = 0; i < BindPairs.Count; i++)
|
||||
{
|
||||
if (v.GetAndSet(BindPairs[i].InIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PackStreams.Count; i++)
|
||||
{
|
||||
if (v.GetAndSet(PackStreams[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var v = new BitVector(UnpackSizes.Count);
|
||||
for (int i = 0; i < BindPairs.Count; i++)
|
||||
{
|
||||
if (v.GetAndSet(BindPairs[i].OutIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint[] mask = new uint[kMaskSize];
|
||||
@@ -112,9 +147,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
CCoderInfo coder = Coders[i];
|
||||
for (int j = 0; j < coder.NumInStreams; j++)
|
||||
{
|
||||
inStreamToCoder.Add(i);
|
||||
}
|
||||
for (int j = 0; j < coder.NumOutStreams; j++)
|
||||
{
|
||||
outStreamToCoder.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < BindPairs.Count; i++)
|
||||
@@ -125,13 +164,23 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
for (int i = 0; i < kMaskSize; i++)
|
||||
{
|
||||
for (int j = 0; j < kMaskSize; j++)
|
||||
{
|
||||
if (((1u << j) & mask[i]) != 0)
|
||||
{
|
||||
mask[i] |= mask[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < kMaskSize; i++)
|
||||
{
|
||||
if (((1u << i) & mask[i]) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
public CMethodId(ulong id)
|
||||
{
|
||||
this.Id = id;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is CMethodId && (CMethodId) obj == this;
|
||||
return obj is CMethodId && (CMethodId)obj == this;
|
||||
}
|
||||
|
||||
public bool Equals(CMethodId other)
|
||||
@@ -48,7 +48,9 @@
|
||||
{
|
||||
int bytes = 0;
|
||||
for (ulong value = Id; value != 0; value >>= 8)
|
||||
{
|
||||
bytes++;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Compressor.LZMA;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
@@ -47,7 +46,9 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
int dataIndex = archive.ReadNum();
|
||||
if (dataIndex < 0 || dataIndex >= dataVector.Count)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Log.WriteLine("[switch to stream {0}]", dataIndex);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SharpCompress.Compressor.LZMA;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
@@ -11,31 +11,30 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
public static uint Get32(byte[] buffer, int offset)
|
||||
{
|
||||
return (uint) buffer[offset]
|
||||
+ ((uint) buffer[offset + 1] << 8)
|
||||
+ ((uint) buffer[offset + 2] << 16)
|
||||
+ ((uint) buffer[offset + 3] << 24);
|
||||
return buffer[offset]
|
||||
+ ((uint)buffer[offset + 1] << 8)
|
||||
+ ((uint)buffer[offset + 2] << 16)
|
||||
+ ((uint)buffer[offset + 3] << 24);
|
||||
}
|
||||
|
||||
public static ulong Get64(byte[] buffer, int offset)
|
||||
{
|
||||
return (ulong) buffer[offset]
|
||||
+ ((ulong) buffer[offset + 1] << 8)
|
||||
+ ((ulong) buffer[offset + 2] << 16)
|
||||
+ ((ulong) buffer[offset + 3] << 24)
|
||||
+ ((ulong) buffer[offset + 4] << 32)
|
||||
+ ((ulong) buffer[offset + 5] << 40)
|
||||
+ ((ulong) buffer[offset + 6] << 48)
|
||||
+ ((ulong) buffer[offset + 7] << 56);
|
||||
return buffer[offset]
|
||||
+ ((ulong)buffer[offset + 1] << 8)
|
||||
+ ((ulong)buffer[offset + 2] << 16)
|
||||
+ ((ulong)buffer[offset + 3] << 24)
|
||||
+ ((ulong)buffer[offset + 4] << 32)
|
||||
+ ((ulong)buffer[offset + 5] << 40)
|
||||
+ ((ulong)buffer[offset + 6] << 48)
|
||||
+ ((ulong)buffer[offset + 7] << 56);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Variables
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _offset;
|
||||
private int _ending;
|
||||
private readonly byte[] _buffer;
|
||||
private readonly int _ending;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -44,38 +43,43 @@ namespace SharpCompress.Common.SevenZip
|
||||
public DataReader(byte[] buffer, int offset, int length)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
Offset = offset;
|
||||
_ending = offset + length;
|
||||
}
|
||||
|
||||
public int Offset
|
||||
{
|
||||
get { return _offset; }
|
||||
}
|
||||
public int Offset { get; private set; }
|
||||
|
||||
public Byte ReadByte()
|
||||
{
|
||||
if (_offset >= _ending)
|
||||
if (Offset >= _ending)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
return _buffer[_offset++];
|
||||
return _buffer[Offset++];
|
||||
}
|
||||
|
||||
public void ReadBytes(byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (length > _ending - _offset)
|
||||
if (length > _ending - Offset)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
while (length-- > 0)
|
||||
buffer[offset++] = _buffer[_offset++];
|
||||
{
|
||||
buffer[offset++] = _buffer[Offset++];
|
||||
}
|
||||
}
|
||||
|
||||
public void SkipData(long size)
|
||||
{
|
||||
if (size > _ending - _offset)
|
||||
if (size > _ending - Offset)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
_offset += (int) size;
|
||||
Offset += (int)size;
|
||||
#if DEBUG
|
||||
Log.WriteLine("SkipData {0}", size);
|
||||
#endif
|
||||
@@ -83,15 +87,17 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
public void SkipData()
|
||||
{
|
||||
SkipData(checked((long) ReadNumber()));
|
||||
SkipData(checked((long)ReadNumber()));
|
||||
}
|
||||
|
||||
public ulong ReadNumber()
|
||||
{
|
||||
if (_offset >= _ending)
|
||||
if (Offset >= _ending)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
byte firstByte = _buffer[_offset++];
|
||||
byte firstByte = _buffer[Offset++];
|
||||
byte mask = 0x80;
|
||||
ulong value = 0;
|
||||
|
||||
@@ -100,14 +106,16 @@ namespace SharpCompress.Common.SevenZip
|
||||
if ((firstByte & mask) == 0)
|
||||
{
|
||||
ulong highPart = firstByte & (mask - 1u);
|
||||
value += highPart << (i*8);
|
||||
value += highPart << (i * 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
if (_offset >= _ending)
|
||||
if (Offset >= _ending)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
value |= (ulong) _buffer[_offset++] << (8*i);
|
||||
value |= (ulong)_buffer[Offset++] << (8 * i);
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
@@ -118,48 +126,58 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
ulong value = ReadNumber();
|
||||
if (value > Int32.MaxValue)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
return (int) value;
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
if (_offset + 4 > _ending)
|
||||
if (Offset + 4 > _ending)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
uint res = Get32(_buffer, _offset);
|
||||
_offset += 4;
|
||||
uint res = Get32(_buffer, Offset);
|
||||
Offset += 4;
|
||||
return res;
|
||||
}
|
||||
|
||||
public ulong ReadUInt64()
|
||||
{
|
||||
if (_offset + 8 > _ending)
|
||||
if (Offset + 8 > _ending)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
ulong res = Get64(_buffer, _offset);
|
||||
_offset += 8;
|
||||
ulong res = Get64(_buffer, Offset);
|
||||
Offset += 8;
|
||||
return res;
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
int ending = _offset;
|
||||
int ending = Offset;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (ending + 2 > _ending)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
if (_buffer[ending] == 0 && _buffer[ending + 1] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ending += 2;
|
||||
}
|
||||
|
||||
string str = Encoding.Unicode.GetString(_buffer, _offset, ending - _offset);
|
||||
_offset = ending + 2;
|
||||
string str = Encoding.Unicode.GetString(_buffer, Offset, ending - Offset);
|
||||
Offset = ending + 2;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,79 +7,37 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
internal SevenZipEntry(SevenZipFilePart filePart)
|
||||
{
|
||||
this.FilePart = filePart;
|
||||
FilePart = filePart;
|
||||
}
|
||||
|
||||
internal SevenZipFilePart FilePart { get; private set; }
|
||||
internal SevenZipFilePart FilePart { get; }
|
||||
|
||||
public override CompressionType CompressionType
|
||||
{
|
||||
get { return FilePart.CompressionType; }
|
||||
}
|
||||
public override CompressionType CompressionType => FilePart.CompressionType;
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get { return FilePart.Header.Crc ?? 0; }
|
||||
}
|
||||
public override long Crc => FilePart.Header.Crc ?? 0;
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return FilePart.Header.Name; }
|
||||
}
|
||||
public override string Key => FilePart.Header.Name;
|
||||
|
||||
public override long CompressedSize
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get { return (long) FilePart.Header.Size; }
|
||||
}
|
||||
public override long Size => FilePart.Header.Size;
|
||||
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return FilePart.Header.MTime; }
|
||||
}
|
||||
public override DateTime? LastModifiedTime => FilePart.Header.MTime;
|
||||
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return FilePart.Header.IsDir; }
|
||||
}
|
||||
public override bool IsDirectory => FilePart.Header.IsDir;
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsSplit => false;
|
||||
|
||||
public override int? Attrib
|
||||
{
|
||||
get { return (int) FilePart.Header.Attrib; }
|
||||
}
|
||||
public override int? Attrib => (int)FilePart.Header.Attrib;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { return FilePart.AsEnumerable<FilePart>(); }
|
||||
}
|
||||
internal override IEnumerable<FilePart> Parts => FilePart.AsEnumerable<FilePart>();
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace SharpCompress.Common.SevenZip
|
||||
internal class SevenZipFilePart : FilePart
|
||||
{
|
||||
private CompressionType? type;
|
||||
private Stream stream;
|
||||
private ArchiveDatabase database;
|
||||
private readonly Stream stream;
|
||||
private readonly ArchiveDatabase database;
|
||||
|
||||
internal SevenZipFilePart(Stream stream, ArchiveDatabase database, int index, CFileItem fileEntry)
|
||||
{
|
||||
@@ -24,14 +24,11 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
internal Stream BaseStream { get; private set; }
|
||||
internal CFileItem Header { get; private set; }
|
||||
internal CFolder Folder { get; private set; }
|
||||
internal int Index { get; private set; }
|
||||
internal CFileItem Header { get; }
|
||||
internal CFolder Folder { get; }
|
||||
internal int Index { get; }
|
||||
|
||||
internal override string FilePartName
|
||||
{
|
||||
get { return Header.Name; }
|
||||
}
|
||||
internal override string FilePartName => Header.Name;
|
||||
|
||||
internal override Stream GetRawStream()
|
||||
{
|
||||
@@ -90,17 +87,17 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
case k_LZMA:
|
||||
case k_LZMA2:
|
||||
{
|
||||
return CompressionType.LZMA;
|
||||
}
|
||||
{
|
||||
return CompressionType.LZMA;
|
||||
}
|
||||
case k_PPMD:
|
||||
{
|
||||
return CompressionType.PPMd;
|
||||
}
|
||||
{
|
||||
return CompressionType.PPMd;
|
||||
}
|
||||
case k_BZip2:
|
||||
{
|
||||
return CompressionType.BZip2;
|
||||
}
|
||||
{
|
||||
return CompressionType.BZip2;
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
public class SevenZipVolume : Volume
|
||||
{
|
||||
public SevenZipVolume(Stream stream, Options options)
|
||||
: base(stream, options)
|
||||
public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions)
|
||||
: base(stream, readerFactoryOptions)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace SharpCompress.Common.Tar.Headers
|
||||
{
|
||||
internal enum EntryType : byte
|
||||
{
|
||||
File = 0,
|
||||
OldFile = (byte) '0',
|
||||
HardLink = (byte) '1',
|
||||
SymLink = (byte) '2',
|
||||
CharDevice = (byte) '3',
|
||||
BlockDevice = (byte) '4',
|
||||
Directory = (byte) '5',
|
||||
Fifo = (byte) '6',
|
||||
LongLink = (byte) 'K',
|
||||
LongName = (byte) 'L',
|
||||
SparseFile = (byte) 'S',
|
||||
VolumeHeader = (byte) 'V',
|
||||
GlobalExtendedHeader = (byte) 'g',
|
||||
}
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SharpCompress.Converter;
|
||||
|
||||
namespace SharpCompress.Common.Tar.Headers
|
||||
{
|
||||
internal class TarHeader
|
||||
{
|
||||
internal static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
internal string Name { get; set; }
|
||||
//internal int Mode { get; set; }
|
||||
//internal int UserId { get; set; }
|
||||
//internal string UserName { get; set; }
|
||||
//internal int GroupId { get; set; }
|
||||
//internal string GroupName { get; set; }
|
||||
internal long Size { get; set; }
|
||||
internal DateTime LastModifiedTime { get; set; }
|
||||
internal EntryType EntryType { get; set; }
|
||||
internal Stream PackedStream { get; set; }
|
||||
|
||||
internal const int BlockSize = 512;
|
||||
|
||||
internal void Write(Stream output)
|
||||
{
|
||||
byte[] buffer = new byte[BlockSize];
|
||||
|
||||
WriteOctalBytes(511, buffer, 100, 8); // file mode
|
||||
WriteOctalBytes(0, buffer, 108, 8); // owner ID
|
||||
WriteOctalBytes(0, buffer, 116, 8); // group ID
|
||||
|
||||
//Encoding.UTF8.GetBytes("magic").CopyTo(buffer, 257);
|
||||
if (Name.Length > 100)
|
||||
{
|
||||
// Set mock filename and filetype to indicate the next block is the actual name of the file
|
||||
WriteStringBytes("././@LongLink", buffer, 0, 100);
|
||||
buffer[156] = (byte)EntryType.LongName;
|
||||
WriteOctalBytes(Name.Length + 1, buffer, 124, 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteStringBytes(Name, buffer, 0, 100);
|
||||
WriteOctalBytes(Size, buffer, 124, 12);
|
||||
var time = (long)(LastModifiedTime.ToUniversalTime() - Epoch).TotalSeconds;
|
||||
WriteOctalBytes(time, buffer, 136, 12);
|
||||
buffer[156] = (byte)EntryType;
|
||||
|
||||
if (Size >= 0x1FFFFFFFF)
|
||||
{
|
||||
byte[] bytes = DataConverter.BigEndian.GetBytes(Size);
|
||||
var bytes12 = new byte[12];
|
||||
bytes.CopyTo(bytes12, 12 - bytes.Length);
|
||||
bytes12[0] |= 0x80;
|
||||
bytes12.CopyTo(buffer, 124);
|
||||
}
|
||||
}
|
||||
|
||||
int crc = RecalculateChecksum(buffer);
|
||||
WriteOctalBytes(crc, buffer, 148, 8);
|
||||
|
||||
output.Write(buffer, 0, buffer.Length);
|
||||
|
||||
if (Name.Length > 100)
|
||||
{
|
||||
WriteLongFilenameHeader(output);
|
||||
Name = Name.Substring(0, 100);
|
||||
Write(output);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteLongFilenameHeader(Stream output)
|
||||
{
|
||||
byte[] nameBytes = ArchiveEncoding.Default.GetBytes(Name);
|
||||
output.Write(nameBytes, 0, nameBytes.Length);
|
||||
|
||||
// pad to multiple of BlockSize bytes, and make sure a terminating null is added
|
||||
int numPaddingBytes = BlockSize - (nameBytes.Length % BlockSize);
|
||||
if (numPaddingBytes == 0)
|
||||
numPaddingBytes = BlockSize;
|
||||
output.Write(new byte[numPaddingBytes], 0, numPaddingBytes);
|
||||
}
|
||||
|
||||
internal bool Read(BinaryReader reader)
|
||||
{
|
||||
var buffer = ReadBlock(reader);
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReadEntryType(buffer) == EntryType.LongName)
|
||||
{
|
||||
Name = ReadLongName(reader, buffer);
|
||||
buffer = ReadBlock(reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
Name = ArchiveEncoding.Default.GetString(buffer, 0, 100).TrimNulls();
|
||||
}
|
||||
|
||||
EntryType = ReadEntryType(buffer);
|
||||
Size = ReadSize(buffer);
|
||||
|
||||
//Mode = ReadASCIIInt32Base8(buffer, 100, 7);
|
||||
//UserId = ReadASCIIInt32Base8(buffer, 108, 7);
|
||||
//GroupId = ReadASCIIInt32Base8(buffer, 116, 7);
|
||||
long unixTimeStamp = ReadASCIIInt64Base8(buffer, 136, 11);
|
||||
LastModifiedTime = Epoch.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
|
||||
Magic = ArchiveEncoding.Default.GetString(buffer, 257, 6).TrimNulls();
|
||||
|
||||
if (!string.IsNullOrEmpty(Magic)
|
||||
&& "ustar".Equals(Magic))
|
||||
{
|
||||
string namePrefix = ArchiveEncoding.Default.GetString(buffer, 345, 157);
|
||||
namePrefix = namePrefix.TrimNulls();
|
||||
if (!string.IsNullOrEmpty(namePrefix))
|
||||
{
|
||||
Name = namePrefix + "/" + Name;
|
||||
}
|
||||
}
|
||||
if (EntryType != EntryType.LongName
|
||||
&& Name.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private string ReadLongName(BinaryReader reader, byte[] buffer)
|
||||
{
|
||||
var size = ReadSize(buffer);
|
||||
var nameLength = (int) size;
|
||||
var nameBytes = reader.ReadBytes(nameLength);
|
||||
var remainingBytesToRead = BlockSize - (nameLength%BlockSize);
|
||||
// Read the rest of the block and discard the data
|
||||
if (remainingBytesToRead < BlockSize) reader.ReadBytes(remainingBytesToRead);
|
||||
return ArchiveEncoding.Default.GetString(nameBytes, 0, nameBytes.Length).TrimNulls();
|
||||
}
|
||||
|
||||
private static EntryType ReadEntryType(byte[] buffer)
|
||||
{
|
||||
return (EntryType)buffer[156];
|
||||
}
|
||||
|
||||
private long ReadSize(byte[] buffer)
|
||||
{
|
||||
if ((buffer[124] & 0x80) == 0x80) // if size in binary
|
||||
{
|
||||
return DataConverter.BigEndian.GetInt64(buffer, 0x80);
|
||||
}
|
||||
return ReadASCIIInt64Base8(buffer, 124, 11);
|
||||
}
|
||||
|
||||
private static byte[] ReadBlock(BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(BlockSize);
|
||||
|
||||
if (buffer.Length < BlockSize)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static void WriteStringBytes(string name, byte[] buffer, int offset, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length - 1 && i < name.Length; ++i)
|
||||
{
|
||||
buffer[offset + i] = (byte)name[i];
|
||||
}
|
||||
|
||||
for (; i < length; ++i)
|
||||
{
|
||||
buffer[offset + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteOctalBytes(long value, byte[] buffer, int offset, int length)
|
||||
{
|
||||
string val = Convert.ToString(value, 8);
|
||||
int shift = length - val.Length - 1;
|
||||
for (int i = 0; i < shift; i++)
|
||||
{
|
||||
buffer[offset + i] = (byte)' ';
|
||||
}
|
||||
for (int i = 0; i < val.Length; i++)
|
||||
{
|
||||
buffer[offset + i + shift] = (byte)val[i];
|
||||
}
|
||||
}
|
||||
|
||||
private static int ReadASCIIInt32Base8(byte[] buffer, int offset, int count)
|
||||
{
|
||||
string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Convert.ToInt32(s, 8);
|
||||
}
|
||||
|
||||
private static long ReadASCIIInt64Base8(byte[] buffer, int offset, int count)
|
||||
{
|
||||
string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Convert.ToInt64(s, 8);
|
||||
}
|
||||
|
||||
private static long ReadASCIIInt64(byte[] buffer, int offset, int count)
|
||||
{
|
||||
string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Convert.ToInt64(s);
|
||||
}
|
||||
|
||||
internal static int RecalculateChecksum(byte[] buf)
|
||||
{
|
||||
// Set default value for checksum. That is 8 spaces.
|
||||
Encoding.UTF8.GetBytes(" ").CopyTo(buf, 148);
|
||||
|
||||
// Calculate checksum
|
||||
int headerChecksum = 0;
|
||||
foreach (byte b in buf)
|
||||
{
|
||||
headerChecksum += b;
|
||||
}
|
||||
return headerChecksum;
|
||||
}
|
||||
|
||||
internal static int RecalculateAltChecksum(byte[] buf)
|
||||
{
|
||||
Encoding.UTF8.GetBytes(" ").CopyTo(buf, 148);
|
||||
int headerChecksum = 0;
|
||||
foreach (byte b in buf)
|
||||
{
|
||||
if ((b & 0x80) == 0x80)
|
||||
{
|
||||
headerChecksum -= b ^ 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
headerChecksum += b;
|
||||
}
|
||||
}
|
||||
return headerChecksum;
|
||||
}
|
||||
|
||||
public long? DataStartPosition { get; set; }
|
||||
|
||||
public string Magic { get; set; }
|
||||
}
|
||||
}
|
||||
541
src/SharpCompress/Common/Tar/TarBuffer.cs
Normal file
541
src/SharpCompress/Common/Tar/TarBuffer.cs
Normal file
@@ -0,0 +1,541 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
{
|
||||
/// <summary>
|
||||
/// The TarBuffer class implements the tar archive concept
|
||||
/// of a buffered input stream. This concept goes back to the
|
||||
/// days of blocked tape drives and special io devices. In the
|
||||
/// C# universe, the only real function that this class
|
||||
/// performs is to ensure that files have the correct "record"
|
||||
/// size, or other tars will complain.
|
||||
/// <p>
|
||||
/// You should never have a need to access this class directly.
|
||||
/// TarBuffers are created by Tar IO Streams.
|
||||
/// </p>
|
||||
/// </summary>
|
||||
public class TarBuffer
|
||||
{
|
||||
|
||||
/* A quote from GNU tar man file on blocking and records
|
||||
A `tar' archive file contains a series of blocks. Each block
|
||||
contains `BLOCKSIZE' bytes. Although this format may be thought of as
|
||||
being on magnetic tape, other media are often used.
|
||||
|
||||
Each file archived is represented by a header block which describes
|
||||
the file, followed by zero or more blocks which give the contents of
|
||||
the file. At the end of the archive file there may be a block filled
|
||||
with binary zeros as an end-of-file marker. A reasonable system should
|
||||
write a block of zeros at the end, but must not assume that such a
|
||||
block exists when reading an archive.
|
||||
|
||||
The blocks may be "blocked" for physical I/O operations. Each
|
||||
record of N blocks is written with a single 'write ()'
|
||||
operation. On magnetic tapes, the result of such a write is a single
|
||||
record. When writing an archive, the last record of blocks should be
|
||||
written at the full size, with blocks after the zero block containing
|
||||
all zeros. When reading an archive, a reasonable system should
|
||||
properly handle an archive whose last record is shorter than the rest,
|
||||
or which contains garbage records after a zero block.
|
||||
*/
|
||||
|
||||
#region Constants
|
||||
/// <summary>
|
||||
/// The size of a block in a tar archive in bytes.
|
||||
/// </summary>
|
||||
/// <remarks>This is 512 bytes.</remarks>
|
||||
public const int BlockSize = 512;
|
||||
|
||||
/// <summary>
|
||||
/// The number of blocks in a default record.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is 20 blocks per record.
|
||||
/// </remarks>
|
||||
public const int DefaultBlockFactor = 20;
|
||||
|
||||
/// <summary>
|
||||
/// The size in bytes of a default record.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default size is 10KB.
|
||||
/// </remarks>
|
||||
public const int DefaultRecordSize = BlockSize * DefaultBlockFactor;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Get the record size for this buffer
|
||||
/// </summary>
|
||||
/// <value>The record size in bytes.
|
||||
/// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></value>
|
||||
public int RecordSize => recordSize;
|
||||
|
||||
/// <summary>
|
||||
/// Get the TAR Buffer's record size.
|
||||
/// </summary>
|
||||
/// <returns>The record size in bytes.
|
||||
/// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></returns>
|
||||
[Obsolete("Use RecordSize property instead")]
|
||||
public int GetRecordSize()
|
||||
{
|
||||
return recordSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Blocking factor for the buffer
|
||||
/// </summary>
|
||||
/// <value>This is the number of blocks in each record.</value>
|
||||
public int BlockFactor => blockFactor;
|
||||
|
||||
/// <summary>
|
||||
/// Get the TAR Buffer's block factor
|
||||
/// </summary>
|
||||
/// <returns>The block factor; the number of blocks per record.</returns>
|
||||
[Obsolete("Use BlockFactor property instead")]
|
||||
public int GetBlockFactor()
|
||||
{
|
||||
return blockFactor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a default TarBuffer
|
||||
/// </summary>
|
||||
protected TarBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create TarBuffer for reading with default BlockFactor
|
||||
/// </summary>
|
||||
/// <param name="inputStream">Stream to buffer</param>
|
||||
/// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
|
||||
public static TarBuffer CreateInputTarBuffer(Stream inputStream)
|
||||
{
|
||||
if (inputStream == null) {
|
||||
throw new ArgumentNullException(nameof(inputStream));
|
||||
}
|
||||
|
||||
return CreateInputTarBuffer(inputStream, DefaultBlockFactor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct TarBuffer for reading inputStream setting BlockFactor
|
||||
/// </summary>
|
||||
/// <param name="inputStream">Stream to buffer</param>
|
||||
/// <param name="blockFactor">Blocking factor to apply</param>
|
||||
/// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
|
||||
public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor)
|
||||
{
|
||||
if (inputStream == null) {
|
||||
throw new ArgumentNullException(nameof(inputStream));
|
||||
}
|
||||
|
||||
if (blockFactor <= 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative");
|
||||
}
|
||||
|
||||
var tarBuffer = new TarBuffer
|
||||
{
|
||||
inputStream = inputStream,
|
||||
outputStream = null
|
||||
};
|
||||
tarBuffer.Initialize(blockFactor);
|
||||
|
||||
return tarBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct TarBuffer for writing with default BlockFactor
|
||||
/// </summary>
|
||||
/// <param name="outputStream">output stream for buffer</param>
|
||||
/// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
|
||||
public static TarBuffer CreateOutputTarBuffer(Stream outputStream)
|
||||
{
|
||||
if (outputStream == null) {
|
||||
throw new ArgumentNullException(nameof(outputStream));
|
||||
}
|
||||
|
||||
return CreateOutputTarBuffer(outputStream, DefaultBlockFactor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct TarBuffer for writing Tar output to streams.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output stream to write to.</param>
|
||||
/// <param name="blockFactor">Blocking factor to apply</param>
|
||||
/// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
|
||||
public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor)
|
||||
{
|
||||
if (outputStream == null) {
|
||||
throw new ArgumentNullException(nameof(outputStream));
|
||||
}
|
||||
|
||||
if (blockFactor <= 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative");
|
||||
}
|
||||
|
||||
var tarBuffer = new TarBuffer();
|
||||
tarBuffer.inputStream = null;
|
||||
tarBuffer.outputStream = outputStream;
|
||||
tarBuffer.Initialize(blockFactor);
|
||||
|
||||
return tarBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialization common to all constructors.
|
||||
/// </summary>
|
||||
void Initialize(int archiveBlockFactor)
|
||||
{
|
||||
blockFactor = archiveBlockFactor;
|
||||
recordSize = archiveBlockFactor * BlockSize;
|
||||
recordBuffer = new byte[RecordSize];
|
||||
|
||||
if (inputStream != null) {
|
||||
currentRecordIndex = -1;
|
||||
currentBlockIndex = BlockFactor;
|
||||
} else {
|
||||
currentRecordIndex = 0;
|
||||
currentBlockIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if an archive block indicates End of Archive. End of
|
||||
/// archive is indicated by a block that consists entirely of null bytes.
|
||||
/// All remaining blocks for the record should also be null's
|
||||
/// However some older tars only do a couple of null blocks (Old GNU tar for one)
|
||||
/// and also partial records
|
||||
/// </summary>
|
||||
/// <param name = "block">The data block to check.</param>
|
||||
/// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
|
||||
[Obsolete("Use IsEndOfArchiveBlock instead")]
|
||||
public bool IsEOFBlock(byte[] block)
|
||||
{
|
||||
if (block == null) {
|
||||
throw new ArgumentNullException(nameof(block));
|
||||
}
|
||||
|
||||
if (block.Length != BlockSize) {
|
||||
throw new ArgumentException("block length is invalid");
|
||||
}
|
||||
|
||||
for (int i = 0; i < BlockSize; ++i) {
|
||||
if (block[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determine if an archive block indicates the End of an Archive has been reached.
|
||||
/// End of archive is indicated by a block that consists entirely of null bytes.
|
||||
/// All remaining blocks for the record should also be null's
|
||||
/// However some older tars only do a couple of null blocks (Old GNU tar for one)
|
||||
/// and also partial records
|
||||
/// </summary>
|
||||
/// <param name = "block">The data block to check.</param>
|
||||
/// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
|
||||
public static bool IsEndOfArchiveBlock(byte[] block)
|
||||
{
|
||||
if (block == null) {
|
||||
throw new ArgumentNullException(nameof(block));
|
||||
}
|
||||
|
||||
if (block.Length != BlockSize) {
|
||||
throw new ArgumentException("block length is invalid");
|
||||
}
|
||||
|
||||
for (int i = 0; i < BlockSize; ++i) {
|
||||
if (block[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip over a block on the input stream.
|
||||
/// </summary>
|
||||
public void SkipBlock()
|
||||
{
|
||||
if (inputStream == null) {
|
||||
throw new TarException("no input stream defined");
|
||||
}
|
||||
|
||||
if (currentBlockIndex >= BlockFactor) {
|
||||
if (!ReadRecord()) {
|
||||
throw new TarException("Failed to read a record");
|
||||
}
|
||||
}
|
||||
|
||||
currentBlockIndex++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a block from the input stream.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The block of data read.
|
||||
/// </returns>
|
||||
public byte[] ReadBlock()
|
||||
{
|
||||
if (inputStream == null) {
|
||||
throw new TarException("TarBuffer.ReadBlock - no input stream defined");
|
||||
}
|
||||
|
||||
if (currentBlockIndex >= BlockFactor) {
|
||||
if (!ReadRecord()) {
|
||||
throw new TarException("Failed to read a record");
|
||||
}
|
||||
}
|
||||
|
||||
byte[] result = new byte[BlockSize];
|
||||
|
||||
Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize);
|
||||
currentBlockIndex++;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a record from data stream.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// false if End-Of-File, else true.
|
||||
/// </returns>
|
||||
bool ReadRecord()
|
||||
{
|
||||
if (inputStream == null) {
|
||||
throw new TarException("no input stream stream defined");
|
||||
}
|
||||
|
||||
currentBlockIndex = 0;
|
||||
|
||||
int offset = 0;
|
||||
int bytesNeeded = RecordSize;
|
||||
|
||||
while (bytesNeeded > 0) {
|
||||
long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded);
|
||||
|
||||
//
|
||||
// NOTE
|
||||
// We have found EOF, and the record is not full!
|
||||
//
|
||||
// This is a broken archive. It does not follow the standard
|
||||
// blocking algorithm. However, because we are generous, and
|
||||
// it requires little effort, we will simply ignore the error
|
||||
// and continue as if the entire record were read. This does
|
||||
// not appear to break anything upstream. We used to return
|
||||
// false in this case.
|
||||
//
|
||||
// Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
|
||||
//
|
||||
if (numBytes <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
offset += (int)numBytes;
|
||||
bytesNeeded -= (int)numBytes;
|
||||
}
|
||||
|
||||
currentRecordIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current block number, within the current record, zero based.
|
||||
/// </summary>
|
||||
/// <remarks>Block numbers are zero based values</remarks>
|
||||
/// <seealso cref="RecordSize"/>
|
||||
public int CurrentBlock => currentBlockIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Get/set flag indicating ownership of the underlying stream.
|
||||
/// When the flag is true <see cref="Close"></see> will close the underlying stream also.
|
||||
/// </summary>
|
||||
public bool IsStreamOwner {
|
||||
get => isStreamOwner_;
|
||||
set => isStreamOwner_ = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current block number, within the current record, zero based.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The current zero based block number.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// The absolute block number = (<see cref="GetCurrentRecordNum">record number</see> * <see cref="BlockFactor">block factor</see>) + <see cref="GetCurrentBlockNum">block number</see>.
|
||||
/// </remarks>
|
||||
[Obsolete("Use CurrentBlock property instead")]
|
||||
public int GetCurrentBlockNum()
|
||||
{
|
||||
return currentBlockIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current record number.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The current zero based record number.
|
||||
/// </returns>
|
||||
public int CurrentRecord => currentRecordIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Get the current record number.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The current zero based record number.
|
||||
/// </returns>
|
||||
[Obsolete("Use CurrentRecord property instead")]
|
||||
public int GetCurrentRecordNum()
|
||||
{
|
||||
return currentRecordIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a block of data to the archive.
|
||||
/// </summary>
|
||||
/// <param name="block">
|
||||
/// The data to write to the archive.
|
||||
/// </param>
|
||||
public void WriteBlock(byte[] block)
|
||||
{
|
||||
if (block == null) {
|
||||
throw new ArgumentNullException(nameof(block));
|
||||
}
|
||||
|
||||
if (outputStream == null) {
|
||||
throw new TarException("TarBuffer.WriteBlock - no output stream defined");
|
||||
}
|
||||
|
||||
if (block.Length != BlockSize) {
|
||||
string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block size of '{1}'",
|
||||
block.Length, BlockSize);
|
||||
throw new TarException(errorText);
|
||||
}
|
||||
|
||||
if (currentBlockIndex >= BlockFactor) {
|
||||
WriteRecord();
|
||||
}
|
||||
|
||||
Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
|
||||
currentBlockIndex++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an archive record to the archive, where the record may be
|
||||
/// inside of a larger array buffer. The buffer must be "offset plus
|
||||
/// record size" long.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// The buffer containing the record data to write.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// The offset of the record data within buffer.
|
||||
/// </param>
|
||||
public void WriteBlock(byte[] buffer, int offset)
|
||||
{
|
||||
if (buffer == null) {
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (outputStream == null) {
|
||||
throw new TarException("TarBuffer.WriteBlock - no output stream stream defined");
|
||||
}
|
||||
|
||||
if ((offset < 0) || (offset >= buffer.Length)) {
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
|
||||
if ((offset + BlockSize) > buffer.Length) {
|
||||
string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less than the record size of '{2}'",
|
||||
buffer.Length, offset, recordSize);
|
||||
throw new TarException(errorText);
|
||||
}
|
||||
|
||||
if (currentBlockIndex >= BlockFactor) {
|
||||
WriteRecord();
|
||||
}
|
||||
|
||||
Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
|
||||
|
||||
currentBlockIndex++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a TarBuffer record to the archive.
|
||||
/// </summary>
|
||||
void WriteRecord()
|
||||
{
|
||||
if (outputStream == null) {
|
||||
throw new TarException("TarBuffer.WriteRecord no output stream defined");
|
||||
}
|
||||
|
||||
outputStream.Write(recordBuffer, 0, RecordSize);
|
||||
outputStream.Flush();
|
||||
|
||||
currentBlockIndex = 0;
|
||||
currentRecordIndex++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WriteFinalRecord writes the current record buffer to output any unwritten data is present.
|
||||
/// </summary>
|
||||
/// <remarks>Any trailing bytes are set to zero which is by definition correct behaviour
|
||||
/// for the end of a tar stream.</remarks>
|
||||
void WriteFinalRecord()
|
||||
{
|
||||
if (outputStream == null) {
|
||||
throw new TarException("TarBuffer.WriteFinalRecord no output stream defined");
|
||||
}
|
||||
|
||||
if (currentBlockIndex > 0) {
|
||||
int dataBytes = currentBlockIndex * BlockSize;
|
||||
Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes);
|
||||
WriteRecord();
|
||||
}
|
||||
|
||||
outputStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the TarBuffer. If this is an output buffer, also flush the
|
||||
/// current block before closing.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (outputStream != null) {
|
||||
WriteFinalRecord();
|
||||
|
||||
if (isStreamOwner_) {
|
||||
outputStream.Dispose();
|
||||
}
|
||||
outputStream = null;
|
||||
} else if (inputStream != null) {
|
||||
if (isStreamOwner_) {
|
||||
inputStream.Dispose();
|
||||
}
|
||||
inputStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Instance Fields
|
||||
Stream inputStream;
|
||||
Stream outputStream;
|
||||
|
||||
byte[] recordBuffer;
|
||||
int currentBlockIndex;
|
||||
int currentRecordIndex;
|
||||
|
||||
int recordSize = DefaultRecordSize;
|
||||
int blockFactor = DefaultBlockFactor;
|
||||
bool isStreamOwner_ = true;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
@@ -9,93 +8,56 @@ namespace SharpCompress.Common.Tar
|
||||
public class TarEntry : Entry
|
||||
{
|
||||
private readonly TarFilePart filePart;
|
||||
private readonly CompressionType type;
|
||||
|
||||
internal TarEntry(TarFilePart filePart, CompressionType type)
|
||||
{
|
||||
this.filePart = filePart;
|
||||
this.type = type;
|
||||
CompressionType = type;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType
|
||||
{
|
||||
get { return type; }
|
||||
}
|
||||
internal TarHeader Header => filePart.Header;
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
public override CompressionType CompressionType { get; }
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return filePart.Header.Name; }
|
||||
}
|
||||
public override long Crc => 0;
|
||||
|
||||
public override long CompressedSize
|
||||
{
|
||||
get { return filePart.Header.Size; }
|
||||
}
|
||||
public override string Key => filePart.Header.Name;
|
||||
|
||||
public override long Size
|
||||
{
|
||||
get { return filePart.Header.Size; }
|
||||
}
|
||||
public override long CompressedSize => filePart.Header.Size;
|
||||
|
||||
public override DateTime? LastModifiedTime
|
||||
{
|
||||
get { return filePart.Header.LastModifiedTime; }
|
||||
}
|
||||
public override long Size => filePart.Header.Size;
|
||||
|
||||
public override DateTime? CreatedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? LastModifiedTime => filePart.Header.ModTime;
|
||||
|
||||
public override DateTime? LastAccessedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override bool IsEncrypted
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsDirectory
|
||||
{
|
||||
get { return filePart.Header.EntryType == EntryType.Directory; }
|
||||
}
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsSplit
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsDirectory => filePart.Header.TypeFlag == TarHeader.LF_DIR;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts
|
||||
{
|
||||
get { return filePart.AsEnumerable<FilePart>(); }
|
||||
}
|
||||
public override bool IsSplit => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => filePart.AsEnumerable<FilePart>();
|
||||
|
||||
internal static IEnumerable<TarEntry> GetEntries(StreamingMode mode, Stream stream,
|
||||
CompressionType compressionType)
|
||||
{
|
||||
foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream))
|
||||
using (var tarStream = new TarInputStream(stream))
|
||||
{
|
||||
if (h != null)
|
||||
TarHeader header = null;
|
||||
while ((header = tarStream.GetNextEntry()) != null)
|
||||
{
|
||||
if (mode == StreamingMode.Seekable)
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(h, stream), compressionType);
|
||||
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(h, null), compressionType);
|
||||
yield return new TarEntry(new TarFilePart(header, null), compressionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/SharpCompress/Common/Tar/TarException.cs
Normal file
19
src/SharpCompress/Common/Tar/TarException.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
{
|
||||
/// <summary>
|
||||
/// TarException represents exceptions specific to Tar classes and code.
|
||||
/// </summary>
|
||||
public class TarException : ArchiveException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialise a new instance of <see cref="TarException" /> with its message string.
|
||||
/// </summary>
|
||||
/// <param name="message">A <see cref="string"/> that describes the error.</param>
|
||||
public TarException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
@@ -14,12 +13,9 @@ namespace SharpCompress.Common.Tar
|
||||
Header = header;
|
||||
}
|
||||
|
||||
internal TarHeader Header { get; private set; }
|
||||
internal TarHeader Header { get; }
|
||||
|
||||
internal override string FilePartName
|
||||
{
|
||||
get { return Header.Name; }
|
||||
}
|
||||
internal override string FilePartName => Header.Name;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
|
||||
1060
src/SharpCompress/Common/Tar/TarHeader.cs
Normal file
1060
src/SharpCompress/Common/Tar/TarHeader.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,61 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
{
|
||||
internal static class TarHeaderFactory
|
||||
{
|
||||
internal static IEnumerable<TarHeader> ReadHeader(StreamingMode mode, Stream stream)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TarHeader header = null;
|
||||
try
|
||||
{
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
header = new TarHeader();
|
||||
if (!header.Read(reader))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
header.DataStartPosition = reader.BaseStream.Position;
|
||||
//skip to nearest 512
|
||||
reader.BaseStream.Position += PadTo512(header.Size);
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
header = null;
|
||||
}
|
||||
yield return header;
|
||||
}
|
||||
}
|
||||
|
||||
private static long PadTo512(long size)
|
||||
{
|
||||
int zeros = (int) (size%512);
|
||||
if (zeros == 0)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
return 512 - zeros + size;
|
||||
}
|
||||
}
|
||||
}
|
||||
547
src/SharpCompress/Common/Tar/TarInputStream.cs
Normal file
547
src/SharpCompress/Common/Tar/TarInputStream.cs
Normal file
@@ -0,0 +1,547 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
{
|
||||
/// <summary>
|
||||
/// The TarInputStream reads a UNIX tar archive as an InputStream.
|
||||
/// methods are provided to position at each successive entry in
|
||||
/// the archive, and the read each entry as a normal input stream
|
||||
/// using read().
|
||||
/// </summary>
|
||||
public class TarInputStream : Stream
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Construct a TarInputStream with default block factor
|
||||
/// </summary>
|
||||
/// <param name="inputStream">stream to source data from</param>
|
||||
public TarInputStream(Stream inputStream)
|
||||
: this(inputStream, TarBuffer.DefaultBlockFactor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a TarInputStream with user specified block factor
|
||||
/// </summary>
|
||||
/// <param name="inputStream">stream to source data from</param>
|
||||
/// <param name="blockFactor">block factor to apply to archive</param>
|
||||
public TarInputStream(Stream inputStream, int blockFactor)
|
||||
{
|
||||
this.inputStream = inputStream;
|
||||
tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Get/set flag indicating ownership of the underlying stream.
|
||||
/// When the flag is true <see cref="Close"></see> will close the underlying stream also.
|
||||
/// </summary>
|
||||
public bool IsStreamOwner { get => tarBuffer.IsStreamOwner; set => tarBuffer.IsStreamOwner = value; }
|
||||
|
||||
#region Stream Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current stream supports reading
|
||||
/// </summary>
|
||||
public override bool CanRead => inputStream.CanRead;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current stream supports seeking
|
||||
/// This property always returns false.
|
||||
/// </summary>
|
||||
public override bool CanSeek => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the stream supports writing.
|
||||
/// This property always returns false.
|
||||
/// </summary>
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <summary>
|
||||
/// The length in bytes of the stream
|
||||
/// </summary>
|
||||
public override long Length => inputStream.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the stream.
|
||||
/// Setting the Position is not supported and throws a NotSupportedExceptionNotSupportedException
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">Any attempt to set position</exception>
|
||||
public override long Position { get => inputStream.Position; set => throw new NotSupportedException("TarInputStream Seek not supported"); }
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the baseInputStream
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
inputStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the streams position. This operation is not supported and will throw a NotSupportedException
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset relative to the origin to seek to.</param>
|
||||
/// <param name="origin">The <see cref="SeekOrigin"/> to start seeking from.</param>
|
||||
/// <returns>The new position in the stream.</returns>
|
||||
/// <exception cref="NotSupportedException">Any access</exception>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException("TarInputStream Seek not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the stream
|
||||
/// This operation is not supported and will throw a NotSupportedException
|
||||
/// </summary>
|
||||
/// <param name="value">The new stream length.</param>
|
||||
/// <exception cref="NotSupportedException">Any access</exception>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException("TarInputStream SetLength not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a block of bytes to this stream using data from a buffer.
|
||||
/// This operation is not supported and will throw a NotSupportedException
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer containing bytes to write.</param>
|
||||
/// <param name="offset">The offset in the buffer of the frist byte to write.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
/// <exception cref="NotSupportedException">Any access</exception>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException("TarInputStream Write not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte to the current position in the file stream.
|
||||
/// This operation is not supported and will throw a NotSupportedException
|
||||
/// </summary>
|
||||
/// <param name="value">The byte value to write.</param>
|
||||
/// <exception cref="NotSupportedException">Any access</exception>
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
throw new NotSupportedException("TarInputStream WriteByte not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the current tar archive entry.
|
||||
/// </summary>
|
||||
/// <returns>A byte cast to an int; -1 if the at the end of the stream.</returns>
|
||||
public override int ReadByte()
|
||||
{
|
||||
byte[] oneByteBuffer = new byte[1];
|
||||
int num = Read(oneByteBuffer, 0, 1);
|
||||
if (num <= 0)
|
||||
{
|
||||
// return -1 to indicate that no byte was read.
|
||||
return -1;
|
||||
}
|
||||
return oneByteBuffer[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads bytes from the current tar archive entry.
|
||||
///
|
||||
/// This method is aware of the boundaries of the current
|
||||
/// entry in the archive and will deal with them appropriately
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// The buffer into which to place bytes read.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// The offset at which to place bytes read.
|
||||
/// </param>
|
||||
/// <param name="count">
|
||||
/// The number of bytes to read.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The number of bytes read, or 0 at end of stream/EOF.
|
||||
/// </returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
int totalRead = 0;
|
||||
|
||||
if (entryOffset >= entrySize)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
long numToRead = count;
|
||||
|
||||
if ((numToRead + entryOffset) > entrySize)
|
||||
{
|
||||
numToRead = entrySize - entryOffset;
|
||||
}
|
||||
|
||||
if (readBuffer != null)
|
||||
{
|
||||
int sz = (numToRead > readBuffer.Length) ? readBuffer.Length : (int)numToRead;
|
||||
|
||||
Array.Copy(readBuffer, 0, buffer, offset, sz);
|
||||
|
||||
if (sz >= readBuffer.Length)
|
||||
{
|
||||
readBuffer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int newLen = readBuffer.Length - sz;
|
||||
byte[] newBuf = new byte[newLen];
|
||||
Array.Copy(readBuffer, sz, newBuf, 0, newLen);
|
||||
readBuffer = newBuf;
|
||||
}
|
||||
|
||||
totalRead += sz;
|
||||
numToRead -= sz;
|
||||
offset += sz;
|
||||
}
|
||||
|
||||
while (numToRead > 0)
|
||||
{
|
||||
byte[] rec = tarBuffer.ReadBlock();
|
||||
if (rec == null)
|
||||
{
|
||||
// Unexpected EOF!
|
||||
throw new TarException("unexpected EOF with " + numToRead + " bytes unread");
|
||||
}
|
||||
|
||||
var sz = (int)numToRead;
|
||||
int recLen = rec.Length;
|
||||
|
||||
if (recLen > sz)
|
||||
{
|
||||
Array.Copy(rec, 0, buffer, offset, sz);
|
||||
readBuffer = new byte[recLen - sz];
|
||||
Array.Copy(rec, sz, readBuffer, 0, recLen - sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
sz = recLen;
|
||||
Array.Copy(rec, 0, buffer, offset, recLen);
|
||||
}
|
||||
|
||||
totalRead += sz;
|
||||
numToRead -= sz;
|
||||
offset += sz;
|
||||
}
|
||||
|
||||
entryOffset += totalRead;
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream. Calls the TarBuffer's close() method.
|
||||
/// The underlying stream is closed by the TarBuffer.
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
tarBuffer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the record size being used by this stream's TarBuffer.
|
||||
/// </summary>
|
||||
public int RecordSize => tarBuffer.RecordSize;
|
||||
|
||||
/// <summary>
|
||||
/// Get the record size being used by this stream's TarBuffer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// TarBuffer record size.
|
||||
/// </returns>
|
||||
[Obsolete("Use RecordSize property instead")]
|
||||
public int GetRecordSize()
|
||||
{
|
||||
return tarBuffer.RecordSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the available data that can be read from the current
|
||||
/// entry in the archive. This does not indicate how much data
|
||||
/// is left in the entire archive, only in the current entry.
|
||||
/// This value is determined from the entry's size header field
|
||||
/// and the amount of data already read from the current entry.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The number of available bytes for the current entry.
|
||||
/// </returns>
|
||||
public long Available => entrySize - entryOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Skip bytes in the input buffer. This skips bytes in the
|
||||
/// current entry's data, not the entire archive, and will
|
||||
/// stop at the end of the current entry's data if the number
|
||||
/// to skip extends beyond that point.
|
||||
/// </summary>
|
||||
/// <param name="skipCount">
|
||||
/// The number of bytes to skip.
|
||||
/// </param>
|
||||
public void Skip(long skipCount)
|
||||
{
|
||||
// TODO: REVIEW efficiency of TarInputStream.Skip
|
||||
// This is horribly inefficient, but it ensures that we
|
||||
// properly skip over bytes via the TarBuffer...
|
||||
//
|
||||
byte[] skipBuf = new byte[8 * 1024];
|
||||
|
||||
for (long num = skipCount; num > 0;)
|
||||
{
|
||||
int toRead = num > skipBuf.Length ? skipBuf.Length : (int)num;
|
||||
int numRead = Read(skipBuf, 0, toRead);
|
||||
|
||||
if (numRead == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
num -= numRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a value of true if marking is supported; false otherwise.
|
||||
/// </summary>
|
||||
/// <remarks>Currently marking is not supported, the return value is always false.</remarks>
|
||||
public bool IsMarkSupported => false;
|
||||
|
||||
/// <summary>
|
||||
/// Since we do not support marking just yet, we do nothing.
|
||||
/// </summary>
|
||||
/// <param name ="markLimit">
|
||||
/// The limit to mark.
|
||||
/// </param>
|
||||
public void Mark(int markLimit)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Since we do not support marking just yet, we do nothing.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the next entry in this tar archive. This will skip
|
||||
/// over any remaining data in the current entry, if there
|
||||
/// is one, and place the input stream at the header of the
|
||||
/// next entry, and read the header and instantiate a new
|
||||
/// TarEntry from the header bytes and return that entry.
|
||||
/// If there are no more entries in the archive, null will
|
||||
/// be returned to indicate that the end of the archive has
|
||||
/// been reached.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The next TarEntry in the archive, or null.
|
||||
/// </returns>
|
||||
public TarHeader GetNextEntry()
|
||||
{
|
||||
if (hasHitEOF)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (currentEntry != null)
|
||||
{
|
||||
SkipToNextEntry();
|
||||
}
|
||||
|
||||
byte[] headerBuf = tarBuffer.ReadBlock();
|
||||
|
||||
if (headerBuf == null)
|
||||
{
|
||||
hasHitEOF = true;
|
||||
}
|
||||
else
|
||||
hasHitEOF |= TarBuffer.IsEndOfArchiveBlock(headerBuf);
|
||||
|
||||
if (hasHitEOF)
|
||||
{
|
||||
currentEntry = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var header = new TarHeader();
|
||||
header.ParseBuffer(headerBuf);
|
||||
if (!header.IsChecksumValid)
|
||||
{
|
||||
throw new TarException("Header checksum is invalid");
|
||||
}
|
||||
this.entryOffset = 0;
|
||||
this.entrySize = header.Size;
|
||||
|
||||
StringBuilder longName = null;
|
||||
|
||||
if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME)
|
||||
{
|
||||
byte[] nameBuffer = new byte[TarBuffer.BlockSize];
|
||||
long numToRead = this.entrySize;
|
||||
|
||||
longName = new StringBuilder();
|
||||
|
||||
while (numToRead > 0)
|
||||
{
|
||||
int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead));
|
||||
|
||||
if (numRead == -1)
|
||||
{
|
||||
throw new TarException("Failed to read long name entry");
|
||||
}
|
||||
|
||||
longName.Append(TarHeader.ParseName(nameBuffer, 0, numRead).ToString());
|
||||
numToRead -= numRead;
|
||||
}
|
||||
|
||||
SkipToNextEntry();
|
||||
headerBuf = this.tarBuffer.ReadBlock();
|
||||
}
|
||||
else if (header.TypeFlag == TarHeader.LF_GHDR)
|
||||
{
|
||||
// POSIX global extended header
|
||||
// Ignore things we dont understand completely for now
|
||||
SkipToNextEntry();
|
||||
headerBuf = this.tarBuffer.ReadBlock();
|
||||
}
|
||||
else if (header.TypeFlag == TarHeader.LF_XHDR)
|
||||
{
|
||||
// POSIX extended header
|
||||
// Ignore things we dont understand completely for now
|
||||
SkipToNextEntry();
|
||||
headerBuf = this.tarBuffer.ReadBlock();
|
||||
}
|
||||
else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR)
|
||||
{
|
||||
// TODO: could show volume name when verbose
|
||||
SkipToNextEntry();
|
||||
headerBuf = this.tarBuffer.ReadBlock();
|
||||
}
|
||||
else if (header.TypeFlag != TarHeader.LF_NORMAL &&
|
||||
header.TypeFlag != TarHeader.LF_OLDNORM &&
|
||||
header.TypeFlag != TarHeader.LF_LINK &&
|
||||
header.TypeFlag != TarHeader.LF_SYMLINK &&
|
||||
header.TypeFlag != TarHeader.LF_DIR)
|
||||
{
|
||||
// Ignore things we dont understand completely for now
|
||||
SkipToNextEntry();
|
||||
headerBuf = tarBuffer.ReadBlock();
|
||||
}
|
||||
currentEntry = new TarHeader();
|
||||
|
||||
if (longName != null)
|
||||
{
|
||||
currentEntry.Name = longName.ToString();
|
||||
}
|
||||
|
||||
// Magic was checked here for 'ustar' but there are multiple valid possibilities
|
||||
// so this is not done anymore.
|
||||
|
||||
entryOffset = 0;
|
||||
|
||||
// TODO: Review How do we resolve this discrepancy?!
|
||||
entrySize = this.currentEntry.Size;
|
||||
}
|
||||
catch (TarException ex)
|
||||
{
|
||||
entrySize = 0;
|
||||
entryOffset = 0;
|
||||
currentEntry = null;
|
||||
string errorText = $"Bad header in record {tarBuffer.CurrentRecord} block {tarBuffer.CurrentBlock} {ex.Message}";
|
||||
throw new TarException(errorText);
|
||||
}
|
||||
}
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of the current tar archive entry directly into
|
||||
/// an output stream.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">
|
||||
/// The OutputStream into which to write the entry's data.
|
||||
/// </param>
|
||||
public void CopyEntryContents(Stream outputStream)
|
||||
{
|
||||
byte[] tempBuffer = new byte[32 * 1024];
|
||||
|
||||
while (true)
|
||||
{
|
||||
int numRead = Read(tempBuffer, 0, tempBuffer.Length);
|
||||
if (numRead <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
outputStream.Write(tempBuffer, 0, numRead);
|
||||
}
|
||||
}
|
||||
|
||||
private void SkipToNextEntry()
|
||||
{
|
||||
long numToSkip = entrySize - entryOffset;
|
||||
|
||||
if (numToSkip > 0)
|
||||
{
|
||||
Skip(numToSkip);
|
||||
}
|
||||
|
||||
readBuffer = null;
|
||||
}
|
||||
|
||||
#region Instance Fields
|
||||
|
||||
/// <summary>
|
||||
/// Flag set when last block has been read
|
||||
/// </summary>
|
||||
protected bool hasHitEOF;
|
||||
|
||||
/// <summary>
|
||||
/// Size of this entry as recorded in header
|
||||
/// </summary>
|
||||
protected long entrySize;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes read for this entry so far
|
||||
/// </summary>
|
||||
protected long entryOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer used with calls to <code>Read()</code>
|
||||
/// </summary>
|
||||
protected byte[] readBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Working buffer
|
||||
/// </summary>
|
||||
protected TarBuffer tarBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Current entry being read
|
||||
/// </summary>
|
||||
private TarHeader currentEntry;
|
||||
|
||||
/// <summary>
|
||||
/// Stream used as the source of input data.
|
||||
/// </summary>
|
||||
private readonly Stream inputStream;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
417
src/SharpCompress/Common/Tar/TarOutputStream.cs
Normal file
417
src/SharpCompress/Common/Tar/TarOutputStream.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
{
|
||||
/// <summary>
|
||||
/// The TarOutputStream writes a UNIX tar archive as an OutputStream.
|
||||
/// Methods are provided to put entries, and then write their contents
|
||||
/// by writing to this stream using write().
|
||||
/// </summary>
|
||||
/// public
|
||||
public class TarOutputStream : Stream
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Construct TarOutputStream using default block factor
|
||||
/// </summary>
|
||||
/// <param name="outputStream">stream to write to</param>
|
||||
public TarOutputStream(Stream outputStream)
|
||||
: this(outputStream, TarBuffer.DefaultBlockFactor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct TarOutputStream with user specified block factor
|
||||
/// </summary>
|
||||
/// <param name="outputStream">stream to write to</param>
|
||||
/// <param name="blockFactor">blocking factor</param>
|
||||
public TarOutputStream(Stream outputStream, int blockFactor)
|
||||
{
|
||||
if (outputStream == null) {
|
||||
throw new ArgumentNullException(nameof(outputStream));
|
||||
}
|
||||
|
||||
this.outputStream = outputStream;
|
||||
buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
|
||||
|
||||
assemblyBuffer = new byte[TarBuffer.BlockSize];
|
||||
blockBuffer = new byte[TarBuffer.BlockSize];
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Get/set flag indicating ownership of the underlying stream.
|
||||
/// When the flag is true <see cref="Close"></see> will close the underlying stream also.
|
||||
/// </summary>
|
||||
public bool IsStreamOwner {
|
||||
get => buffer.IsStreamOwner;
|
||||
set => buffer.IsStreamOwner = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if the stream supports reading; otherwise, false.
|
||||
/// </summary>
|
||||
public override bool CanRead => outputStream.CanRead;
|
||||
|
||||
/// <summary>
|
||||
/// true if the stream supports seeking; otherwise, false.
|
||||
/// </summary>
|
||||
public override bool CanSeek => outputStream.CanSeek;
|
||||
|
||||
/// <summary>
|
||||
/// true if stream supports writing; otherwise, false.
|
||||
/// </summary>
|
||||
public override bool CanWrite => outputStream.CanWrite;
|
||||
|
||||
/// <summary>
|
||||
/// length of stream in bytes
|
||||
/// </summary>
|
||||
public override long Length => outputStream.Length;
|
||||
|
||||
/// <summary>
|
||||
/// gets or sets the position within the current stream.
|
||||
/// </summary>
|
||||
public override long Position {
|
||||
get => outputStream.Position;
|
||||
set => outputStream.Position = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// set the position within the current stream
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset relative to the <paramref name="origin"/> to seek to</param>
|
||||
/// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
|
||||
/// <returns>The new position in the stream.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return outputStream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the length of the current stream
|
||||
/// </summary>
|
||||
/// <param name="value">The new stream length.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
outputStream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte from the stream and advance the position within the stream
|
||||
/// by one byte or returns -1 if at the end of the stream.
|
||||
/// </summary>
|
||||
/// <returns>The byte value or -1 if at end of stream</returns>
|
||||
public override int ReadByte()
|
||||
{
|
||||
return outputStream.ReadByte();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// read bytes from the current stream and advance the position within the
|
||||
/// stream by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to store read bytes in.</param>
|
||||
/// <param name="offset">The index into the buffer to being storing bytes at.</param>
|
||||
/// <param name="count">The desired number of bytes to read.</param>
|
||||
/// <returns>The total number of bytes read, or zero if at the end of the stream.
|
||||
/// The number of bytes may be less than the <paramref name="count">count</paramref>
|
||||
/// requested if data is not avialable.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return outputStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All buffered data is written to destination
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
outputStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the TAR archive without closing the underlying OutputStream.
|
||||
/// The result is that the EOF block of nulls is written.
|
||||
/// </summary>
|
||||
public void Finish()
|
||||
{
|
||||
if (IsEntryOpen) {
|
||||
CloseEntry();
|
||||
}
|
||||
WriteEofBlock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the TAR archive and closes the underlying OutputStream.
|
||||
/// </summary>
|
||||
/// <remarks>This means that Finish() is called followed by calling the
|
||||
/// TarBuffer's Close().</remarks>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!isClosed) {
|
||||
isClosed = true;
|
||||
Finish();
|
||||
buffer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the record size being used by this stream's TarBuffer.
|
||||
/// </summary>
|
||||
public int RecordSize => buffer.RecordSize;
|
||||
|
||||
/// <summary>
|
||||
/// Get the record size being used by this stream's TarBuffer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The TarBuffer record size.
|
||||
/// </returns>
|
||||
[Obsolete("Use RecordSize property instead")]
|
||||
public int GetRecordSize()
|
||||
{
|
||||
return buffer.RecordSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a value indicating wether an entry is open, requiring more data to be written.
|
||||
/// </summary>
|
||||
bool IsEntryOpen => (currBytes < currSize);
|
||||
|
||||
/// <summary>
|
||||
/// Put an entry on the output stream. This writes the entry's
|
||||
/// header and positions the output stream for writing
|
||||
/// the contents of the entry. Once this method is called, the
|
||||
/// stream is ready for calls to write() to write the entry's
|
||||
/// contents. Once the contents are written, closeEntry()
|
||||
/// <B>MUST</B> be called to ensure that all buffered data
|
||||
/// is completely written to the output stream.
|
||||
/// </summary>
|
||||
/// <param name="entry">
|
||||
/// The TarEntry to be written to the archive.
|
||||
/// </param>
|
||||
public void PutNextEntry(TarEntry entry)
|
||||
{
|
||||
if (entry == null) {
|
||||
throw new ArgumentNullException(nameof(entry));
|
||||
}
|
||||
|
||||
if (entry.TarHeader.Name.Length > TarHeader.NAMELEN) {
|
||||
var longHeader = new TarHeader();
|
||||
longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME;
|
||||
longHeader.Name = longHeader.Name + "././@LongLink";
|
||||
longHeader.Mode = 420;//644 by default
|
||||
longHeader.UserId = entry.UserId;
|
||||
longHeader.GroupId = entry.GroupId;
|
||||
longHeader.GroupName = entry.GroupName;
|
||||
longHeader.UserName = entry.UserName;
|
||||
longHeader.LinkName = "";
|
||||
longHeader.Size = entry.TarHeader.Name.Length + 1; // Plus one to avoid dropping last char
|
||||
|
||||
longHeader.WriteHeader(blockBuffer);
|
||||
buffer.WriteBlock(blockBuffer); // Add special long filename header block
|
||||
|
||||
int nameCharIndex = 0;
|
||||
|
||||
while (nameCharIndex < entry.TarHeader.Name.Length + 1 /* we've allocated one for the null char, now we must make sure it gets written out */) {
|
||||
Array.Clear(blockBuffer, 0, blockBuffer.Length);
|
||||
TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize); // This func handles OK the extra char out of string length
|
||||
nameCharIndex += TarBuffer.BlockSize;
|
||||
buffer.WriteBlock(blockBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
entry.WriteEntryHeader(blockBuffer);
|
||||
buffer.WriteBlock(blockBuffer);
|
||||
|
||||
currBytes = 0;
|
||||
|
||||
currSize = entry.IsDirectory ? 0 : entry.Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close an entry. This method MUST be called for all file
|
||||
/// entries that contain data. The reason is that we must
|
||||
/// buffer data written to the stream in order to satisfy
|
||||
/// the buffer's block based writes. Thus, there may be
|
||||
/// data fragments still being assembled that must be written
|
||||
/// to the output stream before this entry is closed and the
|
||||
/// next entry written.
|
||||
/// </summary>
|
||||
public void CloseEntry()
|
||||
{
|
||||
if (assemblyBufferLength > 0) {
|
||||
Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength);
|
||||
|
||||
buffer.WriteBlock(assemblyBuffer);
|
||||
|
||||
currBytes += assemblyBufferLength;
|
||||
assemblyBufferLength = 0;
|
||||
}
|
||||
|
||||
if (currBytes < currSize) {
|
||||
string errorText = string.Format(
|
||||
"Entry closed at '{0}' before the '{1}' bytes specified in the header were written",
|
||||
currBytes, currSize);
|
||||
throw new TarException(errorText);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte to the current tar archive entry.
|
||||
/// This method simply calls Write(byte[], int, int).
|
||||
/// </summary>
|
||||
/// <param name="value">
|
||||
/// The byte to be written.
|
||||
/// </param>
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
Write(new byte[] { value }, 0, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes bytes to the current tar archive entry. This method
|
||||
/// is aware of the current entry and will throw an exception if
|
||||
/// you attempt to write bytes past the length specified for the
|
||||
/// current entry. The method is also (painfully) aware of the
|
||||
/// record buffering required by TarBuffer, and manages buffers
|
||||
/// that are not a multiple of recordsize in length, including
|
||||
/// assembling records from small buffers.
|
||||
/// </summary>
|
||||
/// <param name = "buffer">
|
||||
/// The buffer to write to the archive.
|
||||
/// </param>
|
||||
/// <param name = "offset">
|
||||
/// The offset in the buffer from which to get bytes.
|
||||
/// </param>
|
||||
/// <param name = "count">
|
||||
/// The number of bytes to write.
|
||||
/// </param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null) {
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative");
|
||||
}
|
||||
|
||||
if (buffer.Length - offset < count) {
|
||||
throw new ArgumentException("offset and count combination is invalid");
|
||||
}
|
||||
|
||||
if (count < 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative");
|
||||
}
|
||||
|
||||
if ((currBytes + count) > currSize) {
|
||||
string errorText = string.Format("request to write '{0}' bytes exceeds size in header of '{1}' bytes",
|
||||
count, this.currSize);
|
||||
throw new ArgumentOutOfRangeException(nameof(count), errorText);
|
||||
}
|
||||
|
||||
//
|
||||
// We have to deal with assembly!!!
|
||||
// The programmer can be writing little 32 byte chunks for all
|
||||
// we know, and we must assemble complete blocks for writing.
|
||||
// TODO REVIEW Maybe this should be in TarBuffer? Could that help to
|
||||
// eliminate some of the buffer copying.
|
||||
//
|
||||
if (assemblyBufferLength > 0) {
|
||||
if ((assemblyBufferLength + count) >= blockBuffer.Length) {
|
||||
int aLen = blockBuffer.Length - assemblyBufferLength;
|
||||
|
||||
Array.Copy(assemblyBuffer, 0, blockBuffer, 0, assemblyBufferLength);
|
||||
Array.Copy(buffer, offset, blockBuffer, assemblyBufferLength, aLen);
|
||||
|
||||
this.buffer.WriteBlock(blockBuffer);
|
||||
|
||||
currBytes += blockBuffer.Length;
|
||||
|
||||
offset += aLen;
|
||||
count -= aLen;
|
||||
|
||||
assemblyBufferLength = 0;
|
||||
} else {
|
||||
Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
|
||||
offset += count;
|
||||
assemblyBufferLength += count;
|
||||
count -= count;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// When we get here we have EITHER:
|
||||
// o An empty "assembly" buffer.
|
||||
// o No bytes to write (count == 0)
|
||||
//
|
||||
while (count > 0) {
|
||||
if (count < blockBuffer.Length) {
|
||||
Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
|
||||
assemblyBufferLength += count;
|
||||
break;
|
||||
}
|
||||
|
||||
this.buffer.WriteBlock(buffer, offset);
|
||||
|
||||
int bufferLength = blockBuffer.Length;
|
||||
currBytes += bufferLength;
|
||||
count -= bufferLength;
|
||||
offset += bufferLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an EOF (end of archive) block to the tar archive.
|
||||
/// An EOF block consists of all zeros.
|
||||
/// </summary>
|
||||
void WriteEofBlock()
|
||||
{
|
||||
Array.Clear(blockBuffer, 0, blockBuffer.Length);
|
||||
buffer.WriteBlock(blockBuffer);
|
||||
}
|
||||
|
||||
#region Instance Fields
|
||||
/// <summary>
|
||||
/// bytes written for this entry so far
|
||||
/// </summary>
|
||||
long currBytes;
|
||||
|
||||
/// <summary>
|
||||
/// current 'Assembly' buffer length
|
||||
/// </summary>
|
||||
int assemblyBufferLength;
|
||||
|
||||
/// <summary>
|
||||
/// Flag indicating wether this instance has been closed or not.
|
||||
/// </summary>
|
||||
bool isClosed;
|
||||
|
||||
/// <summary>
|
||||
/// Size for the current entry
|
||||
/// </summary>
|
||||
protected long currSize;
|
||||
|
||||
/// <summary>
|
||||
/// single block working buffer
|
||||
/// </summary>
|
||||
protected byte[] blockBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// 'Assembly' buffer used to assemble data before writing
|
||||
/// </summary>
|
||||
protected byte[] assemblyBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// TarBuffer used to provide correct blocking factor
|
||||
/// </summary>
|
||||
protected TarBuffer buffer;
|
||||
|
||||
/// <summary>
|
||||
/// the destination stream for the archive contents
|
||||
/// </summary>
|
||||
protected Stream outputStream;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar
|
||||
{
|
||||
internal class TarReadOnlySubStream : Stream
|
||||
{
|
||||
private bool isDisposed;
|
||||
private long amountRead;
|
||||
|
||||
public TarReadOnlySubStream(Stream stream, long bytesToRead)
|
||||
{
|
||||
Stream = stream;
|
||||
BytesLeftToRead = bytesToRead;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
isDisposed = true;
|
||||
if (disposing)
|
||||
{
|
||||
long skipBytes = amountRead % 512;
|
||||
if (skipBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
skipBytes = 512 - skipBytes;
|
||||
if (skipBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var buffer = new byte[skipBytes];
|
||||
Stream.ReadFully(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private long BytesLeftToRead { get; set; }
|
||||
|
||||
public Stream Stream { get; private set; }
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new System.NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new System.NotSupportedException(); }
|
||||
set { throw new System.NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (BytesLeftToRead < count)
|
||||
{
|
||||
count = (int)BytesLeftToRead;
|
||||
}
|
||||
int read = Stream.Read(buffer, offset, count);
|
||||
if (read > 0)
|
||||
{
|
||||
BytesLeftToRead -= read;
|
||||
amountRead += read;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user