1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-14 22:34:09 +02:00
Beef/BeefLibs/corlib/src/IO/Path.bf

601 lines
17 KiB
Beef
Raw Normal View History

2019-08-23 11:56:54 -07:00
// This file contains portions of code released by Microsoft under the MIT license as part
// of an open-sourcing initiative in 2014 of the C# core libraries.
// The original source was submitted to https://github.com/Microsoft/referencesource
using System.Text;
using System.Diagnostics;
namespace System.IO
{
public static class Path
{
#if BF_PLATFORM_WINDOWS
public const char8 DirectorySeparatorChar = '\\';
#else
public const char8 DirectorySeparatorChar = '/';
#endif //BF_PLATFORM_WINDOWS
// Platform specific alternate directory separator char8acter.
// This is backslash ('\') on Unix, and slash ('/') on Windows
// and MacOS.
//
#if BF_PLATFORM_WINDOWS
public const char8 AltDirectorySeparatorChar = '/';
#else
public const char8 AltDirectorySeparatorChar = '\\';
#endif //BF_PLATFORM_WINDOWS
// Platform specific volume separator char8acter. This is colon (':')
// on Windows and MacOS, and slash ('/') on Unix. This is mostly
// useful for parsing paths like "c:\windows" or "MacVolume:System Folder".
//
#if BF_PLATFORM_WINDOWS
public const char8 VolumeSeparatorChar = ':';
#else
public const char8 VolumeSeparatorChar = '/';
#endif //BF_PLATFORM_WINDOWS
// Make this public sometime.
// The max total path is 260, and the max individual component length is 255.
// For example, D:\<256 char8 file name> isn't legal, even though it's under 260 char8s.
protected const int32 MaxPath = 260;
2019-08-23 11:56:54 -07:00
private const int32 MaxDirectoryLength = 255;
public static void GetFullPath(String inPartialPath, String outFullPath)
{
Platform.GetStrHelper(outFullPath, scope (outPtr, outSize, outResult) =>
{
Platform.BfpFile_GetFullPath(inPartialPath, outPtr, outSize, (Platform.BfpFileResult*)outResult);
});
}
protected static void CheckInvalidPathChars(StringView path, bool checkAdditional = false)
2019-08-23 11:56:54 -07:00
{
}
2019-09-27 13:03:47 -07:00
public static void GetFileName(StringView inPath, String outFileName)
2019-08-23 11:56:54 -07:00
{
2019-09-27 13:03:47 -07:00
if (inPath.IsEmpty)
2019-08-23 11:56:54 -07:00
return;
CheckInvalidPathChars(inPath);
int length = inPath.Length;
for (int i = length; --i >= 0; )
{
char8 ch = inPath[i];
if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar)
{
outFileName.Append(inPath, i + 1, length - i - 1);
return;
}
}
outFileName.Append(inPath);
}
static String NormalizePath(String path, bool fullCheck)
2019-08-23 11:56:54 -07:00
{
return NormalizePath(path, fullCheck, MaxPath);
}
static String NormalizePath(String path, bool fullCheck, bool expandShortPaths)
2019-08-23 11:56:54 -07:00
{
return NormalizePath(path, fullCheck, MaxPath, expandShortPaths);
}
static String NormalizePath(String path, bool fullCheck, int32 maxPathLength)
2019-08-23 11:56:54 -07:00
{
return NormalizePath(path, fullCheck, maxPathLength, true);
}
static String NormalizePath(String path, bool fullCheck, int32 maxPathLength, bool expandShortPaths)
2019-08-23 11:56:54 -07:00
{
//TODO: Implement
return path;
}
static String RemoveLongPathPrefix(String path)
2019-08-23 11:56:54 -07:00
{
//TODO: Implement
return path;
}
public static Result<void> GetDirectoryPath(StringView path, String outDir)
{
String usePath = scope String(Math.Min(MaxPath, path.Length));
usePath.Append(path);
//String origPath = path;
//if (usePath != null)
{
CheckInvalidPathChars(usePath);
String normalizedPath = NormalizePath(usePath, false);
// If there are no permissions for PathDiscovery to this path, we should NOT expand the short paths
// as this would leak information about paths to which the user would not have access to.
if (usePath.Length > 0)
{
// If we were passed in a path with \\?\ we need to remove it as FileIOPermission does not like it.
String tempPath = Path.RemoveLongPathPrefix(usePath);
// FileIOPermission cannot handle paths that contain ? or *
// So we only pass to FileIOPermission the text up to them.
int32 pos = 0;
while (pos < tempPath.Length && (tempPath[pos] != '?' && tempPath[pos] != '*'))
pos++;
// GetFullPath will Demand that we have the PathDiscovery FileIOPermission and thus throw
// SecurityException if we don't.
// While we don't use the result of this call we are using it as a consistent way of
// doing the security checks.
if (pos > 0)
{
//Path.GetFullPath(tempPath.Substring(0, pos));
String newPath = scope String(usePath.Length - pos);
newPath.Append(usePath, pos);
usePath = newPath;
}
/*}
catch (SecurityException) {
// If the user did not have permissions to the path, make sure that we don't leak expanded short paths
// Only re-normalize if the original path had a ~ in it.
if (path.IndexOf("~", StringComparison.Ordinal) != -1)
{
normalizedPath = NormalizePath(path, /*fullCheck*/ false, /*expandShortPaths*/ false);
}
}
catch (PathTooLongException) { }
catch (NotSupportedException) { } // Security can throw this on "c:\foo:"
catch (IOException) { }
catch (ArgumentException) { } // The normalizePath with fullCheck will throw this for file: and http:*/
}
usePath = normalizedPath;
int root = GetRootLength(usePath);
int i = usePath.Length;
if (i > root)
{
i = usePath.Length;
if (i == root) return .Err;
while (i > root && usePath[--i] != DirectorySeparatorChar && usePath[i] != AltDirectorySeparatorChar) {}
outDir.Append(usePath, 0, i);
return .Ok;
}
}
return .Err;
}
// Gets the length of the root DirectoryInfo or whatever DirectoryInfo markers
// are specified for the first part of the DirectoryInfo name.
//
static int GetRootLength(String path)
2019-08-23 11:56:54 -07:00
{
CheckInvalidPathChars(path);
int i = 0;
int length = path.Length;
#if BF_PLATFORM_WINDOWS
if (length >= 1 && (IsDirectorySeparator(path[0])))
{
// handles UNC names and directories off current drive's root.
i = 1;
if (length >= 2 && (IsDirectorySeparator(path[1])))
{
i = 2;
int32 n = 2;
while (i < length && ((path[i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar) || --n > 0)) i++;
}
}
else if (length >= 2 && path[1] == VolumeSeparatorChar)
{
// handles A:\foo.
i = 2;
if (length >= 3 && (IsDirectorySeparator(path[2]))) i++;
}
return i;
#else
if (length >= 1 && (IsDirectorySeparator(path[0]))) {
i = 1;
}
return i;
#endif //BF_PLATFORM_WINDOWS
}
static bool IsDirectorySeparator(char8 c)
2019-08-23 11:56:54 -07:00
{
return (c==DirectorySeparatorChar || c == AltDirectorySeparatorChar);
}
/*
public static char8[] GetInvalidPathChars()
{
return RealInvalidPathChars;
}
public static char8[] GetInvalidFileNameChars()
{
return (char8[]) InvalidFileNameChars.Clone();
} */
public static void GetFileNameWithoutExtension(String inPath, String outFileName)
{
int lastSlash = Math.Max(inPath.LastIndexOf('\\'), inPath.LastIndexOf('/'));
int i;
if ((i = inPath.LastIndexOf('.')) != -1)
{
int len = i - lastSlash - 1;
if (len > 0)
outFileName.Append(inPath, lastSlash + 1, i - lastSlash - 1);
}
else
outFileName.Append(inPath, lastSlash + 1);
}
2019-09-27 13:03:47 -07:00
public static Result<void> GetExtension(StringView inPath, String outExt)
2019-08-23 11:56:54 -07:00
{
int i;
if ((i = inPath.LastIndexOf('.')) != -1)
outExt.Append(inPath, i);
return .Ok;
}
public static Result<void> GetTempPath(String outPath)
{
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
{
Platform.BfpFile_GetTempPath(outPtr, outSize, (Platform.BfpFileResult*)outResult);
});
return .Ok;
}
public static Result<void> GetTempFileName(String outPath)
{
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
{
Platform.BfpFile_GetTempFileName(outPtr, outSize, (Platform.BfpFileResult*)outResult);
});
return .Ok;
}
public static void InternalCombine(String target, params String[] components)
{
for (var component in components)
{
if ((target.Length > 0) && (!target.EndsWith("\\")) && (!target.EndsWith("/")))
target.Append(Path.DirectorySeparatorChar);
target.Append(component);
}
}
public static void GetActualPathName(StringView inPath, String outPath)
{
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
{
Platform.BfpFile_GetActualPath(scope String()..Append(inPath), outPtr, outSize, (Platform.BfpFileResult*)outResult);
});
}
public static bool Equals(StringView filePathA, StringView filePathB)
{
Debug.Assert(!filePathA.Contains(Path.AltDirectorySeparatorChar));
Debug.Assert(!filePathB.Contains(Path.AltDirectorySeparatorChar));
return filePathA.Equals(filePathB, !Environment.IsFileSystemCaseSensitive);
}
static void GetDriveStringTo(String outDrive, String path)
{
if ((path.Length >= 2) && (path[1] == ':'))
outDrive.Append(path, 0, 2);
}
/// Tests if the given path contains a root. A path is considered rooted
/// if it starts with a backslash ("\") or a drive letter and a colon (":").
public static bool IsPathRooted(StringView path)
{
CheckInvalidPathChars(path);
int length = path.Length;
if ((length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) || (length >= 2 && path[1] == VolumeSeparatorChar))
return true;
return false;
}
2019-08-23 11:56:54 -07:00
public static void GetRelativePath(StringView fullPath, StringView curDir, String outRelPath)
{
String curPath1 = scope String(curDir);
String curPath2 = scope String(fullPath);
if (curPath1.Contains(Path.AltDirectorySeparatorChar))
curPath1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
if (curPath2.Contains(Path.AltDirectorySeparatorChar))
curPath1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
String driveString1 = scope String();
GetDriveStringTo(driveString1, curPath1);
String driveString2 = scope String();
GetDriveStringTo(driveString2, curPath2);
StringComparison compareType = Environment.IsFileSystemCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
// On seperate drives?
if (!String.Equals(driveString1, driveString2, compareType))
{
outRelPath.Set(fullPath);
return;
}
if (driveString1.Length > 0)
curPath1.Remove(0, Math.Min(driveString1.Length + 1, curPath1.Length));
if (driveString2.Length > 0)
curPath2.Remove(0, Math.Min(driveString2.Length + 1, curPath2.Length));
while ((curPath1.Length > 0) && (curPath2.Length > 0))
{
int slashPos1 = curPath1.IndexOf(Path.DirectorySeparatorChar);
if (slashPos1 == -1)
slashPos1 = curPath1.Length;
int slashPos2 = curPath2.IndexOf(Path.DirectorySeparatorChar);
if (slashPos2 == -1)
slashPos2 = curPath2.Length;
String section1 = scope String();
section1.Append(curPath1, 0, slashPos1);
String section2 = scope String();
section2.Append(curPath2, 0, slashPos2);
if (!String.Equals(section1, section2, compareType))
{
// a/b/c
// d/e/f
while (curPath1.Length > 0)
{
slashPos1 = curPath1.IndexOf(Path.DirectorySeparatorChar);
if (slashPos1 == -1)
slashPos1 = curPath1.Length;
if (slashPos1 + 1 >= curPath1.Length)
curPath1.Clear();
else
curPath1.Remove(0, slashPos1 + 1);
if (Path.DirectorySeparatorChar == '\\')
curPath2.Insert(0, "..\\");
else
curPath2.Insert(0, "../");
}
}
else
{
if (slashPos1 + 1 >= curPath1.Length)
curPath1.Clear();
else
curPath1.Remove(0, slashPos1 + 1);
if (slashPos2 + 2 >= curPath2.Length)
curPath1 = "";
else
curPath2.Remove(0, slashPos2 + 1);
}
}
outRelPath.Set(curPath2);
}
public static void GetAbsolutePath(StringView relPath, StringView relToAbsPath, String outAbsPath)
{
String driveString = null;
var relPath;
if (relPath == outAbsPath)
relPath = scope:: String(relPath);
if ((relPath.Length >= 2) && (relPath[1] == ':'))
{
outAbsPath.Append(relPath);
return;
}
if ((relPath.Length > 1) &&
(relPath[0] == '/') || (relPath[0] == '\\'))
{
outAbsPath.Append(relPath);
return;
}
int startLen = outAbsPath.Length;
outAbsPath.Append(relToAbsPath);
//outAbsPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
char8 slashChar = Path.DirectorySeparatorChar;
if ((outAbsPath.Length >= 2) && (outAbsPath[1] == ':'))
{
driveString = scope:: String();
driveString.Append(outAbsPath, 0, 2);
outAbsPath.Remove(0, 2);
}
// Append a trailing slash if necessary
if ((outAbsPath.Length > 0) && (outAbsPath[outAbsPath.Length - 1] != '\\') && (outAbsPath[outAbsPath.Length - 1] != '/'))
outAbsPath.Append(slashChar);
int32 relIdx = 0;
for (; ;)
{
if (outAbsPath.Length == 0)
break;
int32 firstSlash = -1;
for (int32 i = relIdx; i < relPath.Length; i++)
if ((relPath[i] == '\\') || (relPath[i] == '/'))
{
firstSlash = i;
break;
}
if (firstSlash == -1)
break;
String chDir = scope String();
chDir.Append(relPath, relIdx, firstSlash - relIdx);
//relPath.Substring(relIdx, firstSlash - relIdx, chDir);
//chDir.Append(relPath.Ptr + relIdx, firstSlash - relIdx);
relIdx = firstSlash + 1;
if (chDir == "..")
{
int32 lastDirStart = (int32)outAbsPath.Length - 1;
while ((lastDirStart > 0) && (outAbsPath[lastDirStart - 1] != '\\') && (outAbsPath[lastDirStart - 1] != '/'))
lastDirStart--;
String lastDir = scope String();
lastDir.Append(outAbsPath, lastDirStart, outAbsPath.Length - lastDirStart - 1);
if (lastDir == "..")
{
outAbsPath.Append("..");
outAbsPath.Append(Path.DirectorySeparatorChar);
}
else
{
//newPath.erase(newPath.begin() + lastDirStart, newPath.end());
outAbsPath.Remove(lastDirStart, outAbsPath.Length - lastDirStart);
}
}
else if (chDir == "")
{
outAbsPath.Append(Path.DirectorySeparatorChar);
break;
}
else if (chDir != ".")
{
//newPath += chDir + slashChar;
outAbsPath.Append(chDir);
outAbsPath.Append(Path.DirectorySeparatorChar);
break;
}
}
if (driveString != null)
outAbsPath.Insert(0, driveString);
//relPath.Substring(relIdx, outAbsPath);
outAbsPath.Append(relPath, relIdx);
//outAbsPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
for (int i = startLen; i < outAbsPath.Length; i++)
{
if (outAbsPath[i] == Path.AltDirectorySeparatorChar)
outAbsPath[i] = Path.DirectorySeparatorChar;
}
}
public static bool WildcareCompare(StringView path, StringView wildcard)
{
bool matches = true;
char8* afterLastWild = null; // The location after the last '*', if weve encountered one
char8* afterLastPath = null; // The location in the path string, from which we started after last wildcard
char8 t, w;
char8* pathPtr = path.Ptr;
char8* pathEnd = path.EndPtr;
char8* wildPtr = wildcard.Ptr;
char8* wildEnd = wildcard.EndPtr;
// Walk the text strings one character at a time.
while (true)
{
// How do you match a unique text string?
if (pathPtr == pathEnd)
{
// Easy: unique up on it!
if (wildPtr == wildEnd)
{
break; // "x" matches "x"
}
w = *wildPtr;
if (w == '*')
{
wildPtr++;
continue;// "x*" matches "x" or "xy"
}
else if (afterLastPath != null)
{
if (afterLastPath == pathEnd)
{
matches = false;
break;
}
pathPtr = afterLastPath++;
wildPtr = afterLastWild;
continue;
}
matches = false;
break; // "x" doesn't match "xy"
}
else
{
t = *pathPtr;
w = *wildPtr;
if (!Environment.IsFileSystemCaseSensitive)
{
t = t.ToUpper;
w = w.ToUpper;
}
// How do you match a tame text string?
if (t != w)
{
// The tame way: unique up on it!
if (w == '*')
{
afterLastWild = ++wildPtr;
afterLastPath = pathPtr;
if (wildPtr == wildEnd)
{
break; // "*" matches "x"
}
w = *wildPtr;
continue; // "*y" matches "xy"
}
else if (afterLastWild != null)
{
if (afterLastWild != wildPtr)
{
wildPtr = afterLastWild;
w = *wildPtr;
if (!Environment.IsFileSystemCaseSensitive)
w = w.ToUpper;
if (t == w)
{
wildPtr++;
}
}
pathPtr++;
continue; // "*sip*" matches "mississippi"
}
else
{
matches = false;
break; // "x" doesn't match "y"
}
}
}
pathPtr++;
wildPtr++;
}
return matches;
}
}
}