// NAnt - A .NET build tool
// Copyright (C) 2001 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
using NAnt.Core.Filters;
using NAnt.Core.Types;
namespace NAnt.Core.Util {
///
/// Provides modified version for Copy and Move from the File class that
/// allow for filter chain processing.
///
public sealed class FileUtils {
private FileUtils() {
}
#region Public Static Methods
///
/// Copies a file filtering its content through the filter chain.
///
/// The file to copy
/// The file to copy to
/// Chain of filters to apply when copying, or is no filters should be applied.
/// The encoding used to read the soure file.
/// The encoding used to write the destination file.
public static void CopyFile(string sourceFileName, string destFileName, FilterChain filterChain, Encoding inputEncoding, Encoding outputEncoding) {
// determine if filters are available
bool filtersAvailable = filterChain != null && filterChain.Filters.Count > 0;
// if no filters have been defined, and no input or output encoding
// is set, we can just use the File.Copy method
if (!filtersAvailable && inputEncoding == null && outputEncoding == null) {
File.Copy(sourceFileName, destFileName, true);
} else {
// determine actual input encoding to use. if no explicit input
// encoding is specified, we'll use the system's current ANSI
// code page
Encoding actualInputEncoding = (inputEncoding != null) ?
inputEncoding : Encoding.Default;
// get base filter built on the file's reader. Use a 8k buffer.
using (StreamReader sourceFileReader = new StreamReader(sourceFileName, actualInputEncoding, true, 8192)) {
Encoding actualOutputEncoding = outputEncoding;
if (actualOutputEncoding == null) {
// if no explicit output encoding is specified, we'll
// just use the encoding of the input file as determined
// by the runtime
//
// Note : the input encoding as specified on the filterchain
// might not match the current encoding of the streamreader
//
// eg. when specifing an ANSI encoding, the runtime might
// still detect the file is using UTF-8 encoding, because
// we use BOM detection
actualOutputEncoding = sourceFileReader.CurrentEncoding;
}
// writer for destination file
using (StreamWriter destFileWriter = new StreamWriter(destFileName, false, actualOutputEncoding, 8192)) {
if (filtersAvailable) {
Filter baseFilter = filterChain.GetBaseFilter(new PhysicalTextReader(sourceFileReader));
bool atEnd = false;
int character;
while (!atEnd) {
character = baseFilter.Read();
if (character > -1) {
destFileWriter.Write((char)character);
} else {
atEnd = true;
}
}
} else {
char[] buffer = new char[8192];
while (true) {
int charsRead = sourceFileReader.Read(buffer, 0, buffer.Length);
if (charsRead == 0) {
break;
}
destFileWriter.Write(buffer, 0, charsRead);
}
}
}
}
}
}
///
/// Moves a file filtering its content through the filter chain.
///
/// The file to move
/// The file to move move to
/// Chain of filters to apply when moving, or is no filters should be applied.
/// The encoding used to read the soure file.
/// The encoding used to write the destination file.
public static void MoveFile(string sourceFileName, string destFileName, FilterChain filterChain, Encoding inputEncoding, Encoding outputEncoding) {
// if no filters have been defined, and no input or output encoding
// is set, we can just use the File.Move method
if ((filterChain == null || filterChain.Filters.Count == 0) && inputEncoding == null && outputEncoding == null) {
File.Move(sourceFileName, destFileName);
} else {
CopyFile(sourceFileName, destFileName, filterChain, inputEncoding, outputEncoding);
File.Delete(sourceFileName);
}
}
///
/// Returns a uniquely named empty temporary directory on disk.
///
///
/// A representing the temporary directory.
///
public static DirectoryInfo GetTempDirectory() {
// create a uniquely named zero-byte file
string tempFile = Path.GetTempFileName();
// remove the temporary file
File.Delete(tempFile);
// create a directory named after the unique temporary file
Directory.CreateDirectory(tempFile);
// return the
return new DirectoryInfo(tempFile);
}
///
/// Combines two path strings.
///
/// The first path.
/// The second path.
///
/// A string containing the combined paths. If one of the specified
/// paths is a zero-length string, this method returns the other path.
/// If contains an absolute path, this method
/// returns .
///
///
///
/// On *nix, processing is delegated to .
///
///
/// On Windows, this method normalized the paths to avoid running into
/// the 260 character limit of a path and converts forward slashes in
/// both and to
/// the platform's directory separator character.
///
///
public static string CombinePaths(string path1, string path2) {
if (PlatformHelper.IsUnix) {
return Path.Combine(path1, path2);
}
if (path1 == null) {
throw new ArgumentNullException("path1");
}
if (path2 == null) {
throw new ArgumentNullException("path2");
}
if (Path.IsPathRooted(path2)) {
return path2;
}
char separatorChar = Path.DirectorySeparatorChar;
char[] splitChars = new char[] {'/', separatorChar};
// Now we split the Path by the Path Separator
String[] path2Parts = path2.Split(splitChars);
ArrayList arList = new ArrayList();
// for each Item in the path that differs from ".." we just add it
// to the ArrayList, but skip empty parts
for (int iCount = 0; iCount < path2Parts.Length; iCount++) {
string currentPart = path2Parts[iCount];
// skip empty parts or single dot parts
if (currentPart.Length == 0 || currentPart == ".") {
continue;
}
// if we get a ".." Try to remove the last item added (as if
// going up in the Directory Structure)
if (currentPart == "..") {
if (arList.Count > 0 && ((string) arList[arList.Count - 1] != "..")) {
arList.RemoveAt(arList.Count -1);
} else {
arList.Add(currentPart);
}
} else {
arList.Add(currentPart);
}
}
bool trailingSeparator = (path1.Length > 0 && path1.IndexOfAny(splitChars, path1.Length - 1) != -1);
// if the first path ends in directory seperator character, then
// we need to omit that trailing seperator when we split the path
string[] path1Parts;
if (trailingSeparator) {
path1Parts = path1.Substring(0, path1.Length - 1).Split(splitChars);
} else {
path1Parts = path1.Split(splitChars);
}
int counter = path1Parts.Length;
// if the second path starts with parts to move up the directory tree,
// then remove corresponding parts in the first path
//
// eg. path1 = d:\whatever\you\want\to\do
// path2 = ../../test
//
// ->
//
// path1 = d:\whatever\you\want
// path2 = test
ArrayList arList2 = (ArrayList) arList.Clone();
for (int i = 0; i < arList2.Count; i++) {
// never discard first part of path1
if ((string) arList2[i] != ".." || counter < 2) {
break;
}
// skip part of current directory
counter--;
arList.RemoveAt(0);
}
string separatorString = separatorChar.ToString(CultureInfo.InvariantCulture);
// if path1 only has one remaining part, and the original path had
// a trailing separator character or the remaining path had multiple
// parts (which were discarded by a relative path in path2), then
// add separator to remaining part
if (counter == 1 && (trailingSeparator || path1Parts.Length > 1)) {
path1Parts[0] += separatorString;
}
string combinedPath = Path.Combine(string.Join(separatorString, path1Parts,
0, counter), string.Join(separatorString, (String[]) arList.ToArray(typeof(String))));
// if path2 ends in directory separator character, then make sure
// combined path has trailing directory separator character
if (path2.EndsWith("/") || path2.EndsWith(separatorString)) {
combinedPath += Path.DirectorySeparatorChar;
}
return combinedPath;
}
///
/// Returns Absolute Path (Fix for 260 Char Limit of Path.GetFullPath(...))
///
/// The file or directory for which to obtain absolute path information.
/// Path Resolved
/// path is a zero-length string, contains only white space or contains one or more invalid characters as defined by .
/// is .
public static string GetFullPath(string path) {
if (path == null) {
throw new ArgumentNullException("path");
}
if (PlatformHelper.IsUnix || Path.IsPathRooted(path)) {
return Path.GetFullPath(path);
}
if (path.Length == 0 || path.Trim().Length == 0 || path.IndexOfAny(Path.InvalidPathChars) != -1) {
throw new ArgumentException("The path is not of a legal form.");
}
string combinedPath = FileUtils.CombinePaths(
Directory.GetCurrentDirectory(), path);
return Path.GetFullPath(combinedPath);
}
///
/// Returns the home directory of the current user.
///
///
/// The home directory of the current user.
///
public static string GetHomeDirectory() {
if (PlatformHelper.IsUnix) {
return Environment.GetEnvironmentVariable("HOME");
} else {
return Environment.GetEnvironmentVariable("USERPROFILE");
}
}
#endregion Public Static Methods
}
}