using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using Duplicati.Library.Interface; namespace Duplicati.Library.Main { /// /// Implementation of a class that ensures all filenames are emitted to the compression stream using / as the directory seperator, /// but reports the local path format to the caller. This ensures that the archives are cross-platform and avoids cluttering the code /// with if()'s. The compression modules can be made totally unaware of the path issues. /// It also adds a few convenience methods, so each compression module gets these methods without having to implement them. /// public class CompressionWrapper : IDisposable { /// /// The compression instance being wrapped /// private ICompression m_compressor; /// /// Flag describing if we should use UTC times /// private bool m_useUtcTimes = true; /// /// Constructs a new compression wrapper /// /// The compression instance to wrap public CompressionWrapper(ICompression compressor) { if (compressor == null) throw new ArgumentNullException("compressor"); m_compressor = compressor; } /// /// Converts all paths to use / as the directory separator /// /// The paths to convert /// The converted paths public static string[] ToArchivePaths(string[] paths) { if (System.IO.Path.DirectorySeparatorChar != '/') for (int i = 0; i < paths.Length; i++) paths[i] = ToArchivePath(paths[i]); return paths; } /// /// Converts all paths from using / as the directory separator /// /// The paths to convert /// The converted paths public static string[] ToSystemPaths(string[] paths) { if (System.IO.Path.DirectorySeparatorChar != '/') for (int i = 0; i < paths.Length; i++) paths[i] = ToSystemPath(paths[i]); return paths; } /// /// Converts a path from using / as the directory separator /// /// The path to convert /// The converted path public static string ToSystemPath(string path) { return System.IO.Path.DirectorySeparatorChar == '/' ? path : path.Replace('/', System.IO.Path.DirectorySeparatorChar); } /// /// Converts a path to use / as the directory separator /// /// The path to convert /// The converted path public static string ToArchivePath(string path) { return System.IO.Path.DirectorySeparatorChar == '/' ? path : path.Replace(System.IO.Path.DirectorySeparatorChar, '/'); } /// /// Returns all files in the archive, matching the prefix, if any. /// /// An optional prefix for limiting the files returned /// All files in the archive, matching the prefix, if any public string[] ListFiles(string prefix) { string[] files = m_compressor.ListFiles(ToArchivePath(prefix)); for (int i = 0; i < files.Length; i++) files[i] = ToSystemPath(files[i]); return files; } /// /// Reads a path from a file as a string /// /// The file to read from /// All the text found in the file public string ReadPathString(string file) { return ToSystemPath(ReadAllText(file)); } /// /// Returns all paths from a file /// /// The file to read the data from /// All paths in the given file public string[] ReadPathLines(string file) { return ToSystemPaths(ReadAllLines(file)); } /// /// Reads all text from a file as a string /// /// The file to read from /// All the text found in the file protected string ReadAllText(string file) { using (StreamReader s = new StreamReader(OpenRead(file), Encoding.UTF8, true)) return s.ReadToEnd(); } /// /// Returns all lines in the given file /// /// The file to read the data from /// All lines in the given file public string[] ReadAllLines(string file) { return ReadAllText(file).Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); } /// /// Returns a stream with data from the given file /// /// The file to read the data from /// A stream with data from the given file public System.IO.Stream OpenRead(string file) { return m_compressor.OpenRead(ToArchivePath(file)); } /// /// Writes the given path to a file /// /// The file to write to /// The path to write public void WritePathString(string file, string path) { WriteAllText(file, ToArchivePath(path)); } /// /// Writes the given paths to a file /// /// The file to write to /// The paths to write public void WritePathLines(string file, string[] paths) { WriteAllLines(file, ToArchivePaths(paths)); } /// /// Writes the given string to a file /// /// The file to write to /// The data to write private void WriteAllText(string file, string data) { using (StreamWriter sw = new StreamWriter(CreateFile(file, DateTime.Now), Encoding.UTF8)) sw.Write(data); } /// /// Writes the given lines to a file /// /// The file to write to /// The data to write public void WriteAllLines(string file, string[] data) { bool first = true; using (StreamWriter sw = new StreamWriter(CreateFile(file, DateTime.Now), Encoding.UTF8)) foreach (string s in data) { if (first) first = false; else sw.Write("\n"); sw.Write(s); } } /// /// Creates a file in the archive /// /// The file to create /// The time the file was last written /// A stream with the data to write into the file public System.IO.Stream CreateFile(string file, DateTime lastWrite) { return m_compressor.CreateFile(ToArchivePath(file), m_useUtcTimes ? lastWrite.ToUniversalTime() : lastWrite); } /// /// Returns a value indicating if the specified file exists /// /// The name of the file to examine /// True if the file exists, false otherwise public bool FileExists(string file) { return m_compressor.FileExists(ToArchivePath(file)); } /// /// The total size of the archive /// public long Size { get { return m_compressor.Size; } } /// /// Returns the last modification time for the file /// /// The name of the file to examine /// The timestamp on the file public DateTime GetLastWriteTime(string file) { return m_compressor.GetLastWriteTime(ToArchivePath(file)); } /// /// The size in bytes of the buffer that will be written when flushed /// public long FlushBufferSize { get { return m_compressor.FlushBufferSize; } } /// /// Disposes all resources held by this instance /// public void Dispose() { if (m_compressor != null) m_compressor.Dispose(); m_compressor = null; } /// /// Instanciates a specific compression module, given the file extension and options /// /// The file extension to create the instance for /// The filename of the file used to compress/decompress contents /// The options to pass to the instance constructor /// The instanciated compression module or null if the file extension is not supported public static CompressionWrapper GetModule(string fileextension, string filename, Dictionary options) { return new CompressionWrapper(DynamicLoader.CompressionLoader.GetModule(fileextension, filename, options)); } } }