#region License and Copyright /* ------------------------------------------------------------------------- * Dotnet Commons IO * * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the * * Free Software Foundation, Inc., * 59 Temple Place, * Suite 330, * Boston, * MA 02111-1307 * USA * * ------------------------------------------------------------------------- */ #endregion using System; using System.Collections; using System.Globalization; using System.IO; namespace Dotnet.Commons.IO { class MainClass { /// --------------------------------------------------------------- /// <summary> /// Combines two path strings. /// </summary> /// <param name="path1">The first path.</param> /// <param name="path2">The second path.</param> /// <returns> /// A string containing the combined paths. If one of the specified /// paths is a zero-length string, this method returns the other path. /// If <paramref name="path2" /> contains an absolute path, this method /// returns <paramref name="path2" />. /// </returns> /// <remarks> /// <para> /// On Unix, processing is delegated to <see cref="Path.Combine(string, string)" />. /// </para> /// <para> /// On Windows, this method normalized the paths to avoid running into /// the 260 character limit of a path and converts forward slashes in /// both <paramref name="path1" /> and <paramref name="path2" /> to /// the platform's directory separator character. /// </para> /// /// Courtesy of NAnt project. /// </remarks> /// --------------------------------------------------------------- public static string CombinePaths(string path1, string 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:whateveryouwant odo // path2 = ../../test // // -> // // path1 = d:whateveryouwant // 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; } } }