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));
}
}
}