2015-11-16 11:21:57 -08:00
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
2015-10-18 19:02:09 -07:00
using System ;
2016-12-14 13:53:11 -10:00
using System.Collections.Generic ;
2015-10-18 19:02:09 -07:00
using System.IO ;
2016-12-14 13:53:11 -10:00
using System.Linq ;
using Microsoft.DotNet.Cli.Utils ;
2016-08-05 16:54:41 -07:00
using Microsoft.DotNet.PlatformAbstractions ;
2015-10-18 19:02:09 -07:00
namespace Microsoft.DotNet.Tools.Common
{
2016-01-05 23:48:50 -08:00
public static class PathUtility
2015-10-18 19:02:09 -07:00
{
public static bool IsPlaceholderFile ( string path )
{
return string . Equals ( Path . GetFileName ( path ) , "_._" , StringComparison . Ordinal ) ;
}
public static bool IsChildOfDirectory ( string dir , string candidate )
{
if ( dir = = null )
{
throw new ArgumentNullException ( nameof ( dir ) ) ;
}
if ( candidate = = null )
{
throw new ArgumentNullException ( nameof ( candidate ) ) ;
}
dir = Path . GetFullPath ( dir ) ;
dir = EnsureTrailingSlash ( dir ) ;
candidate = Path . GetFullPath ( candidate ) ;
return candidate . StartsWith ( dir , StringComparison . OrdinalIgnoreCase ) ;
}
public static string EnsureTrailingSlash ( string path )
{
return EnsureTrailingCharacter ( path , Path . DirectorySeparatorChar ) ;
}
public static string EnsureTrailingForwardSlash ( string path )
{
return EnsureTrailingCharacter ( path , '/' ) ;
}
private static string EnsureTrailingCharacter ( string path , char trailingCharacter )
{
if ( path = = null )
{
throw new ArgumentNullException ( nameof ( path ) ) ;
}
// if the path is empty, we want to return the original string instead of a single trailing character.
if ( path . Length = = 0 | | path [ path . Length - 1 ] = = trailingCharacter )
{
return path ;
}
return path + trailingCharacter ;
}
2016-08-01 19:06:46 -05:00
public static string EnsureNoTrailingDirectorySeparator ( string path )
2016-07-26 22:27:25 -05:00
{
if ( ! string . IsNullOrEmpty ( path ) )
{
char lastChar = path [ path . Length - 1 ] ;
2016-08-01 19:06:46 -05:00
if ( lastChar = = Path . DirectorySeparatorChar )
2016-07-26 22:27:25 -05:00
{
path = path . Substring ( 0 , path . Length - 1 ) ;
}
}
return path ;
}
2016-12-20 16:03:00 -08:00
public static void EnsureParentDirectoryExists ( string filePath )
2015-10-18 19:02:09 -07:00
{
string directory = Path . GetDirectoryName ( filePath ) ;
2016-04-22 12:17:36 -07:00
2016-12-20 16:03:00 -08:00
EnsureDirectoryExists ( directory ) ;
2016-04-22 12:17:36 -07:00
}
2017-03-02 20:35:20 -08:00
2016-12-20 16:03:00 -08:00
public static void EnsureDirectoryExists ( string directoryPath )
2016-04-22 12:17:36 -07:00
{
if ( ! Directory . Exists ( directoryPath ) )
2015-10-18 19:02:09 -07:00
{
2016-04-22 12:17:36 -07:00
Directory . CreateDirectory ( directoryPath ) ;
2015-10-18 19:02:09 -07:00
}
}
2017-01-12 15:42:36 -08:00
public static bool TryDeleteDirectory ( string directoryPath )
{
try
{
Directory . Delete ( directoryPath , true ) ;
return true ;
}
catch
{
return false ;
}
}
2017-01-06 01:40:26 -08:00
/// <summary>
/// Returns childItem relative to directory, with Path.DirectorySeparatorChar as separator
/// </summary>
public static string GetRelativePath ( DirectoryInfo directory , FileSystemInfo childItem )
{
var path1 = EnsureTrailingSlash ( directory . FullName ) ;
var path2 = childItem . FullName ;
return GetRelativePath ( path1 , path2 , Path . DirectorySeparatorChar , true ) ;
}
2015-10-18 19:02:09 -07:00
/// <summary>
/// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator
/// </summary>
public static string GetRelativePath ( string path1 , string path2 )
{
2016-01-28 16:16:50 -08:00
return GetRelativePath ( path1 , path2 , Path . DirectorySeparatorChar , true ) ;
}
/// <summary>
2017-03-02 20:35:20 -08:00
/// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator but ignoring directory
2016-01-28 16:16:50 -08:00
/// traversals.
/// </summary>
public static string GetRelativePathIgnoringDirectoryTraversals ( string path1 , string path2 )
{
return GetRelativePath ( path1 , path2 , Path . DirectorySeparatorChar , false ) ;
2015-10-18 19:02:09 -07:00
}
/// <summary>
/// Returns path2 relative to path1, with given path separator
/// </summary>
2016-01-28 16:16:50 -08:00
public static string GetRelativePath ( string path1 , string path2 , char separator , bool includeDirectoryTraversals )
2015-10-18 19:02:09 -07:00
{
if ( string . IsNullOrEmpty ( path1 ) )
{
throw new ArgumentException ( "Path must have a value" , nameof ( path1 ) ) ;
}
if ( string . IsNullOrEmpty ( path2 ) )
{
throw new ArgumentException ( "Path must have a value" , nameof ( path2 ) ) ;
}
StringComparison compare ;
2016-04-28 16:30:32 -07:00
if ( RuntimeEnvironment . OperatingSystemPlatform = = Platform . Windows )
2015-10-18 19:02:09 -07:00
{
compare = StringComparison . OrdinalIgnoreCase ;
// check if paths are on the same volume
if ( ! string . Equals ( Path . GetPathRoot ( path1 ) , Path . GetPathRoot ( path2 ) ) )
{
// on different volumes, "relative" path is just path2
return path2 ;
}
}
else
{
compare = StringComparison . Ordinal ;
}
var index = 0 ;
var path1Segments = path1 . Split ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ;
var path2Segments = path2 . Split ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ;
// if path1 does not end with / it is assumed the end is not a directory
// we will assume that is isn't a directory by ignoring the last split
var len1 = path1Segments . Length - 1 ;
var len2 = path2Segments . Length ;
// find largest common absolute path between both paths
var min = Math . Min ( len1 , len2 ) ;
while ( min > index )
{
if ( ! string . Equals ( path1Segments [ index ] , path2Segments [ index ] , compare ) )
{
break ;
}
// Handle scenarios where folder and file have same name (only if os supports same name for file and directory)
// e.g. /file/name /file/name/app
else if ( ( len1 = = index & & len2 > index + 1 ) | | ( len1 > index & & len2 = = index + 1 ) )
{
break ;
}
+ + index ;
}
var path = "" ;
// check if path2 ends with a non-directory separator and if path1 has the same non-directory at the end
if ( len1 + 1 = = len2 & & ! string . IsNullOrEmpty ( path1Segments [ index ] ) & &
string . Equals ( path1Segments [ index ] , path2Segments [ index ] , compare ) )
{
return path ;
}
2016-01-28 16:16:50 -08:00
if ( includeDirectoryTraversals )
2015-10-18 19:02:09 -07:00
{
2016-01-28 16:16:50 -08:00
for ( var i = index ; len1 > i ; + + i )
{
path + = ".." + separator ;
}
2015-10-18 19:02:09 -07:00
}
2016-02-10 16:13:30 -08:00
2015-10-18 19:02:09 -07:00
for ( var i = index ; len2 - 1 > i ; + + i )
{
path + = path2Segments [ i ] + separator ;
}
// if path2 doesn't end with an empty string it means it ended with a non-directory name, so we add it back
if ( ! string . IsNullOrEmpty ( path2Segments [ len2 - 1 ] ) )
{
path + = path2Segments [ len2 - 1 ] ;
}
return path ;
}
public static string GetAbsolutePath ( string basePath , string relativePath )
{
if ( basePath = = null )
{
throw new ArgumentNullException ( nameof ( basePath ) ) ;
}
if ( relativePath = = null )
{
throw new ArgumentNullException ( nameof ( relativePath ) ) ;
}
Uri resultUri = new Uri ( new Uri ( basePath ) , new Uri ( relativePath , UriKind . Relative ) ) ;
return resultUri . LocalPath ;
}
public static string GetDirectoryName ( string path )
{
path = path . TrimEnd ( Path . DirectorySeparatorChar ) ;
return path . Substring ( Path . GetDirectoryName ( path ) . Length ) . Trim ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ;
}
public static string GetPathWithForwardSlashes ( string path )
{
return path . Replace ( '\\' , '/' ) ;
}
public static string GetPathWithBackSlashes ( string path )
{
return path . Replace ( '/' , '\\' ) ;
}
public static string GetPathWithDirectorySeparator ( string path )
{
if ( Path . DirectorySeparatorChar = = '/' )
{
return GetPathWithForwardSlashes ( path ) ;
}
else
{
return GetPathWithBackSlashes ( path ) ;
}
}
2016-01-04 12:49:13 -08:00
2017-02-14 13:47:06 -10:00
public static string RemoveExtraPathSeparators ( string path )
{
if ( string . IsNullOrEmpty ( path ) )
{
return path ;
}
var components = path . Split ( Path . DirectorySeparatorChar ) ;
var result = string . Empty ;
foreach ( var component in components )
{
if ( ! string . IsNullOrEmpty ( component ) )
{
result = Path . Combine ( result , component ) ;
}
}
if ( path [ path . Length - 1 ] = = Path . DirectorySeparatorChar )
{
result + = Path . DirectorySeparatorChar ;
}
return result ;
}
2017-03-12 15:06:34 -07:00
public static bool HasExtension ( this string filePath , string extension )
2016-01-04 12:49:13 -08:00
{
var comparison = StringComparison . Ordinal ;
2016-04-28 16:30:32 -07:00
if ( RuntimeEnvironment . OperatingSystemPlatform = = Platform . Windows )
2016-01-04 12:49:13 -08:00
{
comparison = StringComparison . OrdinalIgnoreCase ;
}
return Path . GetExtension ( filePath ) . Equals ( extension , comparison ) ;
}
2016-05-02 12:21:12 -05:00
/// <summary>
/// Gets the fully-qualified path without failing if the
/// path is empty.
/// </summary>
public static string GetFullPath ( string path )
{
if ( string . IsNullOrWhiteSpace ( path ) )
{
return path ;
}
return Path . GetFullPath ( path ) ;
}
2016-12-14 13:53:11 -10:00
2017-03-10 16:43:44 -08:00
public static void EnsureAllPathsExist (
IReadOnlyCollection < string > paths ,
string pathDoesNotExistLocalizedFormatString )
2016-12-14 13:53:11 -10:00
{
var notExisting = new List < string > ( ) ;
2017-03-10 16:43:44 -08:00
2016-12-14 13:53:11 -10:00
foreach ( var p in paths )
{
if ( ! File . Exists ( p ) )
{
notExisting . Add ( p ) ;
}
}
if ( notExisting . Count > 0 )
{
throw new GracefulException (
string . Join (
Environment . NewLine ,
2017-03-10 16:43:44 -08:00
notExisting . Select ( p = > string . Format ( pathDoesNotExistLocalizedFormatString , p ) ) ) ) ;
2016-12-14 13:53:11 -10:00
}
}
2017-03-12 15:06:34 -07:00
public static bool IsDirectory ( this string path ) = >
File . GetAttributes ( path ) . HasFlag ( FileAttributes . Directory ) ;
2015-10-18 19:02:09 -07:00
}
2017-03-02 20:35:20 -08:00
}