From 3f583916719eeef598d10a5d4e14ef14f008243b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 11 May 2021 07:36:37 -0700 Subject: Merge Dtf --- .../WixToolset.Dtf.Compression/ArchiveFileInfo.cs | 430 +++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs (limited to 'src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs') diff --git a/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs b/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs new file mode 100644 index 00000000..adcae3ec --- /dev/null +++ b/src/dtf/WixToolset.Dtf.Compression/ArchiveFileInfo.cs @@ -0,0 +1,430 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Dtf.Compression +{ + using System; + using System.IO; + using System.Runtime.Serialization; + using System.Diagnostics.CodeAnalysis; + + /// + /// Abstract object representing a compressed file within an archive; + /// provides operations for getting the file properties and unpacking + /// the file. + /// + [Serializable] + public abstract class ArchiveFileInfo : FileSystemInfo + { + private ArchiveInfo archiveInfo; + private string name; + private string path; + + private bool initialized; + private bool exists; + private int archiveNumber; + private FileAttributes attributes; + private DateTime lastWriteTime; + private long length; + + /// + /// Creates a new ArchiveFileInfo object representing a file within + /// an archive in a specified path. + /// + /// An object representing the archive + /// containing the file. + /// The path to the file within the archive. + /// Usually, this is a simple file name, but if the archive contains + /// a directory structure this may include the directory. + protected ArchiveFileInfo(ArchiveInfo archiveInfo, string filePath) + : base() + { + if (filePath == null) + { + throw new ArgumentNullException("filePath"); + } + + this.Archive = archiveInfo; + + this.name = System.IO.Path.GetFileName(filePath); + this.path = System.IO.Path.GetDirectoryName(filePath); + + this.attributes = FileAttributes.Normal; + this.lastWriteTime = DateTime.MinValue; + } + + /// + /// Creates a new ArchiveFileInfo object with all parameters specified; + /// used by subclasses when reading the metadata out of an archive. + /// + /// The internal path and name of the file in + /// the archive. + /// The archive number where the file + /// starts. + /// The stored attributes of the file. + /// The stored last write time of the + /// file. + /// The uncompressed size of the file. + protected ArchiveFileInfo( + string filePath, + int archiveNumber, + FileAttributes attributes, + DateTime lastWriteTime, + long length) + : this(null, filePath) + { + this.exists = true; + this.archiveNumber = archiveNumber; + this.attributes = attributes; + this.lastWriteTime = lastWriteTime; + this.length = length; + this.initialized = true; + } + + /// + /// Initializes a new instance of the ArchiveFileInfo class with + /// serialized data. + /// + /// The SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// The StreamingContext that contains contextual + /// information about the source or destination. + protected ArchiveFileInfo(SerializationInfo info, StreamingContext context) + : base(info, context) + { + this.archiveInfo = (ArchiveInfo) info.GetValue( + "archiveInfo", typeof(ArchiveInfo)); + this.name = info.GetString("name"); + this.path = info.GetString("path"); + this.initialized = info.GetBoolean("initialized"); + this.exists = info.GetBoolean("exists"); + this.archiveNumber = info.GetInt32("archiveNumber"); + this.attributes = (FileAttributes) info.GetValue( + "attributes", typeof(FileAttributes)); + this.lastWriteTime = info.GetDateTime("lastWriteTime"); + this.length = info.GetInt64("length"); + } + + /// + /// Gets the name of the file. + /// + /// The name of the file, not including any path. + public override string Name + { + get + { + return this.name; + } + } + + /// + /// Gets the internal path of the file in the archive. + /// + /// The internal path of the file in the archive, not including + /// the file name. + public string Path + { + get + { + return this.path; + } + } + + /// + /// Gets the full path to the file. + /// + /// The full path to the file, including the full path to the + /// archive, the internal path in the archive, and the file name. + /// + /// For example, the path "C:\archive.cab\file.txt" refers to + /// a file "file.txt" inside the archive "archive.cab". + /// + public override string FullName + { + get + { + string fullName = System.IO.Path.Combine(this.Path, this.Name); + + if (this.Archive != null) + { + fullName = System.IO.Path.Combine(this.ArchiveName, fullName); + } + + return fullName; + } + } + + /// + /// Gets or sets the archive that contains this file. + /// + /// + /// The ArchiveInfo instance that retrieved this file information -- this + /// may be null if the ArchiveFileInfo object was returned directly from + /// a stream. + /// + public ArchiveInfo Archive + { + get + { + return (ArchiveInfo) this.archiveInfo; + } + + internal set + { + this.archiveInfo = value; + + // protected instance members inherited from FileSystemInfo: + this.OriginalPath = (value != null ? value.FullName : null); + this.FullPath = this.OriginalPath; + } + } + + /// + /// Gets the full path of the archive that contains this file. + /// + /// The full path of the archive that contains this file. + public string ArchiveName + { + get + { + return this.Archive != null ? this.Archive.FullName : null; + } + } + + /// + /// Gets the number of the archive where this file starts. + /// + /// The number of the archive where this file starts. + /// A single archive or the first archive in a chain is + /// numbered 0. + public int ArchiveNumber + { + get + { + return this.archiveNumber; + } + } + + /// + /// Checks if the file exists within the archive. + /// + /// True if the file exists, false otherwise. + public override bool Exists + { + get + { + if (!this.initialized) + { + this.Refresh(); + } + + return this.exists; + } + } + + /// + /// Gets the uncompressed size of the file. + /// + /// The uncompressed size of the file in bytes. + public long Length + { + get + { + if (!this.initialized) + { + this.Refresh(); + } + + return this.length; + } + } + + /// + /// Gets the attributes of the file. + /// + /// The attributes of the file as stored in the archive. + public new FileAttributes Attributes + { + get + { + if (!this.initialized) + { + this.Refresh(); + } + + return this.attributes; + } + } + + /// + /// Gets the last modification time of the file. + /// + /// The last modification time of the file as stored in the + /// archive. + public new DateTime LastWriteTime + { + get + { + if (!this.initialized) + { + this.Refresh(); + } + + return this.lastWriteTime; + } + } + + /// + /// Sets the SerializationInfo with information about the archive. + /// + /// The SerializationInfo that holds the serialized + /// object data. + /// The StreamingContext that contains contextual + /// information about the source or destination. + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("archiveInfo", this.archiveInfo); + info.AddValue("name", this.name); + info.AddValue("path", this.path); + info.AddValue("initialized", this.initialized); + info.AddValue("exists", this.exists); + info.AddValue("archiveNumber", this.archiveNumber); + info.AddValue("attributes", this.attributes); + info.AddValue("lastWriteTime", this.lastWriteTime); + info.AddValue("length", this.length); + } + + /// + /// Gets the full path to the file. + /// + /// The same as + public override string ToString() + { + return this.FullName; + } + + /// + /// Deletes the file. NOT SUPPORTED. + /// + /// Files cannot be deleted + /// from an existing archive. + public override void Delete() + { + throw new NotSupportedException(); + } + + /// + /// Refreshes the attributes and other cached information about the file, + /// by re-reading the information from the archive. + /// + public new void Refresh() + { + base.Refresh(); + + if (this.Archive != null) + { + string filePath = System.IO.Path.Combine(this.Path, this.Name); + ArchiveFileInfo updatedFile = this.Archive.GetFile(filePath); + if (updatedFile == null) + { + throw new FileNotFoundException( + "File not found in archive.", filePath); + } + + this.Refresh(updatedFile); + } + } + + /// + /// Extracts the file. + /// + /// The destination path where the file + /// will be extracted. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")] + public void CopyTo(string destFileName) + { + this.CopyTo(destFileName, false); + } + + /// + /// Extracts the file, optionally overwriting any existing file. + /// + /// The destination path where the file + /// will be extracted. + /// If true, + /// will be overwritten if it exists. + /// is false + /// and exists. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dest")] + public void CopyTo(string destFileName, bool overwrite) + { + if (destFileName == null) + { + throw new ArgumentNullException("destFileName"); + } + + if (!overwrite && File.Exists(destFileName)) + { + throw new IOException(); + } + + if (this.Archive == null) + { + throw new InvalidOperationException(); + } + + this.Archive.UnpackFile( + System.IO.Path.Combine(this.Path, this.Name), destFileName); + } + + /// + /// Opens the archive file for reading without actually extracting the + /// file to disk. + /// + /// + /// A stream for reading directly from the packed file. Like any stream + /// this should be closed/disposed as soon as it is no longer needed. + /// + public Stream OpenRead() + { + return this.Archive.OpenRead(System.IO.Path.Combine(this.Path, this.Name)); + } + + /// + /// Opens the archive file reading text with UTF-8 encoding without + /// actually extracting the file to disk. + /// + /// + /// A reader for reading text directly from the packed file. Like any reader + /// this should be closed/disposed as soon as it is no longer needed. + /// + /// + /// To open an archived text file with different encoding, use the + /// method and pass the returned stream to one of + /// the constructor overloads. + /// + public StreamReader OpenText() + { + return this.Archive.OpenText(System.IO.Path.Combine(this.Path, this.Name)); + } + + /// + /// Refreshes the information in this object with new data retrieved + /// from an archive. + /// + /// Fresh instance for the same file just + /// read from the archive. + /// + /// Subclasses may override this method to refresh sublcass fields. + /// However they should always call the base implementation first. + /// + protected virtual void Refresh(ArchiveFileInfo newFileInfo) + { + this.exists = newFileInfo.exists; + this.length = newFileInfo.length; + this.attributes = newFileInfo.attributes; + this.lastWriteTime = newFileInfo.lastWriteTime; + } + } +} -- cgit v1.2.3-55-g6feb