using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UnrealBuildTool { /// /// Base class for file system objects (files or directories). /// public abstract class FullFileSystemName { /// /// The path to this object. Stored as an absolute path, with O/S preferred separator characters, and no trailing slash for directories. /// public readonly string FullName; /// /// The canonical full name for this object. /// public readonly string CanonicalName; /// /// Constructs a filesystem object for the given path. /// public FullFileSystemName(string InPath) { FullName = Path.GetFullPath(InPath); CanonicalName = FullName.ToLowerInvariant(); } /// /// Direct constructor for a path /// protected FullFileSystemName(string InFullName, string InCanonicalName) { FullName = InFullName; CanonicalName = InCanonicalName; } /// /// Determines if the given object is at or under the given directory /// /// /// public bool IsUnderDirectory(FullDirectoryName Other) { return CanonicalName.StartsWith(Other.CanonicalName) && (CanonicalName.Length == Other.CanonicalName.Length || CanonicalName[Other.CanonicalName.Length] == Path.DirectorySeparatorChar); } /// /// Creates a relative path from the given base directory /// /// The directory to create a relative path from /// A relative path from the given directory public string MakeRelativeTo(FullDirectoryName Directory) { // Find how much of the path is common between the two paths. This length does not include a trailing directory separator character. int CommonDirectoryLength = -1; for (int Idx = 0;;Idx++) { if(Idx == CanonicalName.Length) { // The two paths are identical. Just return the "." character. if(Idx == Directory.CanonicalName.Length) { return "."; } // Check if we're finishing on a complete directory name if(Directory.CanonicalName[Idx] == Path.DirectorySeparatorChar) { CommonDirectoryLength = Idx; } break; } else if(Idx == Directory.CanonicalName.Length) { // Check whether the end of the directory name coincides with a boundary for the current name. if(CanonicalName[Idx] == Path.DirectorySeparatorChar) { CommonDirectoryLength = Idx; } break; } else { // Check the two paths match, and bail if they don't. Increase the common directory length if we've reached a separator. if (CanonicalName[Idx] != Directory.CanonicalName[Idx]) { break; } if (CanonicalName[Idx] == Path.DirectorySeparatorChar) { CommonDirectoryLength = Idx; } } } // If there's no relative path, just return the absolute path if(CommonDirectoryLength == -1) { return FullName; } // Append all the '..' separators to get back to the common directory, then the rest of the string to reach the target item StringBuilder Result = new StringBuilder(); for(int Idx = CommonDirectoryLength + 1; Idx < Directory.CanonicalName.Length; Idx++) { // Move up a directory Result.Append(".."); Result.Append(Path.DirectorySeparatorChar); // Scan to the next directory separator while(Idx < Directory.CanonicalName.Length && Directory.CanonicalName[Idx] != Path.DirectorySeparatorChar) { Idx++; } } if(CommonDirectoryLength + 1 < FullName.Length) { Result.Append(FullName, CommonDirectoryLength + 1, FullName.Length - CommonDirectoryLength - 1); } return Result.ToString(); } /// /// Returns a string representation of this filesystem object /// /// Full path to the object public override string ToString() { return FullName; } } /// /// Representation of a directory in the filesystem /// public class FullDirectoryName : FullFileSystemName { /// /// Default constructor. /// /// Path to this directory. public FullDirectoryName(string InPath) : base(InPath) { } /// /// Constructor for creating a directory object directly from two strings. /// /// /// protected FullDirectoryName(string InFullName, string InCanonicalName) : base(InFullName, InCanonicalName) { } /// /// Gets the directory containing this object /// /// A new directory object representing the directory containing this object public FullDirectoryName GetParentDirectory() { if (IsRootDirectory()) { return null; } int ParentLength = CanonicalName.LastIndexOf(Path.DirectorySeparatorChar); if (ParentLength == 2 && CanonicalName[1] == ':') { ParentLength++; } return new FullDirectoryName(FullName.Substring(0, ParentLength), CanonicalName.Substring(0, ParentLength)); } /// /// Gets the parent directory for a file /// /// The file to get directory for /// The full directory name containing the given file public static FullDirectoryName GetParentDirectory(FullFileName File) { int ParentLength = File.CanonicalName.LastIndexOf(Path.DirectorySeparatorChar); return new FullDirectoryName(File.FullName.Substring(0, ParentLength), File.CanonicalName.Substring(0, ParentLength)); } /// /// Determines whether this path represents a root directory in the filesystem /// /// True if this path is a root directory, false otherwise public bool IsRootDirectory() { return CanonicalName[CanonicalName.Length - 1] == Path.DirectorySeparatorChar; } /// /// Compares two filesystem object names for equality. Uses the canonical name representation, not the display name representation. /// /// First object to compare. /// Second object to compare. /// True if the names represent the same object, false otherwise public static bool operator ==(FullDirectoryName A, FullDirectoryName B) { return A.CanonicalName == B.CanonicalName; } /// /// Compares two filesystem object names for inequality. Uses the canonical name representation, not the display name representation. /// /// First object to compare. /// Second object to compare. /// False if the names represent the same object, true otherwise public static bool operator !=(FullDirectoryName A, FullDirectoryName B) { return A.CanonicalName != B.CanonicalName; } /// /// Compares against another object for equality. /// /// First name to compare. /// Second name to compare. /// True if the names represent the same object, false otherwise public override bool Equals(object Obj) { return (Obj is FullDirectoryName) && ((FullDirectoryName)Obj) == this; } /// /// Returns a hash code for this object /// /// public override int GetHashCode() { return CanonicalName.GetHashCode(); } } /// /// Representation of a file in the filesystem /// public class FullFileName : FullFileSystemName { /// /// Default constructor. /// /// Path to this file public FullFileName(string InPath) : base(InPath) { } /// /// Gets the directory containing this file /// /// A new directory object representing the directory containing this object public FullDirectoryName GetDirectory() { return FullDirectoryName.GetParentDirectory(this); } /// /// Compares two filesystem object names for equality. Uses the canonical name representation, not the display name representation. /// /// First object to compare. /// Second object to compare. /// True if the names represent the same object, false otherwise public static bool operator ==(FullFileName A, FullFileName B) { return A.CanonicalName == B.CanonicalName; } /// /// Compares two filesystem object names for inequality. Uses the canonical name representation, not the display name representation. /// /// First object to compare. /// Second object to compare. /// False if the names represent the same object, true otherwise public static bool operator !=(FullFileName A, FullFileName B) { return A.CanonicalName != B.CanonicalName; } /// /// Compares against another object for equality. /// /// First name to compare. /// Second name to compare. /// True if the names represent the same object, false otherwise public override bool Equals(object Obj) { return (Obj is FullFileName) && ((FullFileName)Obj) == this; } /// /// Returns a hash code for this object /// /// public override int GetHashCode() { return CanonicalName.GetHashCode(); } } }