mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Merge remote-tracking branch 'origin/master' into remove-netstandard20
# Conflicts: # .config/dotnet-tools.json
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "0.28.0",
|
||||
"version": "0.28.1",
|
||||
"commands": [
|
||||
"dotnet-csharpier"
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@ The major feature is support for non-seekable streams so large files can be proc
|
||||
|
||||
GitHub Actions Build -
|
||||
[](https://github.com/adamhathcock/sharpcompress/actions/workflows/dotnetcore.yml)
|
||||
[](https://www.robiniadocs.com/d/sharpcompress/api/SharpCompress.html)
|
||||
[](https://dndocs.com/d/sharpcompress/api/index.html)
|
||||
|
||||
## Need Help?
|
||||
|
||||
|
||||
@@ -12,39 +12,35 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
where TEntry : IArchiveEntry
|
||||
where TVolume : IVolume
|
||||
{
|
||||
private readonly LazyReadOnlyCollection<TVolume> lazyVolumes;
|
||||
private readonly LazyReadOnlyCollection<TEntry> lazyEntries;
|
||||
private readonly LazyReadOnlyCollection<TVolume> _lazyVolumes;
|
||||
private readonly LazyReadOnlyCollection<TEntry> _lazyEntries;
|
||||
private bool _disposed;
|
||||
private readonly SourceStream? _sourceStream;
|
||||
|
||||
public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>>? EntryExtractionBegin;
|
||||
public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>>? EntryExtractionEnd;
|
||||
|
||||
public event EventHandler<CompressedBytesReadEventArgs>? CompressedBytesRead;
|
||||
public event EventHandler<FilePartExtractionBeginEventArgs>? FilePartExtractionBegin;
|
||||
|
||||
protected ReaderOptions ReaderOptions { get; }
|
||||
|
||||
private bool disposed;
|
||||
protected SourceStream SrcStream;
|
||||
|
||||
internal AbstractArchive(ArchiveType type, SourceStream srcStream)
|
||||
internal AbstractArchive(ArchiveType type, SourceStream sourceStream)
|
||||
{
|
||||
Type = type;
|
||||
ReaderOptions = srcStream.ReaderOptions;
|
||||
SrcStream = srcStream;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(SrcStream));
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
|
||||
ReaderOptions = sourceStream.ReaderOptions;
|
||||
_sourceStream = sourceStream;
|
||||
_lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(_sourceStream));
|
||||
_lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
internal AbstractArchive(ArchiveType type)
|
||||
{
|
||||
Type = type;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
|
||||
ReaderOptions = new();
|
||||
_lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
|
||||
_lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
|
||||
public ArchiveType Type { get; }
|
||||
|
||||
void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) =>
|
||||
@@ -65,12 +61,12 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
/// <summary>
|
||||
/// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
|
||||
/// </summary>
|
||||
public virtual ICollection<TEntry> Entries => lazyEntries;
|
||||
public virtual ICollection<TEntry> Entries => _lazyEntries;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
|
||||
/// </summary>
|
||||
public ICollection<TVolume> Volumes => lazyVolumes;
|
||||
public ICollection<TVolume> Volumes => _lazyVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// The total size of the files compressed in the archive.
|
||||
@@ -84,29 +80,29 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
public virtual long TotalUncompressSize =>
|
||||
Entries.Aggregate(0L, (total, cf) => total + cf.Size);
|
||||
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(SourceStream srcStream);
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(SourceStream sourceStream);
|
||||
protected abstract IEnumerable<TEntry> LoadEntries(IEnumerable<TVolume> volumes);
|
||||
|
||||
IEnumerable<IArchiveEntry> IArchive.Entries => Entries.Cast<IArchiveEntry>();
|
||||
|
||||
IEnumerable<IVolume> IArchive.Volumes => lazyVolumes.Cast<IVolume>();
|
||||
IEnumerable<IVolume> IArchive.Volumes => _lazyVolumes.Cast<IVolume>();
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
if (!_disposed)
|
||||
{
|
||||
lazyVolumes.ForEach(v => v.Dispose());
|
||||
lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
|
||||
SrcStream?.Dispose();
|
||||
_lazyVolumes.ForEach(v => v.Dispose());
|
||||
_lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
|
||||
_sourceStream?.Dispose();
|
||||
|
||||
disposed = true;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IArchiveExtractionListener.EnsureEntriesLoaded()
|
||||
{
|
||||
lazyEntries.EnsureFullyLoaded();
|
||||
lazyVolumes.EnsureFullyLoaded();
|
||||
_lazyEntries.EnsureFullyLoaded();
|
||||
_lazyVolumes.EnsureFullyLoaded();
|
||||
}
|
||||
|
||||
void IExtractionListener.FireCompressedBytesRead(
|
||||
|
||||
@@ -41,8 +41,8 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
internal AbstractWritableArchive(ArchiveType type)
|
||||
: base(type) { }
|
||||
|
||||
internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
|
||||
: base(type, srcStream) { }
|
||||
internal AbstractWritableArchive(ArchiveType type, SourceStream sourceStream)
|
||||
: base(type, sourceStream) { }
|
||||
|
||||
public override ICollection<TEntry> Entries
|
||||
{
|
||||
@@ -120,6 +120,10 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
{
|
||||
foreach (var path in Entries.Select(x => x.Key))
|
||||
{
|
||||
if (path is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var p = path.Replace('/', '\\');
|
||||
if (p.Length > 0 && p[0] == '\\')
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
return new GZipArchive(
|
||||
new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
|
||||
new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,16 +99,14 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal GZipArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Tar, srcStream) { }
|
||||
/// <param name="sourceStream"></param>
|
||||
private GZipArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.Tar, sourceStream) { }
|
||||
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream srcStream)
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream sourceStream)
|
||||
{
|
||||
srcStream.LoadAllParts();
|
||||
var idx = 0;
|
||||
return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++));
|
||||
sourceStream.LoadAllParts();
|
||||
return sourceStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, 0));
|
||||
}
|
||||
|
||||
public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));
|
||||
@@ -184,7 +182,11 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace SharpCompress.Archives.GZip;
|
||||
|
||||
public class GZipArchiveEntry : GZipEntry, IArchiveEntry
|
||||
{
|
||||
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part)
|
||||
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart? part)
|
||||
: base(part) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -32,7 +30,7 @@ internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArch
|
||||
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string Key { get; }
|
||||
public override string? Key { get; }
|
||||
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
|
||||
@@ -17,15 +17,11 @@ public static class IArchiveEntryExtensions
|
||||
streamListener.EnsureEntriesLoaded();
|
||||
streamListener.FireEntryExtractionBegin(archiveEntry);
|
||||
streamListener.FireFilePartExtractionBegin(
|
||||
archiveEntry.Key,
|
||||
archiveEntry.Key ?? "Key",
|
||||
archiveEntry.Size,
|
||||
archiveEntry.CompressedSize
|
||||
);
|
||||
var entryStream = archiveEntry.OpenEntryStream();
|
||||
if (entryStream is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (entryStream)
|
||||
{
|
||||
using Stream s = new ListeningStream(streamListener, entryStream);
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
@@ -59,7 +58,7 @@ public static class IArchiveExtensions
|
||||
}
|
||||
|
||||
// Create each directory
|
||||
var path = Path.Combine(destination, entry.Key);
|
||||
var path = Path.Combine(destination, entry.Key.NotNull("Entry Key is null"));
|
||||
if (Path.GetDirectoryName(path) is { } directory && seenDirectories.Add(path))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace SharpCompress.Archives.Rar;
|
||||
/// </summary>
|
||||
internal class FileInfoRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0)
|
||||
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
|
||||
{
|
||||
FileInfo = fileInfo;
|
||||
|
||||
@@ -21,35 +21,33 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal RarArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Rar, srcStream) { }
|
||||
/// <param name="sourceStream"></param>
|
||||
private RarArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.Rar, sourceStream) { }
|
||||
|
||||
protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes) =>
|
||||
RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
|
||||
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream srcStream)
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream sourceStream)
|
||||
{
|
||||
SrcStream.LoadAllParts(); //request all streams
|
||||
var streams = SrcStream.Streams.ToArray();
|
||||
var idx = 0;
|
||||
sourceStream.LoadAllParts(); //request all streams
|
||||
var streams = sourceStream.Streams.ToArray();
|
||||
var i = 0;
|
||||
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
|
||||
{
|
||||
SrcStream.IsVolumes = true;
|
||||
sourceStream.IsVolumes = true;
|
||||
streams[1].Position = 0;
|
||||
SrcStream.Position = 0;
|
||||
sourceStream.Position = 0;
|
||||
|
||||
return srcStream.Streams.Select(a => new StreamRarArchiveVolume(
|
||||
return sourceStream.Streams.Select(a => new StreamRarArchiveVolume(
|
||||
a,
|
||||
ReaderOptions,
|
||||
idx++
|
||||
i++
|
||||
));
|
||||
}
|
||||
else //split mode or single file
|
||||
{
|
||||
return new StreamRarArchiveVolume(SrcStream, ReaderOptions, idx++).AsEnumerable();
|
||||
}
|
||||
|
||||
//split mode or single file
|
||||
return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
@@ -108,7 +106,7 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
public static RarArchive Open(Stream stream, ReaderOptions? options = null)
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions()));
|
||||
return new RarArchive(new SourceStream(stream, _ => null, options ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal class SeekableFilePart : RarFilePart
|
||||
{
|
||||
private readonly Stream stream;
|
||||
private readonly string? password;
|
||||
private readonly Stream _stream;
|
||||
private readonly string? _password;
|
||||
|
||||
internal SeekableFilePart(
|
||||
MarkHeader mh,
|
||||
@@ -18,27 +18,27 @@ internal class SeekableFilePart : RarFilePart
|
||||
)
|
||||
: base(mh, fh, index)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.password = password;
|
||||
_stream = stream;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
stream.Position = FileHeader.DataStartPosition;
|
||||
_stream.Position = FileHeader.DataStartPosition;
|
||||
|
||||
if (FileHeader.R4Salt != null)
|
||||
{
|
||||
var cryptKey = new CryptKey3(password!);
|
||||
return new RarCryptoWrapper(stream, FileHeader.R4Salt, cryptKey);
|
||||
var cryptKey = new CryptKey3(_password!);
|
||||
return new RarCryptoWrapper(_stream, FileHeader.R4Salt, cryptKey);
|
||||
}
|
||||
|
||||
if (FileHeader.Rar5CryptoInfo != null)
|
||||
{
|
||||
var cryptKey = new CryptKey5(password!, FileHeader.Rar5CryptoInfo);
|
||||
return new RarCryptoWrapper(stream, FileHeader.Rar5CryptoInfo.Salt, cryptKey);
|
||||
var cryptKey = new CryptKey5(_password!, FileHeader.Rar5CryptoInfo);
|
||||
return new RarCryptoWrapper(_stream, FileHeader.Rar5CryptoInfo.Salt, cryptKey);
|
||||
}
|
||||
|
||||
return stream;
|
||||
return _stream;
|
||||
}
|
||||
|
||||
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal class StreamRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0)
|
||||
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Seekable, stream, options, index) { }
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts() => GetVolumeFileParts();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -14,14 +12,14 @@ namespace SharpCompress.Archives.SevenZip;
|
||||
|
||||
public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVolume>
|
||||
{
|
||||
private ArchiveDatabase database;
|
||||
private ArchiveDatabase? _database;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null)
|
||||
public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty("filePath");
|
||||
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
|
||||
@@ -32,7 +30,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new SevenZipArchive(
|
||||
@@ -51,7 +49,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions readerOptions = null
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
fileInfos.CheckNotNull(nameof(fileInfos));
|
||||
@@ -72,7 +70,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(
|
||||
IEnumerable<Stream> streams,
|
||||
ReaderOptions readerOptions = null
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
@@ -91,27 +89,25 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SevenZipArchive(
|
||||
new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
|
||||
new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal SevenZipArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.SevenZip, srcStream) { }
|
||||
/// <param name="sourceStream"></param>
|
||||
private SevenZipArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.SevenZip, sourceStream) { }
|
||||
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream srcStream)
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream sourceStream)
|
||||
{
|
||||
SrcStream.LoadAllParts(); //request all streams
|
||||
var idx = 0;
|
||||
return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
|
||||
sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams
|
||||
return new SevenZipVolume(sourceStream, ReaderOptions, 0).AsEnumerable(); //simple single volume or split, multivolume not supported
|
||||
}
|
||||
|
||||
public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath));
|
||||
@@ -135,13 +131,17 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
{
|
||||
var stream = volumes.Single().Stream;
|
||||
LoadFactory(stream);
|
||||
var entries = new SevenZipArchiveEntry[database._files.Count];
|
||||
for (var i = 0; i < database._files.Count; i++)
|
||||
if (_database is null)
|
||||
{
|
||||
var file = database._files[i];
|
||||
return Enumerable.Empty<SevenZipArchiveEntry>();
|
||||
}
|
||||
var entries = new SevenZipArchiveEntry[_database._files.Count];
|
||||
for (var i = 0; i < _database._files.Count; i++)
|
||||
{
|
||||
var file = _database._files[i];
|
||||
entries[i] = new SevenZipArchiveEntry(
|
||||
this,
|
||||
new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding)
|
||||
new SevenZipFilePart(stream, _database, i, file, ReaderOptions.ArchiveEncoding)
|
||||
);
|
||||
}
|
||||
foreach (var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder))
|
||||
@@ -159,12 +159,12 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
|
||||
private void LoadFactory(Stream stream)
|
||||
{
|
||||
if (database is null)
|
||||
if (_database is null)
|
||||
{
|
||||
stream.Position = 0;
|
||||
var reader = new ArchiveReader();
|
||||
reader.Open(stream);
|
||||
database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
|
||||
_database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,14 +180,14 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
}
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> SIGNATURE =>
|
||||
private static ReadOnlySpan<byte> Signature =>
|
||||
new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C };
|
||||
|
||||
private static bool SignatureMatch(Stream stream)
|
||||
{
|
||||
var reader = new BinaryReader(stream);
|
||||
ReadOnlySpan<byte> signatureBytes = reader.ReadBytes(6);
|
||||
return signatureBytes.SequenceEqual(SIGNATURE);
|
||||
return signatureBytes.SequenceEqual(Signature);
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction() =>
|
||||
@@ -196,30 +196,24 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
public override bool IsSolid =>
|
||||
Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1;
|
||||
|
||||
public override long TotalSize
|
||||
{
|
||||
get
|
||||
{
|
||||
var i = Entries.Count;
|
||||
return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize);
|
||||
}
|
||||
}
|
||||
public override long TotalSize =>
|
||||
_database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0;
|
||||
|
||||
private sealed class SevenZipReader : AbstractReader<SevenZipEntry, SevenZipVolume>
|
||||
{
|
||||
private readonly SevenZipArchive archive;
|
||||
private CFolder currentFolder;
|
||||
private Stream currentStream;
|
||||
private CFileItem currentItem;
|
||||
private readonly SevenZipArchive _archive;
|
||||
private CFolder? _currentFolder;
|
||||
private Stream? _currentStream;
|
||||
private CFileItem? _currentItem;
|
||||
|
||||
internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive)
|
||||
: base(readerOptions, ArchiveType.SevenZip) => this.archive = archive;
|
||||
: base(readerOptions, ArchiveType.SevenZip) => this._archive = archive;
|
||||
|
||||
public override SevenZipVolume Volume => archive.Volumes.Single();
|
||||
public override SevenZipVolume Volume => _archive.Volumes.Single();
|
||||
|
||||
protected override IEnumerable<SevenZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
var entries = archive.Entries.ToList();
|
||||
var entries = _archive.Entries.ToList();
|
||||
stream.Position = 0;
|
||||
foreach (var dir in entries.Where(x => x.IsDirectory))
|
||||
{
|
||||
@@ -229,37 +223,42 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder)
|
||||
)
|
||||
{
|
||||
currentFolder = group.Key;
|
||||
_currentFolder = group.Key;
|
||||
if (group.Key is null)
|
||||
{
|
||||
currentStream = Stream.Null;
|
||||
_currentStream = Stream.Null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentStream = archive.database.GetFolderStream(
|
||||
_currentStream = _archive._database?.GetFolderStream(
|
||||
stream,
|
||||
currentFolder,
|
||||
_currentFolder,
|
||||
new PasswordProvider(Options.Password)
|
||||
);
|
||||
}
|
||||
foreach (var entry in group)
|
||||
{
|
||||
currentItem = entry.FilePart.Header;
|
||||
_currentItem = entry.FilePart.Header;
|
||||
yield return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override EntryStream GetEntryStream() =>
|
||||
CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size));
|
||||
CreateEntryStream(
|
||||
new ReadOnlySubStream(
|
||||
_currentStream.NotNull("currentStream is not null"),
|
||||
_currentItem?.Size ?? 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private class PasswordProvider : IPasswordProvider
|
||||
{
|
||||
private readonly string _password;
|
||||
private readonly string? _password;
|
||||
|
||||
public PasswordProvider(string password) => _password = password;
|
||||
public PasswordProvider(string? password) => _password = password;
|
||||
|
||||
public string CryptoGetTextPassword() => _password;
|
||||
public string? CryptoGetTextPassword() => _password;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
var tarHeader = new TarHeader(new ArchiveEncoding());
|
||||
var readSucceeded = tarHeader.Read(new BinaryReader(stream));
|
||||
var isEmptyArchive =
|
||||
tarHeader.Name.Length == 0
|
||||
tarHeader.Name?.Length == 0
|
||||
&& tarHeader.Size == 0
|
||||
&& Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
|
||||
return readSucceeded || isEmptyArchive;
|
||||
@@ -123,22 +123,20 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream srcStream)
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream sourceStream)
|
||||
{
|
||||
SrcStream.LoadAllParts(); //request all streams
|
||||
var idx = 0;
|
||||
return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
|
||||
sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams
|
||||
return new TarVolume(sourceStream, ReaderOptions, 1).AsEnumerable(); //simple single volume or split, multivolume not supported
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal TarArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Tar, srcStream) { }
|
||||
/// <param name="sourceStream"></param>
|
||||
private TarArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.Tar, sourceStream) { }
|
||||
|
||||
internal TarArchive()
|
||||
private TarArchive()
|
||||
: base(ArchiveType.Tar) { }
|
||||
|
||||
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
|
||||
@@ -225,7 +223,12 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size);
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime,
|
||||
entry.Size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace SharpCompress.Archives.Tar;
|
||||
|
||||
public class TarArchiveEntry : TarEntry, IArchiveEntry
|
||||
{
|
||||
internal TarArchiveEntry(TarArchive archive, TarFilePart part, CompressionType compressionType)
|
||||
internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType compressionType)
|
||||
: base(part, compressionType) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -16,10 +16,7 @@ namespace SharpCompress.Archives.Zip;
|
||||
|
||||
public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
{
|
||||
#nullable disable
|
||||
private readonly SeekableZipHeaderFactory headerFactory;
|
||||
|
||||
#nullable enable
|
||||
private readonly SeekableZipHeaderFactory? headerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compression level applied to files added to the archive,
|
||||
@@ -30,13 +27,13 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="sourceStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal ZipArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Zip, srcStream) =>
|
||||
internal ZipArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.Zip, sourceStream) =>
|
||||
headerFactory = new SeekableZipHeaderFactory(
|
||||
srcStream.ReaderOptions.Password,
|
||||
srcStream.ReaderOptions.ArchiveEncoding
|
||||
sourceStream.ReaderOptions.Password,
|
||||
sourceStream.ReaderOptions.ArchiveEncoding
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
@@ -189,21 +186,21 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(SourceStream srcStream)
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(SourceStream stream)
|
||||
{
|
||||
SrcStream.LoadAllParts(); //request all streams
|
||||
SrcStream.Position = 0;
|
||||
stream.LoadAllParts(); //request all streams
|
||||
stream.Position = 0;
|
||||
|
||||
var streams = SrcStream.Streams.ToList();
|
||||
var streams = stream.Streams.ToList();
|
||||
var idx = 0;
|
||||
if (streams.Count > 1) //test part 2 - true = multipart not split
|
||||
if (streams.Count() > 1) //test part 2 - true = multipart not split
|
||||
{
|
||||
streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
|
||||
var isZip = IsZipFile(streams[1], ReaderOptions.Password);
|
||||
streams[1].Position -= 4;
|
||||
if (isZip)
|
||||
{
|
||||
SrcStream.IsVolumes = true;
|
||||
stream.IsVolumes = true;
|
||||
|
||||
var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end
|
||||
streams.RemoveAt(0);
|
||||
@@ -215,7 +212,7 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
}
|
||||
|
||||
//split mode or single file
|
||||
return new ZipVolume(SrcStream, ReaderOptions, idx++).AsEnumerable();
|
||||
return new ZipVolume(stream, ReaderOptions, idx++).AsEnumerable();
|
||||
}
|
||||
|
||||
internal ZipArchive()
|
||||
@@ -224,7 +221,7 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
|
||||
{
|
||||
var vols = volumes.ToArray();
|
||||
foreach (var h in headerFactory.ReadSeekableHeader(vols.Last().Stream))
|
||||
foreach (var h in headerFactory.NotNull().ReadSeekableHeader(vols.Last().Stream))
|
||||
{
|
||||
if (h != null)
|
||||
{
|
||||
@@ -253,14 +250,14 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(headerFactory, deh, s)
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ZipHeaderType.DirectoryEnd:
|
||||
{
|
||||
var bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty<byte>();
|
||||
volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes);
|
||||
vols.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
@@ -281,7 +278,11 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ public class ArchiveEncoding
|
||||
/// <summary>
|
||||
/// Default encoding to use when archive format doesn't specify one.
|
||||
/// </summary>
|
||||
public Encoding Default { get; set; }
|
||||
public Encoding? Default { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ArchiveEncoding used by encryption schemes which don't comply with RFC 2898.
|
||||
/// </summary>
|
||||
public Encoding Password { get; set; }
|
||||
public Encoding? Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this encoding when you want to force it for all encoding operations.
|
||||
@@ -50,6 +50,8 @@ public class ArchiveEncoding
|
||||
|
||||
public Encoding GetEncoding() => Forced ?? Default ?? Encoding.UTF8;
|
||||
|
||||
public Encoding GetPasswordEncoding() => Password ?? Encoding.UTF8;
|
||||
|
||||
public Func<byte[], int, int, string> GetDecoder() =>
|
||||
CustomDecoder ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public abstract class Entry : IEntry
|
||||
/// <summary>
|
||||
/// The string key of the file internal to the Archive.
|
||||
/// </summary>
|
||||
public abstract string Key { get; }
|
||||
public abstract string? Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The target of a symlink entry internal to the Archive. Will be null if not a symlink.
|
||||
@@ -71,11 +71,11 @@ public abstract class Entry : IEntry
|
||||
/// </summary>
|
||||
public abstract bool IsSplitAfter { get; }
|
||||
|
||||
public int VolumeIndexFirst => Parts?.FirstOrDefault()?.Index ?? 0;
|
||||
public int VolumeIndexLast => Parts?.LastOrDefault()?.Index ?? 0;
|
||||
public int VolumeIndexFirst => Parts.FirstOrDefault()?.Index ?? 0;
|
||||
public int VolumeIndexLast => Parts.LastOrDefault()?.Index ?? 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => Key;
|
||||
public override string ToString() => Key ?? "Entry";
|
||||
|
||||
internal abstract IEnumerable<FilePart> Parts { get; }
|
||||
|
||||
|
||||
@@ -36,10 +36,11 @@ internal static class ExtractionMethods
|
||||
|
||||
options ??= new ExtractionOptions() { Overwrite = true };
|
||||
|
||||
var file = Path.GetFileName(entry.Key);
|
||||
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
|
||||
if (options.ExtractFullPath)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(entry.Key)!;
|
||||
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
|
||||
.NotNull("Directory is null");
|
||||
var destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder));
|
||||
|
||||
if (!Directory.Exists(destdir))
|
||||
|
||||
@@ -8,7 +8,7 @@ public abstract class FilePart
|
||||
|
||||
internal ArchiveEncoding ArchiveEncoding { get; }
|
||||
|
||||
internal abstract string FilePartName { get; }
|
||||
internal abstract string? FilePartName { get; }
|
||||
public int Index { get; set; }
|
||||
|
||||
internal abstract Stream GetCompressedStream();
|
||||
|
||||
@@ -6,23 +6,23 @@ namespace SharpCompress.Common.GZip;
|
||||
|
||||
public class GZipEntry : Entry
|
||||
{
|
||||
private readonly GZipFilePart _filePart;
|
||||
private readonly GZipFilePart? _filePart;
|
||||
|
||||
internal GZipEntry(GZipFilePart filePart) => _filePart = filePart;
|
||||
internal GZipEntry(GZipFilePart? filePart) => _filePart = filePart;
|
||||
|
||||
public override CompressionType CompressionType => CompressionType.GZip;
|
||||
|
||||
public override long Crc => _filePart.Crc ?? 0;
|
||||
public override long Crc => _filePart?.Crc ?? 0;
|
||||
|
||||
public override string Key => _filePart.FilePartName;
|
||||
public override string? Key => _filePart?.FilePartName;
|
||||
|
||||
public override string? LinkTarget => null;
|
||||
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size => _filePart.UncompressedSize ?? 0;
|
||||
public override long Size => _filePart?.UncompressedSize ?? 0;
|
||||
|
||||
public override DateTime? LastModifiedTime => _filePart.DateModified;
|
||||
public override DateTime? LastModifiedTime => _filePart?.DateModified;
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
@@ -36,7 +36,7 @@ public class GZipEntry : Entry
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
|
||||
|
||||
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, OptionsBase options)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@ internal sealed class GZipFilePart : FilePart
|
||||
internal uint? Crc { get; private set; }
|
||||
internal uint? UncompressedSize { get; private set; }
|
||||
|
||||
internal override string FilePartName => _name!;
|
||||
internal override string? FilePartName => _name;
|
||||
|
||||
internal override Stream GetCompressedStream() =>
|
||||
new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default);
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace SharpCompress.Common.GZip;
|
||||
|
||||
public class GZipVolume : Volume
|
||||
{
|
||||
public GZipVolume(Stream stream, ReaderOptions options, int index = 0)
|
||||
public GZipVolume(Stream stream, ReaderOptions? options, int index)
|
||||
: base(stream, options, index) { }
|
||||
|
||||
public GZipVolume(FileInfo fileInfo, ReaderOptions options)
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface IEntry
|
||||
long CompressedSize { get; }
|
||||
long Crc { get; }
|
||||
DateTime? CreatedTime { get; }
|
||||
string Key { get; }
|
||||
string? Key { get; }
|
||||
string? LinkTarget { get; }
|
||||
bool IsDirectory { get; }
|
||||
bool IsEncrypted { get; }
|
||||
|
||||
@@ -6,5 +6,5 @@ public interface IVolume : IDisposable
|
||||
{
|
||||
int Index { get; }
|
||||
|
||||
string FileName { get; }
|
||||
string? FileName { get; }
|
||||
}
|
||||
|
||||
@@ -70,11 +70,11 @@ internal sealed class RarCryptoWrapper : Stream
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_rijndael != null)
|
||||
if (disposing)
|
||||
{
|
||||
_rijndael.Dispose();
|
||||
_rijndael = null!;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public abstract class RarEntry : Entry
|
||||
/// <summary>
|
||||
/// The path of the file internal to the Rar Archive.
|
||||
/// </summary>
|
||||
public override string Key => FileHeader.FileName;
|
||||
public override string? Key => FileHeader.FileName;
|
||||
|
||||
public override string? LinkTarget => null;
|
||||
|
||||
|
||||
@@ -15,17 +15,14 @@ namespace SharpCompress.Common.Rar;
|
||||
public abstract class RarVolume : Volume
|
||||
{
|
||||
private readonly RarHeaderFactory _headerFactory;
|
||||
internal int _maxCompressionAlgorithm;
|
||||
private int _maxCompressionAlgorithm;
|
||||
|
||||
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0)
|
||||
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index)
|
||||
: base(stream, options, index) => _headerFactory = new RarHeaderFactory(mode, options);
|
||||
|
||||
#nullable disable
|
||||
internal ArchiveHeader ArchiveHeader { get; private set; }
|
||||
private ArchiveHeader? ArchiveHeader { get; set; }
|
||||
|
||||
#nullable enable
|
||||
|
||||
internal StreamingMode Mode => _headerFactory.StreamingMode;
|
||||
private StreamingMode Mode => _headerFactory.StreamingMode;
|
||||
|
||||
internal abstract IEnumerable<RarFilePart> ReadFileParts();
|
||||
|
||||
@@ -101,7 +98,7 @@ public abstract class RarVolume : Volume
|
||||
get
|
||||
{
|
||||
EnsureArchiveHeaderLoaded();
|
||||
return ArchiveHeader.IsFirstVolume;
|
||||
return ArchiveHeader?.IsFirstVolume ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +110,7 @@ public abstract class RarVolume : Volume
|
||||
get
|
||||
{
|
||||
EnsureArchiveHeaderLoaded();
|
||||
return ArchiveHeader.IsVolume;
|
||||
return ArchiveHeader?.IsVolume ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +123,7 @@ public abstract class RarVolume : Volume
|
||||
get
|
||||
{
|
||||
EnsureArchiveHeaderLoaded();
|
||||
return ArchiveHeader.IsSolid;
|
||||
return ArchiveHeader?.IsSolid ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ internal class ArchiveDatabase
|
||||
_packSizes.Clear();
|
||||
_packCrCs.Clear();
|
||||
_folders.Clear();
|
||||
_numUnpackStreamsVector = null!;
|
||||
_numUnpackStreamsVector = null;
|
||||
_files.Clear();
|
||||
|
||||
_packStreamStartPositions.Clear();
|
||||
|
||||
@@ -13,7 +13,7 @@ public class SevenZipEntry : Entry
|
||||
|
||||
public override long Crc => FilePart.Header.Crc ?? 0;
|
||||
|
||||
public override string Key => FilePart.Header.Name;
|
||||
public override string? Key => FilePart.Header.Name;
|
||||
|
||||
public override string? LinkTarget => null;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ internal class SevenZipFilePart : FilePart
|
||||
{
|
||||
if (!Header.HasStream)
|
||||
{
|
||||
return null!;
|
||||
throw new InvalidOperationException("File does not have a stream.");
|
||||
}
|
||||
var folderStream = _database.GetFolderStream(_stream, Folder!, _database.PasswordProvider);
|
||||
|
||||
@@ -73,34 +73,24 @@ internal class SevenZipFilePart : FilePart
|
||||
private const uint K_PPMD = 0x030401;
|
||||
private const uint K_B_ZIP2 = 0x040202;
|
||||
|
||||
internal CompressionType GetCompression()
|
||||
private CompressionType GetCompression()
|
||||
{
|
||||
if (Header.IsDir)
|
||||
return CompressionType.None;
|
||||
|
||||
var coder = Folder!._coders.First();
|
||||
switch (coder._methodId._id)
|
||||
{
|
||||
case K_LZMA:
|
||||
case K_LZMA2:
|
||||
{
|
||||
return CompressionType.LZMA;
|
||||
}
|
||||
case K_PPMD:
|
||||
{
|
||||
return CompressionType.PPMd;
|
||||
}
|
||||
case K_B_ZIP2:
|
||||
{
|
||||
return CompressionType.BZip2;
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
return CompressionType.None;
|
||||
}
|
||||
|
||||
var coder = Folder.NotNull()._coders.First();
|
||||
return coder._methodId._id switch
|
||||
{
|
||||
K_LZMA or K_LZMA2 => CompressionType.LZMA,
|
||||
K_PPMD => CompressionType.PPMd,
|
||||
K_B_ZIP2 => CompressionType.BZip2,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
|
||||
internal bool IsEncrypted =>
|
||||
Header.IsDir
|
||||
? false
|
||||
: Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
|
||||
!Header.IsDir
|
||||
&& Folder?._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
@@ -13,8 +11,8 @@ internal sealed class TarHeader
|
||||
|
||||
public TarHeader(ArchiveEncoding archiveEncoding) => ArchiveEncoding = archiveEncoding;
|
||||
|
||||
internal string Name { get; set; }
|
||||
internal string LinkName { get; set; }
|
||||
internal string? Name { get; set; }
|
||||
internal string? LinkName { get; set; }
|
||||
|
||||
internal long Mode { get; set; }
|
||||
internal long UserId { get; set; }
|
||||
@@ -22,7 +20,7 @@ internal sealed class TarHeader
|
||||
internal long Size { get; set; }
|
||||
internal DateTime LastModifiedTime { get; set; }
|
||||
internal EntryType EntryType { get; set; }
|
||||
internal Stream PackedStream { get; set; }
|
||||
internal Stream? PackedStream { get; set; }
|
||||
internal ArchiveEncoding ArchiveEncoding { get; }
|
||||
|
||||
internal const int BLOCK_SIZE = 512;
|
||||
@@ -36,7 +34,9 @@ internal sealed class TarHeader
|
||||
WriteOctalBytes(0, buffer, 116, 8); // group ID
|
||||
|
||||
//ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257);
|
||||
var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name);
|
||||
var nameByteCount = ArchiveEncoding
|
||||
.GetEncoding()
|
||||
.GetByteCount(Name.NotNull("Name is null"));
|
||||
if (nameByteCount > 100)
|
||||
{
|
||||
// Set mock filename and filetype to indicate the next block is the actual name of the file
|
||||
@@ -46,7 +46,7 @@ internal sealed class TarHeader
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100);
|
||||
WriteStringBytes(ArchiveEncoding.Encode(Name.NotNull("Name is null")), buffer, 100);
|
||||
WriteOctalBytes(Size, buffer, 124, 12);
|
||||
var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds;
|
||||
WriteOctalBytes(time, buffer, 136, 12);
|
||||
@@ -77,7 +77,7 @@ internal sealed class TarHeader
|
||||
//
|
||||
// and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102.
|
||||
Name = ArchiveEncoding.Decode(
|
||||
ArchiveEncoding.Encode(Name),
|
||||
ArchiveEncoding.Encode(Name.NotNull("Name is null")),
|
||||
0,
|
||||
100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1)
|
||||
);
|
||||
@@ -87,7 +87,7 @@ internal sealed class TarHeader
|
||||
|
||||
private void WriteLongFilenameHeader(Stream output)
|
||||
{
|
||||
var nameBytes = ArchiveEncoding.Encode(Name);
|
||||
var nameBytes = ArchiveEncoding.Encode(Name.NotNull("Name is null"));
|
||||
output.Write(nameBytes, 0, nameBytes.Length);
|
||||
|
||||
// pad to multiple of BlockSize bytes, and make sure a terminating null is added
|
||||
@@ -323,5 +323,5 @@ internal sealed class TarHeader
|
||||
|
||||
public long? DataStartPosition { get; set; }
|
||||
|
||||
public string Magic { get; set; }
|
||||
public string? Magic { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -10,9 +8,9 @@ namespace SharpCompress.Common.Tar;
|
||||
|
||||
public class TarEntry : Entry
|
||||
{
|
||||
private readonly TarFilePart _filePart;
|
||||
private readonly TarFilePart? _filePart;
|
||||
|
||||
internal TarEntry(TarFilePart filePart, CompressionType type)
|
||||
internal TarEntry(TarFilePart? filePart, CompressionType type)
|
||||
{
|
||||
_filePart = filePart;
|
||||
CompressionType = type;
|
||||
@@ -22,15 +20,15 @@ public class TarEntry : Entry
|
||||
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string Key => _filePart.Header.Name;
|
||||
public override string? Key => _filePart?.Header.Name;
|
||||
|
||||
public override string LinkTarget => _filePart.Header.LinkName;
|
||||
public override string? LinkTarget => _filePart?.Header.LinkName;
|
||||
|
||||
public override long CompressedSize => _filePart.Header.Size;
|
||||
public override long CompressedSize => _filePart?.Header.Size ?? 0;
|
||||
|
||||
public override long Size => _filePart.Header.Size;
|
||||
public override long Size => _filePart?.Header.Size ?? 0;
|
||||
|
||||
public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime;
|
||||
public override DateTime? LastModifiedTime => _filePart?.Header.LastModifiedTime;
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
@@ -40,17 +38,17 @@ public class TarEntry : Entry
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory;
|
||||
public override bool IsDirectory => _filePart?.Header.EntryType == EntryType.Directory;
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
public long Mode => _filePart.Header.Mode;
|
||||
public long Mode => _filePart?.Header.Mode ?? 0;
|
||||
|
||||
public long UserID => _filePart.Header.UserId;
|
||||
public long UserID => _filePart?.Header.UserId ?? 0;
|
||||
|
||||
public long GroupId => _filePart.Header.GroupId;
|
||||
public long GroupId => _filePart?.Header.GroupId ?? 0;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
|
||||
|
||||
internal static IEnumerable<TarEntry> GetEntries(
|
||||
StreamingMode mode,
|
||||
@@ -59,17 +57,17 @@ public class TarEntry : Entry
|
||||
ArchiveEncoding archiveEncoding
|
||||
)
|
||||
{
|
||||
foreach (var h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding))
|
||||
foreach (var header in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding))
|
||||
{
|
||||
if (h != null)
|
||||
if (header != 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace SharpCompress.Common.Tar;
|
||||
|
||||
internal sealed class TarFilePart : FilePart
|
||||
{
|
||||
private readonly Stream _seekableStream;
|
||||
private readonly Stream? _seekableStream;
|
||||
|
||||
internal TarFilePart(TarHeader header, Stream seekableStream)
|
||||
internal TarFilePart(TarHeader header, Stream? seekableStream)
|
||||
: base(header.ArchiveEncoding)
|
||||
{
|
||||
_seekableStream = seekableStream;
|
||||
@@ -16,16 +16,16 @@ internal sealed class TarFilePart : FilePart
|
||||
|
||||
internal TarHeader Header { get; }
|
||||
|
||||
internal override string FilePartName => Header.Name;
|
||||
internal override string? FilePartName => Header?.Name;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
if (_seekableStream != null)
|
||||
{
|
||||
_seekableStream.Position = Header.DataStartPosition!.Value;
|
||||
_seekableStream.Position = Header.DataStartPosition ?? 0;
|
||||
return new TarReadOnlySubStream(_seekableStream, Header.Size);
|
||||
}
|
||||
return Header.PackedStream;
|
||||
return Header.PackedStream.NotNull();
|
||||
}
|
||||
|
||||
internal override Stream? GetRawStream() => null;
|
||||
|
||||
@@ -9,11 +9,11 @@ public abstract class Volume : IVolume
|
||||
{
|
||||
private readonly Stream _actualStream;
|
||||
|
||||
internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0)
|
||||
internal Volume(Stream stream, ReaderOptions? readerOptions, int index = 0)
|
||||
{
|
||||
Index = index;
|
||||
ReaderOptions = readerOptions;
|
||||
if (readerOptions.LeaveStreamOpen)
|
||||
ReaderOptions = readerOptions ?? new ReaderOptions();
|
||||
if (ReaderOptions.LeaveStreamOpen)
|
||||
{
|
||||
stream = NonDisposingStream.Create(stream);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public abstract class Volume : IVolume
|
||||
|
||||
public virtual int Index { get; internal set; }
|
||||
|
||||
public string FileName => (_actualStream as FileStream)?.Name!;
|
||||
public string? FileName => (_actualStream as FileStream)?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// RarArchive is part of a multi-part archive.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
@@ -20,21 +18,21 @@ internal abstract class ZipFileEntry : ZipHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Name.EndsWith('/'))
|
||||
if (Name?.EndsWith('/') ?? false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//.NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers
|
||||
return CompressedSize == 0 && UncompressedSize == 0 && Name.EndsWith('\\');
|
||||
return CompressedSize == 0 && UncompressedSize == 0 && (Name?.EndsWith('\\') ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
internal Stream PackedStream { get; set; }
|
||||
internal Stream? PackedStream { get; set; }
|
||||
|
||||
internal ArchiveEncoding ArchiveEncoding { get; }
|
||||
|
||||
internal string Name { get; set; }
|
||||
internal string? Name { get; set; }
|
||||
|
||||
internal HeaderFlags Flags { get; set; }
|
||||
|
||||
@@ -48,7 +46,7 @@ internal abstract class ZipFileEntry : ZipHeader
|
||||
|
||||
internal List<ExtraData> Extra { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
public string? Password { get; set; }
|
||||
|
||||
internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream)
|
||||
{
|
||||
@@ -65,7 +63,7 @@ internal abstract class ZipFileEntry : ZipHeader
|
||||
return encryptionData;
|
||||
}
|
||||
|
||||
internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; }
|
||||
internal WinzipAesEncryptionData? WinzipAesEncryptionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The last modified date as read from the Local or Central Directory header.
|
||||
@@ -119,7 +117,7 @@ internal abstract class ZipFileEntry : ZipHeader
|
||||
}
|
||||
}
|
||||
|
||||
internal ZipFilePart Part { get; set; }
|
||||
internal ZipFilePart? Part { get; set; }
|
||||
|
||||
internal bool IsZip64 => CompressedSize >= uint.MaxValue;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ internal class PkwareTraditionalEncryptionData
|
||||
|
||||
internal byte[] StringToByteArray(string value)
|
||||
{
|
||||
var a = _archiveEncoding.Password.GetBytes(value);
|
||||
var a = _archiveEncoding.GetPasswordEncoding().GetBytes(value);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,16 +42,16 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
|
||||
protected override Stream CreateBaseStream()
|
||||
{
|
||||
BaseStream.Position = Header.DataStartPosition!.Value;
|
||||
BaseStream.Position = Header.DataStartPosition.NotNull();
|
||||
|
||||
if (
|
||||
(Header.CompressedSize == 0)
|
||||
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
|
||||
&& (_directoryEntryHeader?.HasData == true)
|
||||
&& (_directoryEntryHeader?.CompressedSize != 0)
|
||||
&& _directoryEntryHeader.HasData
|
||||
&& (_directoryEntryHeader.CompressedSize != 0)
|
||||
)
|
||||
{
|
||||
return new ReadOnlySubStream(BaseStream, _directoryEntryHeader!.CompressedSize);
|
||||
return new ReadOnlySubStream(BaseStream, _directoryEntryHeader.CompressedSize);
|
||||
}
|
||||
|
||||
return BaseStream;
|
||||
|
||||
@@ -13,7 +13,7 @@ internal sealed class StreamingZipFilePart : ZipFilePart
|
||||
internal StreamingZipFilePart(ZipFileEntry header, Stream stream)
|
||||
: base(header, stream) { }
|
||||
|
||||
protected override Stream CreateBaseStream() => Header.PackedStream;
|
||||
protected override Stream CreateBaseStream() => Header.PackedStream.NotNull();
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
|
||||
@@ -42,6 +42,10 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
)
|
||||
)
|
||||
{
|
||||
if (_lastEntryHeader.Part is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
|
||||
ref rewindableStream
|
||||
);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
@@ -8,22 +6,23 @@ namespace SharpCompress.Common.Zip;
|
||||
|
||||
public class ZipEntry : Entry
|
||||
{
|
||||
private readonly ZipFilePart _filePart;
|
||||
private readonly ZipFilePart? _filePart;
|
||||
|
||||
internal ZipEntry(ZipFilePart filePart)
|
||||
internal ZipEntry(ZipFilePart? filePart)
|
||||
{
|
||||
if (filePart != null)
|
||||
if (filePart == null)
|
||||
{
|
||||
_filePart = filePart;
|
||||
LastModifiedTime = Utility.DosDateToDateTime(
|
||||
filePart.Header.LastModifiedDate,
|
||||
filePart.Header.LastModifiedTime
|
||||
);
|
||||
return;
|
||||
}
|
||||
_filePart = filePart;
|
||||
LastModifiedTime = Utility.DosDateToDateTime(
|
||||
filePart.Header.LastModifiedDate,
|
||||
filePart.Header.LastModifiedTime
|
||||
);
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType =>
|
||||
_filePart.Header.CompressionMethod switch
|
||||
_filePart?.Header.CompressionMethod switch
|
||||
{
|
||||
ZipCompressionMethod.BZip2 => CompressionType.BZip2,
|
||||
ZipCompressionMethod.Deflate => CompressionType.Deflate,
|
||||
@@ -35,15 +34,15 @@ public class ZipEntry : Entry
|
||||
_ => CompressionType.Unknown
|
||||
};
|
||||
|
||||
public override long Crc => _filePart.Header.Crc;
|
||||
public override long Crc => _filePart?.Header.Crc ?? 0;
|
||||
|
||||
public override string Key => _filePart.Header.Name;
|
||||
public override string? Key => _filePart?.Header.Name;
|
||||
|
||||
public override string LinkTarget => null;
|
||||
public override string? LinkTarget => null;
|
||||
|
||||
public override long CompressedSize => _filePart.Header.CompressedSize;
|
||||
public override long CompressedSize => _filePart?.Header.CompressedSize ?? 0;
|
||||
|
||||
public override long Size => _filePart.Header.UncompressedSize;
|
||||
public override long Size => _filePart?.Header.UncompressedSize ?? 0;
|
||||
|
||||
public override DateTime? LastModifiedTime { get; }
|
||||
|
||||
@@ -54,11 +53,11 @@ public class ZipEntry : Entry
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted =>
|
||||
FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted);
|
||||
FlagUtility.HasFlag(_filePart?.Header.Flags ?? HeaderFlags.None, HeaderFlags.Encrypted);
|
||||
|
||||
public override bool IsDirectory => _filePart.Header.IsDirectory;
|
||||
public override bool IsDirectory => _filePart?.Header.IsDirectory ?? false;
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ internal abstract class ZipFilePart : FilePart
|
||||
internal Stream BaseStream { get; }
|
||||
internal ZipFileEntry Header { get; set; }
|
||||
|
||||
internal override string FilePartName => Header.Name;
|
||||
internal override string? FilePartName => Header.Name;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
|
||||
@@ -55,7 +55,13 @@ internal class ZipHeaderFactory
|
||||
}
|
||||
case POST_DATA_DESCRIPTOR:
|
||||
{
|
||||
if (FlagUtility.HasFlag(_lastEntryHeader!.Flags, HeaderFlags.UsePostDataDescriptor))
|
||||
if (
|
||||
FlagUtility.HasFlag(
|
||||
_lastEntryHeader.NotNull().Flags,
|
||||
HeaderFlags.UsePostDataDescriptor
|
||||
)
|
||||
&& _lastEntryHeader != null
|
||||
)
|
||||
{
|
||||
_lastEntryHeader.Crc = reader.ReadUInt32();
|
||||
_lastEntryHeader.CompressedSize = zip64
|
||||
|
||||
@@ -20,7 +20,8 @@ internal sealed class AesDecoderStream : DecoderStream2
|
||||
|
||||
public AesDecoderStream(Stream input, byte[] info, IPasswordProvider pass, long limit)
|
||||
{
|
||||
if (pass.CryptoGetTextPassword() == null)
|
||||
var password = pass.CryptoGetTextPassword();
|
||||
if (password == null)
|
||||
{
|
||||
throw new SharpCompress.Common.CryptographicException(
|
||||
"Encrypted 7Zip archive has no password specified."
|
||||
@@ -37,8 +38,8 @@ internal sealed class AesDecoderStream : DecoderStream2
|
||||
|
||||
Init(info, out var numCyclesPower, out var salt, out var seed);
|
||||
|
||||
var password = Encoding.Unicode.GetBytes(pass.CryptoGetTextPassword());
|
||||
var key = InitKey(numCyclesPower, salt, password);
|
||||
var passwordBytes = Encoding.Unicode.GetBytes(password);
|
||||
var key = InitKey(numCyclesPower, salt, passwordBytes);
|
||||
if (key == null)
|
||||
{
|
||||
throw new InvalidOperationException("Initialized with null key");
|
||||
|
||||
@@ -63,18 +63,18 @@ public sealed class LZipStream : Stream
|
||||
var crc32Stream = (Crc32Stream)_stream;
|
||||
crc32Stream.WrappedStream.Dispose();
|
||||
crc32Stream.Dispose();
|
||||
var compressedCount = _countingWritableSubStream!.Count;
|
||||
var compressedCount = _countingWritableSubStream.NotNull().Count;
|
||||
|
||||
Span<byte> intBuf = stackalloc byte[8];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc32Stream.Crc);
|
||||
_countingWritableSubStream.Write(intBuf.Slice(0, 4));
|
||||
_countingWritableSubStream?.Write(intBuf.Slice(0, 4));
|
||||
|
||||
BinaryPrimitives.WriteInt64LittleEndian(intBuf, _writeCount);
|
||||
_countingWritableSubStream.Write(intBuf);
|
||||
_countingWritableSubStream?.Write(intBuf);
|
||||
|
||||
//total with headers
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf, compressedCount + 6 + 20);
|
||||
_countingWritableSubStream.Write(intBuf);
|
||||
_countingWritableSubStream?.Write(intBuf);
|
||||
}
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@ namespace SharpCompress.Compressors.LZMA.Utilites;
|
||||
|
||||
internal interface IPasswordProvider
|
||||
{
|
||||
string CryptoGetTextPassword();
|
||||
string? CryptoGetTextPassword();
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ public class SourceStream : Stream
|
||||
private long _prevSize;
|
||||
private readonly List<FileInfo> _files;
|
||||
private readonly List<Stream> _streams;
|
||||
private readonly Func<int, FileInfo?> _getFilePart;
|
||||
private readonly Func<int, Stream?> _getStreamPart;
|
||||
private readonly Func<int, FileInfo?>? _getFilePart;
|
||||
private readonly Func<int, Stream?>? _getStreamPart;
|
||||
private int _stream;
|
||||
|
||||
public SourceStream(FileInfo file, Func<int, FileInfo?> getPart, ReaderOptions options)
|
||||
@@ -38,8 +38,8 @@ public class SourceStream : Stream
|
||||
if (!IsFileMode)
|
||||
{
|
||||
_streams.Add(stream!);
|
||||
_getStreamPart = getStreamPart!;
|
||||
_getFilePart = _ => null!;
|
||||
_getStreamPart = getStreamPart;
|
||||
_getFilePart = _ => null;
|
||||
if (stream is FileStream fileStream)
|
||||
{
|
||||
_files.Add(new FileInfo(fileStream.Name));
|
||||
@@ -49,8 +49,8 @@ public class SourceStream : Stream
|
||||
{
|
||||
_files.Add(file!);
|
||||
_streams.Add(_files[0].OpenRead());
|
||||
_getFilePart = getFilePart!;
|
||||
_getStreamPart = _ => null!;
|
||||
_getFilePart = getFilePart;
|
||||
_getStreamPart = _ => null;
|
||||
}
|
||||
_stream = 0;
|
||||
_prevSize = 0;
|
||||
@@ -78,7 +78,7 @@ public class SourceStream : Stream
|
||||
{
|
||||
if (IsFileMode)
|
||||
{
|
||||
var f = _getFilePart(_streams.Count);
|
||||
var f = _getFilePart.NotNull("GetFilePart is null")(_streams.Count);
|
||||
if (f == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
@@ -90,7 +90,7 @@ public class SourceStream : Stream
|
||||
}
|
||||
else
|
||||
{
|
||||
var s = _getStreamPart(_streams.Count);
|
||||
var s = _getStreamPart.NotNull("GetStreamPart is null")(_streams.Count);
|
||||
if (s == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
|
||||
65
src/SharpCompress/NotNullExtensions.cs
Normal file
65
src/SharpCompress/NotNullExtensions.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace SharpCompress;
|
||||
|
||||
public static class NotNullExtensions
|
||||
{
|
||||
public static IEnumerable<T> Empty<T>(this IEnumerable<T>? source) =>
|
||||
source ?? Enumerable.Empty<T>();
|
||||
|
||||
public static IEnumerable<T> Empty<T>(this T? source)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
return source.AsEnumerable();
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK || NETSTANDARD
|
||||
public static T NotNull<T>(this T? obj, string? message = null)
|
||||
where T : class
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(message ?? "Value is null");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static T NotNull<T>(this T? obj, string? message = null)
|
||||
where T : struct
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(message ?? "Value is null");
|
||||
}
|
||||
return obj.Value;
|
||||
}
|
||||
#else
|
||||
|
||||
public static T NotNull<T>(
|
||||
[NotNull] this T? obj,
|
||||
[CallerArgumentExpression(nameof(obj))] string? paramName = null
|
||||
)
|
||||
where T : class
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(obj, paramName);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static T NotNull<T>(
|
||||
[NotNull] this T? obj,
|
||||
[CallerArgumentExpression(nameof(obj))] string? paramName = null
|
||||
)
|
||||
where T : struct
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(obj, paramName);
|
||||
return obj.Value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -13,9 +13,9 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
where TEntry : Entry
|
||||
where TVolume : Volume
|
||||
{
|
||||
private bool completed;
|
||||
private IEnumerator<TEntry>? entriesForCurrentReadStream;
|
||||
private bool wroteCurrentEntry;
|
||||
private bool _completed;
|
||||
private IEnumerator<TEntry>? _entriesForCurrentReadStream;
|
||||
private bool _wroteCurrentEntry;
|
||||
|
||||
public event EventHandler<ReaderExtractionEventArgs<IEntry>>? EntryExtractionProgress;
|
||||
|
||||
@@ -35,18 +35,18 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
/// <summary>
|
||||
/// Current volume that the current entry resides in
|
||||
/// </summary>
|
||||
public abstract TVolume Volume { get; }
|
||||
public abstract TVolume? Volume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current file entry
|
||||
/// </summary>
|
||||
public TEntry Entry => entriesForCurrentReadStream!.Current;
|
||||
public TEntry Entry => _entriesForCurrentReadStream.NotNull().Current;
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
entriesForCurrentReadStream?.Dispose();
|
||||
_entriesForCurrentReadStream?.Dispose();
|
||||
Volume?.Dispose();
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
if (!completed)
|
||||
if (!_completed)
|
||||
{
|
||||
Cancelled = true;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
|
||||
public bool MoveToNextEntry()
|
||||
{
|
||||
if (completed)
|
||||
if (_completed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -77,27 +77,27 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
{
|
||||
throw new ReaderCancelledException("Reader has been cancelled.");
|
||||
}
|
||||
if (entriesForCurrentReadStream is null)
|
||||
if (_entriesForCurrentReadStream is null)
|
||||
{
|
||||
return LoadStreamForReading(RequestInitialStream());
|
||||
}
|
||||
if (!wroteCurrentEntry)
|
||||
if (!_wroteCurrentEntry)
|
||||
{
|
||||
SkipEntry();
|
||||
}
|
||||
wroteCurrentEntry = false;
|
||||
_wroteCurrentEntry = false;
|
||||
if (NextEntryForCurrentStream())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
completed = true;
|
||||
_completed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool LoadStreamForReading(Stream stream)
|
||||
{
|
||||
entriesForCurrentReadStream?.Dispose();
|
||||
if ((stream is null) || (!stream.CanRead))
|
||||
_entriesForCurrentReadStream?.Dispose();
|
||||
if (stream is null || !stream.CanRead)
|
||||
{
|
||||
throw new MultipartStreamRequiredException(
|
||||
"File is split into multiple archives: '"
|
||||
@@ -105,13 +105,15 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
+ "'. A new readable stream is required. Use Cancel if it was intended."
|
||||
);
|
||||
}
|
||||
entriesForCurrentReadStream = GetEntries(stream).GetEnumerator();
|
||||
return entriesForCurrentReadStream.MoveNext();
|
||||
_entriesForCurrentReadStream = GetEntries(stream).GetEnumerator();
|
||||
return _entriesForCurrentReadStream.MoveNext();
|
||||
}
|
||||
|
||||
protected virtual Stream RequestInitialStream() => Volume.Stream;
|
||||
protected virtual Stream RequestInitialStream() =>
|
||||
Volume.NotNull("Volume isn't loaded.").Stream;
|
||||
|
||||
internal virtual bool NextEntryForCurrentStream() => entriesForCurrentReadStream!.MoveNext();
|
||||
internal virtual bool NextEntryForCurrentStream() =>
|
||||
_entriesForCurrentReadStream.NotNull().MoveNext();
|
||||
|
||||
protected abstract IEnumerable<TEntry> GetEntries(Stream stream);
|
||||
|
||||
@@ -149,7 +151,7 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
|
||||
public void WriteEntryTo(Stream writableStream)
|
||||
{
|
||||
if (wroteCurrentEntry)
|
||||
if (_wroteCurrentEntry)
|
||||
{
|
||||
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
|
||||
}
|
||||
@@ -166,7 +168,7 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
}
|
||||
|
||||
Write(writableStream);
|
||||
wroteCurrentEntry = true;
|
||||
_wroteCurrentEntry = true;
|
||||
}
|
||||
|
||||
internal void Write(Stream writeStream)
|
||||
@@ -178,12 +180,12 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
|
||||
public EntryStream OpenEntryStream()
|
||||
{
|
||||
if (wroteCurrentEntry)
|
||||
if (_wroteCurrentEntry)
|
||||
{
|
||||
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
|
||||
}
|
||||
var stream = GetEntryStream();
|
||||
wroteCurrentEntry = true;
|
||||
_wroteCurrentEntry = true;
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ namespace SharpCompress.Readers.GZip;
|
||||
|
||||
public class GZipReader : AbstractReader<GZipEntry, GZipVolume>
|
||||
{
|
||||
internal GZipReader(Stream stream, ReaderOptions options)
|
||||
: base(options, ArchiveType.GZip) => Volume = new GZipVolume(stream, options);
|
||||
private GZipReader(Stream stream, ReaderOptions options)
|
||||
: base(options, ArchiveType.GZip) => Volume = new GZipVolume(stream, options, 0);
|
||||
|
||||
public override GZipVolume Volume { get; }
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ internal class MultiVolumeRarReader : RarReader
|
||||
internal MultiVolumeRarReader(IEnumerable<Stream> streams, ReaderOptions options)
|
||||
: base(options) => this.streams = streams.GetEnumerator();
|
||||
|
||||
internal override void ValidateArchive(RarVolume archive) { }
|
||||
protected override void ValidateArchive(RarVolume archive) { }
|
||||
|
||||
protected override Stream RequestInitialStream()
|
||||
{
|
||||
|
||||
@@ -14,16 +14,16 @@ namespace SharpCompress.Readers.Rar;
|
||||
public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
{
|
||||
private RarVolume? volume;
|
||||
internal Lazy<IRarUnpack> UnpackV2017 { get; } =
|
||||
private Lazy<IRarUnpack> UnpackV2017 { get; } =
|
||||
new(() => new Compressors.Rar.UnpackV2017.Unpack());
|
||||
internal Lazy<IRarUnpack> UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
|
||||
private Lazy<IRarUnpack> UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
|
||||
|
||||
internal RarReader(ReaderOptions options)
|
||||
: base(options, ArchiveType.Rar) { }
|
||||
|
||||
internal abstract void ValidateArchive(RarVolume archive);
|
||||
protected abstract void ValidateArchive(RarVolume archive);
|
||||
|
||||
public override RarVolume Volume => volume!;
|
||||
public override RarVolume? Volume => volume;
|
||||
|
||||
/// <summary>
|
||||
/// Opens a RarReader for Non-seeking usage with a single volume
|
||||
@@ -51,7 +51,7 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
|
||||
protected override IEnumerable<RarReaderEntry> GetEntries(Stream stream)
|
||||
{
|
||||
volume = new RarReaderVolume(stream, Options);
|
||||
volume = new RarReaderVolume(stream, Options, 0);
|
||||
foreach (var fp in volume.ReadFileParts())
|
||||
{
|
||||
ValidateArchive(volume);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace SharpCompress.Readers.Rar;
|
||||
|
||||
public class RarReaderVolume : RarVolume
|
||||
{
|
||||
internal RarReaderVolume(Stream stream, ReaderOptions options, int index = 0)
|
||||
internal RarReaderVolume(Stream stream, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Streaming, stream, options, index) { }
|
||||
|
||||
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
|
||||
|
||||
@@ -11,7 +11,7 @@ internal class SingleVolumeRarReader : RarReader
|
||||
internal SingleVolumeRarReader(Stream stream, ReaderOptions options)
|
||||
: base(options) => this.stream = stream;
|
||||
|
||||
internal override void ValidateArchive(RarVolume archive)
|
||||
protected override void ValidateArchive(RarVolume archive)
|
||||
{
|
||||
if (archive.IsMultiVolume)
|
||||
{
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Writers;
|
||||
|
||||
public abstract class AbstractWriter : IWriter
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
public abstract class AbstractWriter(ArchiveType type, WriterOptions writerOptions) : IWriter
|
||||
{
|
||||
private bool _isDisposed;
|
||||
|
||||
protected AbstractWriter(ArchiveType type, WriterOptions writerOptions)
|
||||
{
|
||||
WriterType = type;
|
||||
WriterOptions = writerOptions;
|
||||
}
|
||||
//always initializes the stream
|
||||
|
||||
protected void InitalizeStream(Stream stream) => OutputStream = stream;
|
||||
protected void InitializeStream(Stream stream) => OutputStream = stream;
|
||||
|
||||
protected Stream OutputStream { get; private set; }
|
||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
public ArchiveType WriterType { get; }
|
||||
public ArchiveType WriterType { get; } = type;
|
||||
|
||||
protected WriterOptions WriterOptions { get; }
|
||||
protected WriterOptions WriterOptions { get; } = writerOptions;
|
||||
|
||||
public abstract void Write(string filename, Stream source, DateTime? modificationTime);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class GZipWriter : AbstractWriter
|
||||
{
|
||||
destination = NonDisposingStream.Create(destination);
|
||||
}
|
||||
InitalizeStream(
|
||||
InitializeStream(
|
||||
new GZipStream(
|
||||
destination,
|
||||
CompressionMode.Compress,
|
||||
|
||||
@@ -53,7 +53,7 @@ public class TarWriter : AbstractWriter
|
||||
);
|
||||
}
|
||||
}
|
||||
InitalizeStream(destination);
|
||||
InitializeStream(destination);
|
||||
}
|
||||
|
||||
public override void Write(string filename, Stream source, DateTime? modificationTime) =>
|
||||
|
||||
@@ -42,14 +42,14 @@ public class ZipWriter : AbstractWriter
|
||||
{
|
||||
destination = NonDisposingStream.Create(destination);
|
||||
}
|
||||
InitalizeStream(destination);
|
||||
InitializeStream(destination);
|
||||
}
|
||||
|
||||
private PpmdProperties PpmdProperties => ppmdProps ??= new PpmdProperties();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
if (isDisposing && OutputStream is not null)
|
||||
{
|
||||
ulong size = 0;
|
||||
foreach (var entry in entries)
|
||||
@@ -114,7 +114,7 @@ public class ZipWriter : AbstractWriter
|
||||
streamPosition += headersize;
|
||||
return new ZipWritingStream(
|
||||
this,
|
||||
OutputStream,
|
||||
OutputStream.NotNull(),
|
||||
entry,
|
||||
compression,
|
||||
options.DeflateCompressionLevel ?? compressionLevel
|
||||
@@ -419,13 +419,14 @@ public class ZipWriter : AbstractWriter
|
||||
return;
|
||||
}
|
||||
|
||||
var countingCount = counting?.Count ?? 0;
|
||||
entry.Crc = (uint)crc.Crc32Result;
|
||||
entry.Compressed = counting!.Count;
|
||||
entry.Compressed = countingCount;
|
||||
entry.Decompressed = decompressed;
|
||||
|
||||
var zip64 =
|
||||
entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue;
|
||||
var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count;
|
||||
var compressedvalue = zip64 ? uint.MaxValue : (uint)countingCount;
|
||||
var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed;
|
||||
|
||||
if (originalStream.CanSeek)
|
||||
@@ -433,7 +434,7 @@ public class ZipWriter : AbstractWriter
|
||||
originalStream.Position = (long)(entry.HeaderOffset + 6);
|
||||
originalStream.WriteByte(0);
|
||||
|
||||
if (counting.Count == 0 && entry.Decompressed == 0)
|
||||
if (countingCount == 0 && entry.Decompressed == 0)
|
||||
{
|
||||
// set compression to STORED for zero byte files (no compression data)
|
||||
originalStream.Position = (long)(entry.HeaderOffset + 8);
|
||||
@@ -520,11 +521,12 @@ public class ZipWriter : AbstractWriter
|
||||
// if we can prevent the writes from happening
|
||||
if (entry.Zip64HeaderOffset == 0)
|
||||
{
|
||||
var countingCount = counting?.Count ?? 0;
|
||||
// Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it
|
||||
if (
|
||||
limitsExceeded
|
||||
|| ((decompressed + (uint)count) > uint.MaxValue)
|
||||
|| (counting!.Count + (uint)count) > uint.MaxValue
|
||||
|| (countingCount + (uint)count) > uint.MaxValue
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
@@ -539,8 +541,9 @@ public class ZipWriter : AbstractWriter
|
||||
|
||||
if (entry.Zip64HeaderOffset == 0)
|
||||
{
|
||||
var countingCount = counting?.Count ?? 0;
|
||||
// Post-check, this is accurate
|
||||
if ((decompressed > uint.MaxValue) || counting!.Count > uint.MaxValue)
|
||||
if ((decompressed > uint.MaxValue) || countingCount > uint.MaxValue)
|
||||
{
|
||||
// We have written the data, so the archive is now broken
|
||||
// Throwing the exception here, allows us to avoid
|
||||
|
||||
@@ -240,12 +240,10 @@ public class ArchiveTests : ReaderTests
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
#if !NETFRAMEWORK
|
||||
if (!OperatingSystem.IsWindows())
|
||||
if (!Environment.OSVersion.IsWindows())
|
||||
{
|
||||
fileOrder = fileOrder.Replace('\\', '/');
|
||||
}
|
||||
#endif
|
||||
var expected = new Stack<string>(fileOrder.Split(' '));
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
using var archive = ArchiveFactory.Open(testArchive, readerOptions);
|
||||
|
||||
@@ -19,7 +19,7 @@ public class GZipArchiveTests : ArchiveTests
|
||||
using (var archive = ArchiveFactory.Open(stream))
|
||||
{
|
||||
var entry = archive.Entries.First();
|
||||
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key));
|
||||
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
|
||||
|
||||
var size = entry.Size;
|
||||
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"));
|
||||
@@ -41,7 +41,7 @@ public class GZipArchiveTests : ArchiveTests
|
||||
using (var archive = GZipArchive.Open(stream))
|
||||
{
|
||||
var entry = archive.Entries.First();
|
||||
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key));
|
||||
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
|
||||
|
||||
var size = entry.Size;
|
||||
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"));
|
||||
@@ -94,6 +94,7 @@ public class GZipArchiveTests : ArchiveTests
|
||||
using (var entryStream = archiveEntry.OpenEntryStream())
|
||||
{
|
||||
var result = TarArchive.IsTarFile(entryStream);
|
||||
Assert.True(result);
|
||||
}
|
||||
Assert.Equal(size, tarStream.Length);
|
||||
using (var entryStream = archiveEntry.OpenEntryStream())
|
||||
|
||||
11
tests/SharpCompress.Test/OperatingSystemExtensions.cs
Normal file
11
tests/SharpCompress.Test/OperatingSystemExtensions.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Test;
|
||||
|
||||
public static class OperatingSystemExtensions
|
||||
{
|
||||
public static bool IsWindows(this OperatingSystem os) =>
|
||||
os.Platform == PlatformID.Win32NT
|
||||
|| os.Platform == PlatformID.Win32Windows
|
||||
|| os.Platform == PlatformID.Win32S;
|
||||
}
|
||||
@@ -209,7 +209,7 @@ public class RarReaderTests : ReaderTests
|
||||
{
|
||||
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
||||
using var entryStream = reader.OpenEntryStream();
|
||||
var file = Path.GetFileName(reader.Entry.Key);
|
||||
var file = Path.GetFileName(reader.Entry.Key).NotNull();
|
||||
var folder =
|
||||
Path.GetDirectoryName(reader.Entry.Key)
|
||||
?? throw new ArgumentNullException();
|
||||
@@ -293,7 +293,7 @@ public class RarReaderTests : ReaderTests
|
||||
using var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true });
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (reader.Entry.Key.Contains("jpg"))
|
||||
if (reader.Entry.Key.NotNull().Contains("jpg"))
|
||||
{
|
||||
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
||||
reader.WriteEntryToDirectory(
|
||||
@@ -316,7 +316,7 @@ public class RarReaderTests : ReaderTests
|
||||
using var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true });
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (reader.Entry.Key.Contains("jpg"))
|
||||
if (reader.Entry.Key.NotNull().Contains("jpg"))
|
||||
{
|
||||
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
||||
reader.WriteEntryToDirectory(
|
||||
|
||||
@@ -77,12 +77,10 @@ public abstract class ReaderTests : TestBase
|
||||
ReaderOptions? options = null
|
||||
)
|
||||
{
|
||||
#if !NETFRAMEWORK
|
||||
if (!OperatingSystem.IsWindows())
|
||||
if (!Environment.OSVersion.IsWindows())
|
||||
{
|
||||
fileOrder = fileOrder.Replace('\\', '/');
|
||||
}
|
||||
#endif
|
||||
var expected = new Stack<string>(fileOrder.Split(' '));
|
||||
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
|
||||
@@ -182,7 +182,7 @@ public class SevenZipArchiveTests : ArchiveTests
|
||||
using (var archive = SevenZipArchive.Open(stream))
|
||||
{
|
||||
var entry = archive.Entries.First();
|
||||
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key));
|
||||
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
|
||||
|
||||
var size = entry.Size;
|
||||
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "7Zip.Tar.tar"));
|
||||
|
||||
@@ -195,7 +195,7 @@ public class TarArchiveTests : ArchiveTests
|
||||
using (var archive = TarArchive.Open(unmodified))
|
||||
{
|
||||
var entry = archive.Entries.Single(x =>
|
||||
x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
);
|
||||
archive.RemoveEntry(entry);
|
||||
archive.SaveTo(scratchPath, CompressionType.None);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class TarReaderTests : ReaderTests
|
||||
{
|
||||
Directory.CreateDirectory(destdir);
|
||||
}
|
||||
var destinationFileName = Path.Combine(destdir, file);
|
||||
var destinationFileName = Path.Combine(destdir, file.NotNull());
|
||||
|
||||
using var fs = File.OpenWrite(destinationFileName);
|
||||
entryStream.TransferTo(fs);
|
||||
@@ -105,7 +105,7 @@ public class TarReaderTests : ReaderTests
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
filePaths.Add(reader.Entry.Key);
|
||||
filePaths.Add(reader.Entry.Key.NotNull("Entry Key is null"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ public class TarReaderTests : ReaderTests
|
||||
Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType);
|
||||
using var entryStream = reader.OpenEntryStream();
|
||||
entryStream.SkipEntry();
|
||||
names.Add(reader.Entry.Key);
|
||||
names.Add(reader.Entry.Key.NotNull());
|
||||
}
|
||||
}
|
||||
Assert.Equal(3, names.Count);
|
||||
@@ -224,7 +224,7 @@ public class TarReaderTests : ReaderTests
|
||||
{
|
||||
if (reader.Entry.LinkTarget != null)
|
||||
{
|
||||
var path = Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key);
|
||||
var path = Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key.NotNull());
|
||||
var link = new Mono.Unix.UnixSymbolicLinkInfo(path);
|
||||
if (link.HasContents)
|
||||
{
|
||||
|
||||
@@ -216,8 +216,8 @@ public class TestBase : IDisposable
|
||||
while (archive1.MoveToNextEntry())
|
||||
{
|
||||
Assert.True(archive2.MoveToNextEntry());
|
||||
archive1Entries.Add(archive1.Entry.Key);
|
||||
archive2Entries.Add(archive2.Entry.Key);
|
||||
archive1Entries.Add(archive1.Entry.Key.NotNull());
|
||||
archive2Entries.Add(archive2.Entry.Key.NotNull());
|
||||
}
|
||||
Assert.False(archive2.MoveToNextEntry());
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ public class ZipArchiveTests : ArchiveTests
|
||||
using (var archive = ZipArchive.Open(unmodified))
|
||||
{
|
||||
var entry = archive.Entries.Single(x =>
|
||||
x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
);
|
||||
archive.RemoveEntry(entry);
|
||||
|
||||
@@ -249,16 +249,18 @@ public class ZipArchiveTests : ArchiveTests
|
||||
var scratchPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip");
|
||||
|
||||
using var vfs = (ZipArchive)ArchiveFactory.Open(scratchPath);
|
||||
var e = vfs.Entries.First(v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase));
|
||||
var e = vfs.Entries.First(v =>
|
||||
v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
);
|
||||
vfs.RemoveEntry(e);
|
||||
Assert.Null(
|
||||
vfs.Entries.FirstOrDefault(v =>
|
||||
v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
);
|
||||
Assert.Null(
|
||||
((IArchive)vfs).Entries.FirstOrDefault(v =>
|
||||
v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -394,12 +396,12 @@ public class ZipArchiveTests : ArchiveTests
|
||||
archive.AddAllFromDirectory(SCRATCH_FILES_PATH);
|
||||
archive.RemoveEntry(
|
||||
archive.Entries.Single(x =>
|
||||
x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
);
|
||||
Assert.Null(
|
||||
archive.Entries.FirstOrDefault(x =>
|
||||
x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user