// 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 } }