mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Implement Nintendo's path normalization functions
This commit is contained in:
parent
ac3c496018
commit
bc11d7ceaf
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace LibHac.Common
|
||||
@ -27,6 +29,26 @@ namespace LibHac.Common
|
||||
_buffer = Encoding.UTF8.GetBytes(value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public byte GetOrNull(int i)
|
||||
{
|
||||
byte value = 0;
|
||||
ReadOnlySpan<byte> b = _buffer;
|
||||
|
||||
if ((uint)i < (uint)b.Length)
|
||||
{
|
||||
value = b[i];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public byte GetUnsafe(int i)
|
||||
{
|
||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||
}
|
||||
|
||||
public U8Span Slice(int start)
|
||||
{
|
||||
return new U8Span(_buffer.Slice(start));
|
||||
@ -52,6 +74,6 @@ namespace LibHac.Common
|
||||
return new U8String(_buffer.ToArray());
|
||||
}
|
||||
|
||||
public bool IsNull() => _buffer == default;
|
||||
public bool IsEmpty() => _buffer.IsEmpty;
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,6 @@ namespace LibHac.Common
|
||||
return StringUtils.Utf8ZToString(_buffer);
|
||||
}
|
||||
|
||||
public bool IsNull() => _buffer == null;
|
||||
public bool IsEmpty() => _buffer == null || _buffer.Length == 0;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ namespace LibHac.Fs
|
||||
internal static Result GetMountName(ReadOnlySpan<char> path, out ReadOnlySpan<char> mountName, out ReadOnlySpan<char> subPath)
|
||||
{
|
||||
int mountLen = 0;
|
||||
int maxMountLen = Math.Min(path.Length, PathTools.MountNameLength);
|
||||
int maxMountLen = Math.Min(path.Length, PathTools.MountNameLengthMax);
|
||||
|
||||
for (int i = 0; i <= maxMountLen; i++)
|
||||
{
|
||||
|
@ -6,7 +6,7 @@ namespace LibHac.Fs
|
||||
{
|
||||
public static Result CheckMountName(U8Span name)
|
||||
{
|
||||
if (name.IsNull()) return ResultFs.NullArgument.Log();
|
||||
if (name.IsEmpty()) return ResultFs.NullArgument.Log();
|
||||
|
||||
if (name.Length > 0 && name[0] == '@') return ResultFs.InvalidMountName.Log();
|
||||
if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log();
|
||||
@ -16,7 +16,7 @@ namespace LibHac.Fs
|
||||
|
||||
public static Result CheckMountNameAcceptingReservedMountName(U8Span name)
|
||||
{
|
||||
if (name.IsNull()) return ResultFs.NullArgument.Log();
|
||||
if (name.IsEmpty()) return ResultFs.NullArgument.Log();
|
||||
|
||||
if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log();
|
||||
|
||||
|
731
src/LibHac/Fs/PathTool.cs
Normal file
731
src/LibHac/Fs/PathTool.cs
Normal file
@ -0,0 +1,731 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public static class PathTool
|
||||
{
|
||||
public static bool IsSeparator(byte c)
|
||||
{
|
||||
return c == StringTraits.DirectorySeparator;
|
||||
}
|
||||
|
||||
public static bool IsAltSeparator(byte c)
|
||||
{
|
||||
return c == StringTraits.AltDirectorySeparator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsAnySeparator(byte c)
|
||||
{
|
||||
return IsSeparator(c) || IsAltSeparator(c);
|
||||
}
|
||||
|
||||
public static bool IsNullTerminator(byte c)
|
||||
{
|
||||
return c == StringTraits.NullTerminator;
|
||||
}
|
||||
|
||||
public static bool IsDot(byte c)
|
||||
{
|
||||
return c == StringTraits.Dot;
|
||||
}
|
||||
|
||||
public static bool IsDriveSeparator(byte c)
|
||||
{
|
||||
return c == StringTraits.DriveSeparator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCurrentDirectory(ReadOnlySpan<byte> p)
|
||||
{
|
||||
if ((uint)p.Length < 1) return false;
|
||||
|
||||
ref byte b = ref MemoryMarshal.GetReference(p);
|
||||
|
||||
return IsDot(b) && (p.Length == 1 || IsSeparator(Unsafe.Add(ref b, 1)) || IsNullTerminator(Unsafe.Add(ref b, 1)));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsParentDirectory(ReadOnlySpan<byte> p)
|
||||
{
|
||||
if ((uint)p.Length < 2) return false;
|
||||
|
||||
ref byte b = ref MemoryMarshal.GetReference(p);
|
||||
|
||||
return IsDot(b) && IsDot(Unsafe.Add(ref b, 1)) &&
|
||||
(p.Length == 2 || IsSeparator(Unsafe.Add(ref b, 2)) || IsNullTerminator(Unsafe.Add(ref b, 2)));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsParentDirectoryAlt(ReadOnlySpan<byte> p)
|
||||
{
|
||||
if ((uint)p.Length < 3) return false;
|
||||
|
||||
ref byte b = ref MemoryMarshal.GetReference(p);
|
||||
|
||||
return IsAnySeparator(b) && IsDot(Unsafe.Add(ref b, 1)) && IsDot(Unsafe.Add(ref b, 2)) &&
|
||||
(p.Length == 3 || IsAnySeparator(Unsafe.Add(ref b, 3)) || IsNullTerminator(Unsafe.Add(ref b, 3)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a path begins with / or \ and contains any of these patterns:
|
||||
/// "/..\", "\..\", "\../", "\..0" where '0' is the null terminator.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsParentDirectoryAlt(U8Span path2)
|
||||
{
|
||||
if (!IsAnySeparator(path2.GetOrNull(0))) return false;
|
||||
|
||||
for (int i = 0; i < path2.Length - 2; i++)
|
||||
{
|
||||
byte c = path2[i];
|
||||
|
||||
if (IsSeparator(c) &&
|
||||
IsDot(path2[i + 1]) &&
|
||||
IsDot(path2[i + 2]) &&
|
||||
IsAltSeparator(path2.GetOrNull(i + 3)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsAltSeparator(c) &&
|
||||
IsDot(path2[i + 1]) &&
|
||||
IsDot(path2[i + 2]))
|
||||
{
|
||||
byte c3 = path2.GetOrNull(i + 3);
|
||||
|
||||
if (IsNullTerminator(c3) || IsAnySeparator(c3))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (IsNullTerminator(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Result Normalize(Span<byte> outputBuffer, out long normalizedLength, U8Span path,
|
||||
bool preserveUnc, bool hasMountName)
|
||||
{
|
||||
normalizedLength = default;
|
||||
|
||||
U8Span path2 = path;
|
||||
int prefixLength = 0;
|
||||
bool isUncPath = false;
|
||||
|
||||
if (hasMountName)
|
||||
{
|
||||
Result rc = ParseMountName(out path2, outputBuffer, out long mountNameLength, path2);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
prefixLength += (int)mountNameLength;
|
||||
}
|
||||
|
||||
if (preserveUnc)
|
||||
{
|
||||
U8Span originalPath = path2;
|
||||
|
||||
Result rc = ParseWindowsPath(out path2, outputBuffer.Slice(prefixLength), out long windowsPathLength,
|
||||
out _, false, path2, hasMountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
prefixLength += (int)windowsPathLength;
|
||||
if (originalPath.Value != path2.Value)
|
||||
{
|
||||
isUncPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (prefixLength == 0 && !IsSeparator(path2.GetOrNull(0)))
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
if (ContainsParentDirectoryAlt(path2))
|
||||
{
|
||||
var buffer2 = new byte[PathTools.MaxPathLength + 1];
|
||||
|
||||
buffer2[0] = StringTraits.DirectorySeparator;
|
||||
int j;
|
||||
|
||||
for (j = 1; j < path2.Length; j++)
|
||||
{
|
||||
byte c = path2[j];
|
||||
if (IsNullTerminator(c))
|
||||
break;
|
||||
|
||||
// Current char is a dot. Check the surrounding chars for the /../ pattern
|
||||
if (IsDot(c) && IsParentDirectoryAlt(path2.Value.Slice(j - 1)))
|
||||
{
|
||||
buffer2[j - 1] = StringTraits.DirectorySeparator;
|
||||
buffer2[j] = StringTraits.Dot;
|
||||
buffer2[j + 1] = StringTraits.Dot;
|
||||
|
||||
j += 2;
|
||||
|
||||
if (!IsNullTerminator(path2.GetOrNull(j)))
|
||||
{
|
||||
buffer2[j] = StringTraits.DirectorySeparator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer2[j] = c;
|
||||
}
|
||||
}
|
||||
|
||||
buffer2[j] = StringTraits.NullTerminator;
|
||||
path2 = new U8Span(buffer2);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
bool skipNextSep = false;
|
||||
int totalLength = prefixLength;
|
||||
|
||||
while (i < path2.Length && !IsNullTerminator(path2[i]))
|
||||
{
|
||||
if (IsSeparator(path2[i]))
|
||||
{
|
||||
do
|
||||
{
|
||||
i++;
|
||||
} while (i < path2.Length && IsSeparator(path2[i]));
|
||||
|
||||
if (i >= path2.Length || IsNullTerminator(path2[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skipNextSep)
|
||||
{
|
||||
if (totalLength + 1 == outputBuffer.Length)
|
||||
{
|
||||
outputBuffer[totalLength] = StringTraits.NullTerminator;
|
||||
normalizedLength = totalLength;
|
||||
|
||||
return ResultFs.TooLongPath.Log();
|
||||
}
|
||||
|
||||
outputBuffer[totalLength++] = StringTraits.DirectorySeparator;
|
||||
}
|
||||
|
||||
skipNextSep = false;
|
||||
}
|
||||
|
||||
int dirLen = 0;
|
||||
while (path2.Length > i + dirLen && !IsNullTerminator(path2[i + dirLen]) && !IsSeparator(path2[i + dirLen]))
|
||||
{
|
||||
dirLen++;
|
||||
}
|
||||
|
||||
if (IsCurrentDirectory(path2.Slice(i)))
|
||||
{
|
||||
skipNextSep = true;
|
||||
}
|
||||
else if (IsParentDirectory(path2.Slice(i)))
|
||||
{
|
||||
Debug.Assert(IsSeparator(outputBuffer[totalLength - 1]));
|
||||
Debug.Assert(IsSeparator(outputBuffer[prefixLength]));
|
||||
|
||||
if (totalLength == prefixLength + 1)
|
||||
{
|
||||
if (isUncPath)
|
||||
{
|
||||
totalLength--;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResultFs.DirectoryUnobtainable.Log();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
totalLength -= 2;
|
||||
while (!IsSeparator(outputBuffer[totalLength]))
|
||||
{
|
||||
totalLength--;
|
||||
|
||||
if (totalLength == prefixLength)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(IsSeparator(outputBuffer[totalLength]));
|
||||
Debug.Assert(totalLength < outputBuffer.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (totalLength + dirLen + 1 <= outputBuffer.Length)
|
||||
{
|
||||
for (int j = 0; j < dirLen; j++)
|
||||
{
|
||||
outputBuffer[totalLength++] = path2[i + j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int copyLen = outputBuffer.Length - 1 - totalLength;
|
||||
|
||||
for (int j = 0; j < copyLen; j++)
|
||||
{
|
||||
outputBuffer[totalLength++] = path2[i + j];
|
||||
}
|
||||
|
||||
outputBuffer[totalLength] = StringTraits.NullTerminator;
|
||||
normalizedLength = totalLength;
|
||||
return ResultFs.TooLongPath.Log();
|
||||
}
|
||||
}
|
||||
|
||||
i += dirLen;
|
||||
}
|
||||
|
||||
if (skipNextSep)
|
||||
totalLength--;
|
||||
|
||||
if (totalLength < outputBuffer.Length && totalLength == prefixLength && !isUncPath)
|
||||
{
|
||||
outputBuffer[prefixLength] = StringTraits.DirectorySeparator;
|
||||
totalLength++;
|
||||
}
|
||||
|
||||
if (totalLength - 1 > outputBuffer.Length)
|
||||
{
|
||||
return ResultFs.TooLongPath.Log();
|
||||
}
|
||||
|
||||
outputBuffer[totalLength] = StringTraits.NullTerminator;
|
||||
|
||||
Debug.Assert(IsNormalized(out bool isNormalized, new U8Span(outputBuffer), preserveUnc, hasMountName).IsSuccess());
|
||||
Debug.Assert(isNormalized);
|
||||
|
||||
normalizedLength = totalLength;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result IsNormalized(out bool isNormalized, U8Span path, bool preserveUnc, bool hasMountName)
|
||||
{
|
||||
isNormalized = default;
|
||||
U8Span path2 = path;
|
||||
bool isUncPath = false;
|
||||
|
||||
if (hasMountName)
|
||||
{
|
||||
Result rc = ParseMountName(out path2, Span<byte>.Empty, out _, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (path2.Length == 0 || !IsSeparator(path2[0]))
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
if (preserveUnc)
|
||||
{
|
||||
U8Span originalPath = path2;
|
||||
|
||||
Result rc = ParseWindowsPath(out path2, Span<byte>.Empty, out _, out bool isNormalizedWin,
|
||||
true, originalPath, hasMountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!isNormalizedWin)
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// Path is a UNC path if the new path skips part of the original
|
||||
isUncPath = originalPath.Value != path2.Value;
|
||||
|
||||
if (isUncPath)
|
||||
{
|
||||
if (IsSeparator(originalPath.GetOrNull(0)) && IsSeparator(originalPath.GetOrNull(1)))
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (IsNullTerminator(path2.GetOrNull(0)))
|
||||
{
|
||||
isNormalized = true;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path2.IsEmpty() || IsNullTerminator(path2[0]))
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
if (ContainsParentDirectoryAlt(path2))
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
bool pathWasSkipped = path.Value != path2.Value;
|
||||
var state = NormalizeState.Initial;
|
||||
|
||||
for (int i = 0; i < path2.Length; i++)
|
||||
{
|
||||
byte c = path2[i];
|
||||
if (IsNullTerminator(c)) break;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
// I don't think this first case can actually be triggered, but Nintendo has it there anyway
|
||||
case NormalizeState.Initial when pathWasSkipped && IsDot(c): state = NormalizeState.Dot; break;
|
||||
case NormalizeState.Initial when IsSeparator(c): state = NormalizeState.FirstSeparator; break;
|
||||
case NormalizeState.Initial when !pathWasSkipped: return ResultFs.InvalidPathFormat.Log();
|
||||
case NormalizeState.Initial: state = NormalizeState.Normal; break;
|
||||
|
||||
case NormalizeState.Normal when IsSeparator(c): state = NormalizeState.Separator; break;
|
||||
|
||||
case NormalizeState.FirstSeparator when IsDot(c):
|
||||
case NormalizeState.Separator when IsDot(c):
|
||||
state = NormalizeState.Dot;
|
||||
break;
|
||||
|
||||
case NormalizeState.FirstSeparator when IsSeparator(c):
|
||||
case NormalizeState.Separator when IsSeparator(c):
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
|
||||
case NormalizeState.FirstSeparator:
|
||||
case NormalizeState.Separator:
|
||||
state = NormalizeState.Normal;
|
||||
break;
|
||||
|
||||
case NormalizeState.Dot when IsSeparator(c):
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
|
||||
case NormalizeState.Dot when IsDot(c): state = NormalizeState.DoubleDot; break;
|
||||
case NormalizeState.Dot: state = NormalizeState.Normal; break;
|
||||
|
||||
case NormalizeState.DoubleDot when IsSeparator(c):
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
|
||||
case NormalizeState.DoubleDot: state = NormalizeState.Normal; break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case NormalizeState.Initial:
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
case NormalizeState.Normal:
|
||||
isNormalized = true;
|
||||
break;
|
||||
case NormalizeState.FirstSeparator:
|
||||
isNormalized = !isUncPath;
|
||||
break;
|
||||
case NormalizeState.Separator:
|
||||
case NormalizeState.Dot:
|
||||
case NormalizeState.DoubleDot:
|
||||
isNormalized = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result ParseMountName(out U8Span pathAfterMount, Span<byte> outMountNameBuffer,
|
||||
out long mountNameLength, U8Span path)
|
||||
{
|
||||
pathAfterMount = default;
|
||||
mountNameLength = default;
|
||||
|
||||
int mountStart = IsSeparator(path.GetOrNull(0)) ? 1 : 0;
|
||||
int mountEnd;
|
||||
|
||||
int maxMountLength = Math.Min(PathTools.MountNameLengthMax, path.Length - mountStart);
|
||||
|
||||
for (mountEnd = mountStart; mountEnd <= maxMountLength; mountEnd++)
|
||||
{
|
||||
byte c = path[mountEnd];
|
||||
|
||||
if (IsSeparator(c))
|
||||
{
|
||||
pathAfterMount = path;
|
||||
mountNameLength = 0;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (IsDriveSeparator(c))
|
||||
{
|
||||
mountEnd++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsNullTerminator(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mountStart >= mountEnd - 1 || !IsDriveSeparator(path[mountEnd - 1]))
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
for (int i = mountStart; i < mountEnd; i++)
|
||||
{
|
||||
if (IsDot(path[i]))
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
|
||||
if (!outMountNameBuffer.IsEmpty)
|
||||
{
|
||||
if (mountEnd - mountStart > outMountNameBuffer.Length)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
path.Value.Slice(0, mountEnd).CopyTo(outMountNameBuffer);
|
||||
}
|
||||
|
||||
pathAfterMount = path.Slice(mountEnd);
|
||||
mountNameLength = mountEnd - mountStart;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result ParseWindowsPath(out U8Span newPath, Span<byte> buffer, out long windowsPathLength,
|
||||
out bool isNormalized, bool checkIfNormalized, U8Span path, bool hasMountName)
|
||||
{
|
||||
newPath = default;
|
||||
windowsPathLength = 0;
|
||||
isNormalized = checkIfNormalized;
|
||||
|
||||
int winPathLen = 0;
|
||||
int mountNameLen = 0;
|
||||
bool skippedMount = false;
|
||||
|
||||
bool needsSeparatorFixup = false;
|
||||
|
||||
if (hasMountName)
|
||||
{
|
||||
if (IsSeparator(path.GetOrNull(0)) && IsAltSeparator(path.GetOrNull(1)) &&
|
||||
IsAltSeparator(path.GetOrNull(2)))
|
||||
{
|
||||
mountNameLen = 1;
|
||||
skippedMount = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int separatorCount = 0;
|
||||
|
||||
while (IsSeparator(path.GetOrNull(separatorCount)))
|
||||
{
|
||||
separatorCount++;
|
||||
}
|
||||
|
||||
if (separatorCount == 1 || PathUtility.IsWindowsDrive(path.Slice(separatorCount)))
|
||||
{
|
||||
mountNameLen = separatorCount;
|
||||
skippedMount = true;
|
||||
}
|
||||
else if (separatorCount > 2 && checkIfNormalized)
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
else if (separatorCount > 1)
|
||||
{
|
||||
mountNameLen = separatorCount - 2;
|
||||
skippedMount = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsSeparator(path.GetOrNull(0)) && !PathUtility.IsUnc(path))
|
||||
{
|
||||
mountNameLen = 1;
|
||||
skippedMount = true;
|
||||
}
|
||||
}
|
||||
|
||||
U8Span pathTrimmed = path.Slice(mountNameLen);
|
||||
|
||||
if (PathUtility.IsWindowsDrive(pathTrimmed))
|
||||
{
|
||||
int i = 2;
|
||||
|
||||
while (!IsAnySeparator(pathTrimmed.GetOrNull(i)) && !IsNullTerminator(pathTrimmed.GetOrNull(i)))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
winPathLen = mountNameLen + i;
|
||||
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (winPathLen > buffer.Length)
|
||||
{
|
||||
return ResultFs.TooLongPath.Log();
|
||||
}
|
||||
|
||||
path.Value.Slice(0, winPathLen).CopyTo(buffer);
|
||||
}
|
||||
|
||||
newPath = path.Slice(winPathLen);
|
||||
windowsPathLength = winPathLen;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// A UNC path should be in the format "\\" host-name "\" share-name [ "\" object-name ]
|
||||
if (PathUtility.IsUnc(pathTrimmed))
|
||||
{
|
||||
if (IsAnySeparator(pathTrimmed.GetOrNull(2)))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
int currentComponentStart = 2;
|
||||
|
||||
for (int i = 2; ; i++)
|
||||
{
|
||||
byte c = pathTrimmed.GetOrNull(i);
|
||||
|
||||
if (IsAnySeparator(c))
|
||||
{
|
||||
// Nintendo feels the need to change the '\' separators to '/'s
|
||||
if (IsAltSeparator(c))
|
||||
{
|
||||
needsSeparatorFixup = true;
|
||||
|
||||
if (checkIfNormalized)
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure share-name is not empty
|
||||
if (currentComponentStart == 2 && IsSeparator(pathTrimmed.GetOrNull(i + 1)))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
int componentLength = i - currentComponentStart;
|
||||
|
||||
// neither host-name nor share-name can be "." or ".."
|
||||
if (componentLength == 1 && IsDot(pathTrimmed[currentComponentStart]))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
if (componentLength == 2 && IsDot(pathTrimmed[currentComponentStart]) &&
|
||||
IsDot(pathTrimmed[currentComponentStart + 1]))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
// If we're currently processing the share-name path component
|
||||
if (currentComponentStart != 2)
|
||||
{
|
||||
winPathLen = mountNameLen + i;
|
||||
break;
|
||||
}
|
||||
|
||||
currentComponentStart = i + 1;
|
||||
}
|
||||
else if (c == (byte)'$' || IsDriveSeparator(c))
|
||||
{
|
||||
// '$' and ':' are not allowed in the host-name path component
|
||||
if (currentComponentStart == 2)
|
||||
{
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
|
||||
// A '$' or ':' must be the last character in share-name
|
||||
byte nextChar = pathTrimmed.GetOrNull(i + 1);
|
||||
if (!IsSeparator(nextChar) && !IsNullTerminator(nextChar))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
winPathLen = mountNameLen + i + 1;
|
||||
break;
|
||||
}
|
||||
else if (IsNullTerminator(c))
|
||||
{
|
||||
if (currentComponentStart != 2)
|
||||
{
|
||||
int componentLength = i - currentComponentStart;
|
||||
|
||||
// neither host-name nor share-name can be "." or ".."
|
||||
if (componentLength == 1 && IsDot(pathTrimmed[currentComponentStart]))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
if (componentLength == 2 && IsDot(pathTrimmed[currentComponentStart]) &&
|
||||
IsDot(pathTrimmed[currentComponentStart + 1]))
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
winPathLen = mountNameLen + i;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (winPathLen - mountNameLen > buffer.Length)
|
||||
{
|
||||
return ResultFs.TooLongPath.Log();
|
||||
}
|
||||
|
||||
int outPos = 0;
|
||||
|
||||
if (skippedMount)
|
||||
{
|
||||
buffer[0] = StringTraits.DirectorySeparator;
|
||||
outPos++;
|
||||
}
|
||||
|
||||
pathTrimmed.Value.Slice(0, winPathLen - mountNameLen).CopyTo(buffer.Slice(outPos));
|
||||
|
||||
buffer[outPos] = StringTraits.AltDirectorySeparator;
|
||||
buffer[outPos + 1] = StringTraits.AltDirectorySeparator;
|
||||
|
||||
if (needsSeparatorFixup)
|
||||
{
|
||||
for (int i = mountNameLen + 2; i < winPathLen; i++)
|
||||
{
|
||||
if (IsAltSeparator(buffer[i]))
|
||||
{
|
||||
buffer[i] = StringTraits.DirectorySeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newPath = path.Slice(winPathLen);
|
||||
windowsPathLength = outPos + winPathLen - mountNameLen;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
newPath = path.Slice(winPathLen);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private enum NormalizeState
|
||||
{
|
||||
Initial,
|
||||
Normal,
|
||||
FirstSeparator,
|
||||
Separator,
|
||||
Dot,
|
||||
DoubleDot
|
||||
}
|
||||
}
|
||||
}
|
32
src/LibHac/Fs/PathUtility.cs
Normal file
32
src/LibHac/Fs/PathUtility.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using static LibHac.Fs.PathTool;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public static class PathUtility
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsWindowsDrive(U8Span path)
|
||||
{
|
||||
return (uint)path.Length > 1 &&
|
||||
(IsDriveSeparator(path[1]) &&
|
||||
IsWindowsDriveCharacter(path[0]));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsWindowsDriveCharacter(byte c)
|
||||
{
|
||||
return (0b1101_1111 & c) - 'A' <= 'Z' - 'A';
|
||||
//return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsUnc(U8Span path)
|
||||
{
|
||||
return (uint)path.Length > 1 &&
|
||||
(IsSeparator(path[0]) && IsSeparator(path[1]) ||
|
||||
IsAltSeparator(path[0]) && IsAltSeparator(path[1]));
|
||||
}
|
||||
}
|
||||
}
|
11
src/LibHac/Fs/StringTraits.cs
Normal file
11
src/LibHac/Fs/StringTraits.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
internal static class StringTraits
|
||||
{
|
||||
public const byte DirectorySeparator = (byte)'/';
|
||||
public const byte AltDirectorySeparator = (byte)'\\';
|
||||
public const byte DriveSeparator = (byte)':';
|
||||
public const byte Dot = (byte)'.';
|
||||
public const byte NullTerminator = 0;
|
||||
}
|
||||
}
|
45
src/LibHac/FsService/PathNormalizer.cs
Normal file
45
src/LibHac/FsService/PathNormalizer.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public ref struct PathNormalizer
|
||||
{
|
||||
private U8Span _path;
|
||||
private Result _result;
|
||||
|
||||
public PathNormalizer(U8Span path, Option option)
|
||||
{
|
||||
if (option.HasFlag(Option.AcceptEmpty) && path.IsEmpty())
|
||||
{
|
||||
_path = path;
|
||||
_result = Result.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool preserveUnc = option.HasFlag(Option.PreserveUnc);
|
||||
bool preserveTailSeparator = option.HasFlag(Option.PreserveTailSeparator);
|
||||
bool hasMountName = option.HasFlag(Option.HasMountName);
|
||||
_result = Normalize(out _path, path, preserveUnc, preserveTailSeparator, hasMountName);
|
||||
}
|
||||
}
|
||||
|
||||
private static Result Normalize(out U8Span normalizedPath, U8Span path, bool preserveUnc,
|
||||
bool preserveTailSeparator, bool hasMountName)
|
||||
{
|
||||
normalizedPath = default;
|
||||
throw new NotImplementedException();
|
||||
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Option
|
||||
{
|
||||
None = 0,
|
||||
PreserveUnc = (1 << 0),
|
||||
PreserveTailSeparator = (1 << 1),
|
||||
HasMountName = (1 << 2),
|
||||
AcceptEmpty = (1 << 3),
|
||||
}
|
||||
}
|
||||
}
|
@ -9,9 +9,10 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
public static class PathTools
|
||||
{
|
||||
// todo: Consolidate these
|
||||
internal const char DirectorySeparator = '/';
|
||||
internal const char MountSeparator = ':';
|
||||
internal const int MountNameLength = 0xF;
|
||||
internal const int MountNameLengthMax = 0xF;
|
||||
|
||||
// Todo: Remove
|
||||
internal const int MaxPathLength = 0x300;
|
||||
@ -24,7 +25,7 @@ namespace LibHac.FsSystem
|
||||
var sb = new ValueStringBuilder(initialBuffer);
|
||||
|
||||
int rootLen = 0;
|
||||
int maxMountLen = Math.Min(inPath.Length, MountNameLength);
|
||||
int maxMountLen = Math.Min(inPath.Length, MountNameLengthMax);
|
||||
|
||||
for (int i = 0; i < maxMountLen; i++)
|
||||
{
|
||||
@ -467,7 +468,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
public static Result GetMountNameLength(string path, out int length)
|
||||
{
|
||||
int maxLen = Math.Min(path.Length, MountNameLength);
|
||||
int maxLen = Math.Min(path.Length, MountNameLengthMax);
|
||||
|
||||
for (int i = 0; i < maxLen; i++)
|
||||
{
|
||||
@ -515,7 +516,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
int rootLength = 0;
|
||||
|
||||
for (int i = mountNameStart; i < mountNameStart + MountNameLength; i++)
|
||||
for (int i = mountNameStart; i < mountNameStart + MountNameLengthMax; i++)
|
||||
{
|
||||
if (i >= path.Length || path[i] == 0) break;
|
||||
|
||||
|
12
tests/LibHac.Tests/DebugAssertHandler.cs
Normal file
12
tests/LibHac.Tests/DebugAssertHandler.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace LibHac.Tests
|
||||
{
|
||||
public class DebugAssertHandler : DefaultTraceListener
|
||||
{
|
||||
public override void Fail(string message, string detailMessage)
|
||||
{
|
||||
// throw new System.NotImplementedException(message + detailMessage);
|
||||
}
|
||||
}
|
||||
}
|
279
tests/LibHac.Tests/Fs/PathToolTestGenerator.cpp
Normal file
279
tests/LibHac.Tests/Fs/PathToolTestGenerator.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
// Uses GLoat to run code in nnsdk https://github.com/h1k421/GLoat
|
||||
#include <gloat.hpp>
|
||||
|
||||
static char Buf[0x80000];
|
||||
static int BufPos = 0;
|
||||
|
||||
static char ResultNameBuf[0x100];
|
||||
|
||||
namespace nn::fs::detail {
|
||||
bool IsEnabledAccessLog();
|
||||
}
|
||||
|
||||
namespace nn::fs::PathTool {
|
||||
// SDK < 7
|
||||
nn::Result Normalize(char* buffer, uint64_t* outNormalizedPathLength, const char* path, uint64_t bufferLength, bool preserveUnc);
|
||||
nn::Result IsNormalized(bool* outIsNormalized, const char* path);
|
||||
|
||||
// SDK >= 7
|
||||
nn::Result Normalize(char* buffer, uint64_t* outNormalizedPathLength, const char* path, uint64_t bufferLength, bool preserveUnc, bool hasMountName);
|
||||
nn::Result IsNormalized(bool* outIsNormalized, const char* path, bool preserveUnc, bool hasMountName);
|
||||
}
|
||||
|
||||
void* allocate(size_t size)
|
||||
{
|
||||
void* ptr = malloc(size);
|
||||
|
||||
char buffer[0x40];
|
||||
sprintf(buffer, "Allocating %ld. 0x%p", size, ptr);
|
||||
svcOutputDebugString(buffer, sizeof(buffer));
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, size_t size)
|
||||
{
|
||||
char buffer[0x40];
|
||||
sprintf(buffer, "Deallocating %ld. 0x%p", size, ptr);
|
||||
svcOutputDebugString(buffer, sizeof(buffer));
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void setAllocators(void) {
|
||||
nn::fs::SetAllocator(allocate, deallocate);
|
||||
}
|
||||
|
||||
const char* GetResultName(nn::Result result) {
|
||||
switch (result.GetValue()) {
|
||||
case 0: return "Result.Success";
|
||||
case 0x2EE402: return "ResultFs.InvalidPath.Value";
|
||||
case 0x2EE602: return "ResultFs.TooLongPath.Value";
|
||||
case 0x2EE802: return "ResultFs.InvalidCharacter.Value";
|
||||
case 0x2EEA02: return "ResultFs.InvalidPathFormat.Value";
|
||||
case 0x2EEC02: return "ResultFs.DirectoryUnobtainable.Value";
|
||||
default:
|
||||
sprintf(ResultNameBuf, "0x%x", result.GetValue());
|
||||
return ResultNameBuf;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateNormalizeTestData(char const* path, bool preserveUnc, bool hasMountName) {
|
||||
char normalized[0x200];
|
||||
uint64_t normalizedLen = 0;
|
||||
memset(normalized, 0, 0x200);
|
||||
|
||||
//svcOutputDebugString(path, strnlen(path, 0x200));
|
||||
|
||||
nn::Result result = nn::fs::PathTool::Normalize(normalized, &normalizedLen, path, 0x200, preserveUnc, hasMountName);
|
||||
|
||||
const char* preserveUncStr = preserveUnc ? "true" : "false";
|
||||
const char* hasMountNameStr = hasMountName ? "true" : "false";
|
||||
BufPos += sprintf(&Buf[BufPos], "new object[] {@\"%s\", %s, %s, @\"%s\", %ld, %s},\n",
|
||||
path, preserveUncStr, hasMountNameStr, normalized, normalizedLen, GetResultName(result));
|
||||
}
|
||||
|
||||
void CreateIsNormalizedTestData(char const* path, bool preserveUnc, bool hasMountName) {
|
||||
bool isNormalized = false;
|
||||
|
||||
nn::Result result = nn::fs::PathTool::IsNormalized(&isNormalized, path, preserveUnc, hasMountName);
|
||||
|
||||
const char* preserveUncStr = preserveUnc ? "true" : "false";
|
||||
const char* hasMountNameStr = hasMountName ? "true" : "false";
|
||||
const char* isNormalizedStr = isNormalized ? "true" : "false";
|
||||
BufPos += sprintf(&Buf[BufPos], "new object[] {@\"%s\", %s, %s, %s, %s},\n",
|
||||
path, preserveUncStr, hasMountNameStr, isNormalizedStr, GetResultName(result));
|
||||
}
|
||||
|
||||
void CreateTestDataWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool), int parentCount) {
|
||||
char parentPath[0x200];
|
||||
memset(parentPath, 0, sizeof(parentPath));
|
||||
|
||||
strcpy(parentPath, path);
|
||||
func(parentPath, preserveUnc, hasMountName);
|
||||
|
||||
for (int i = 0; i < parentCount; i++) {
|
||||
strcat(parentPath, "/..");
|
||||
func(parentPath, preserveUnc, hasMountName);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTestDataWithParentDirs(char const* path, bool preserveUnc, bool hasMountName, void (*func)(char const*, bool, bool)) {
|
||||
CreateTestDataWithParentDirs(path, preserveUnc, hasMountName, func, 3);
|
||||
}
|
||||
|
||||
void CreateTestData(void (*func)(char const*, bool, bool)) {
|
||||
Buf[0] = '\n';
|
||||
BufPos = 1;
|
||||
|
||||
bool preserveUnc = false;
|
||||
|
||||
func("", preserveUnc, false);
|
||||
func("/", preserveUnc, false);
|
||||
func("/.", preserveUnc, false);
|
||||
func("/a/b/c", preserveUnc, false);
|
||||
func("/a/b/../c", preserveUnc, false);
|
||||
func("/a/b/c/..", preserveUnc, false);
|
||||
func("/a/b/c/.", preserveUnc, false);
|
||||
func("/a/../../..", preserveUnc, false);
|
||||
func("/a/../../../a/b/c", preserveUnc, false);
|
||||
func("//a/b//.//c", preserveUnc, false);
|
||||
func("/../a/b/c/.", preserveUnc, false);
|
||||
func("/./aaa/bbb/ccc/.", preserveUnc, false);
|
||||
func("/a/b/c/", preserveUnc, false);
|
||||
func("a/b/c/", preserveUnc, false);
|
||||
func("/aa/./bb/../cc/", preserveUnc, false);
|
||||
func("/./b/../c/", preserveUnc, false);
|
||||
func("/a/../../../", preserveUnc, false);
|
||||
func("//a/b//.//c/", preserveUnc, false);
|
||||
func("/tmp/../", preserveUnc, false);
|
||||
func("a", preserveUnc, false);
|
||||
func("a/../../../a/b/c", preserveUnc, false);
|
||||
func("./b/../c/", preserveUnc, false);
|
||||
func(".", preserveUnc, false);
|
||||
func("..", preserveUnc, false);
|
||||
func("../a/b/c/.", preserveUnc, false);
|
||||
func("./a/b/c/.", preserveUnc, false);
|
||||
func("abc", preserveUnc, false);
|
||||
func("mount:/a/b/../c", preserveUnc, true);
|
||||
func("a:/a/b/c", preserveUnc, true);
|
||||
func("mount:/a/b/../c", preserveUnc, true);
|
||||
func("mount:\\a/b/../c", preserveUnc, true);
|
||||
func("mount:\\a/b\\../c", preserveUnc, true);
|
||||
func("mount:\\a/b/c", preserveUnc, true);
|
||||
func("mount:/a\\../b\\..c", preserveUnc, true);
|
||||
func("mount:/a\\../b/..cd", preserveUnc, true);
|
||||
func("mount:/a\\..d/b/c\\..", preserveUnc, true);
|
||||
func("mount:", preserveUnc, true);
|
||||
func("abc:/a/../../../a/b/c", preserveUnc, true);
|
||||
func("abc:/./b/../c/", preserveUnc, true);
|
||||
func("abc:/.", preserveUnc, true);
|
||||
func("abc:/..", preserveUnc, true);
|
||||
func("abc:/", preserveUnc, true);
|
||||
func("abc://a/b//.//c", preserveUnc, true);
|
||||
func("abc:/././/././a/b//.//c", preserveUnc, true);
|
||||
func("mount:/d./aa", preserveUnc, true);
|
||||
func("mount:/d/..", preserveUnc, true);
|
||||
func("/path/aaa/bbb\\..\\h/ddd", preserveUnc, false);
|
||||
func("/path/aaa/bbb/../h/ddd", preserveUnc, false);
|
||||
func("/path/aaa/bbb\\.\\h/ddd", preserveUnc, false);
|
||||
func("/path/aaa/bbb\\./h/ddd", preserveUnc, false);
|
||||
func("/path/aaa/bbb/./h/ddd", preserveUnc, false);
|
||||
func("mount:abcd", preserveUnc, true);
|
||||
func("mount:", preserveUnc, true);
|
||||
func("mount:/", preserveUnc, true);
|
||||
func("mount:\\..", preserveUnc, true);
|
||||
func("mount:/a/b\\..", preserveUnc, true);
|
||||
func("mount:/dir", preserveUnc, true);
|
||||
func("mount:/dir/", preserveUnc, true);
|
||||
func("mount:\\", preserveUnc, true);
|
||||
func("mo.unt:\\", preserveUnc, true);
|
||||
func("mount.:\\", preserveUnc, true);
|
||||
func("mount:./aa/bb", preserveUnc, true);
|
||||
//func("mount:../aa/bb", preserveUnc, true); // crashes nnsdk
|
||||
func("mount:.../aa/bb", preserveUnc, true);
|
||||
func("mount:...aa/bb", preserveUnc, true);
|
||||
func("...aa/bb", preserveUnc, false);
|
||||
func("mount01234567890/aa/bb", preserveUnc, true);
|
||||
func("mount01234567890:/aa/bb", preserveUnc, true);
|
||||
func("mount0123456789:/aa/bb", preserveUnc, true);
|
||||
func("mount012345678:/aa/bb", preserveUnc, true);
|
||||
func("mount:aa/..\\bb", preserveUnc, true);
|
||||
func("mount:..\\bb", preserveUnc, true);
|
||||
func("mount:/..\\bb", preserveUnc, true);
|
||||
func("mount:/.\\bb", preserveUnc, true);
|
||||
func("mount:\\..\\bb", preserveUnc, true);
|
||||
func("mount:\\.\\bb", preserveUnc, true);
|
||||
func("mount:/a\\..\\bb", preserveUnc, true);
|
||||
func("mount:/a\\.\\bb", preserveUnc, true);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
preserveUnc = (bool)i;
|
||||
|
||||
CreateTestDataWithParentDirs("//$abc/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//:abc/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("\\\\\\asd", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("\\\\/asd", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("\\\\//asd", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a/b/cc/../d", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("c:/aa/bb", preserveUnc, true, func);
|
||||
CreateTestDataWithParentDirs("mount:\\c:/aa", preserveUnc, true, func);
|
||||
CreateTestDataWithParentDirs("mount:/c:\\aa/bb", preserveUnc, true, func);
|
||||
CreateTestDataWithParentDirs("mount:////c:\\aa/bb", preserveUnc, true, func);
|
||||
CreateTestDataWithParentDirs("mount:/\\aa/bb", preserveUnc, true, func);
|
||||
CreateTestDataWithParentDirs("mount:/c:/aa/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("mount:c:/aa/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("mount:c:/aa/bb", preserveUnc, true, func, 0);
|
||||
CreateTestDataWithParentDirs("mount:/\\aa/../b", preserveUnc, true, func, 2);
|
||||
CreateTestDataWithParentDirs("mount://aa/bb", preserveUnc, true, func, 1);
|
||||
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, true, func, 1);
|
||||
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("/aa/bb", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("c:/aa", preserveUnc, false, func, 2);
|
||||
CreateTestDataWithParentDirs("c:abcde/aa/bb", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("c:abcde", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("c:abcde/", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("///aa", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa//bb", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//./bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//../bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//.../bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa$abc/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa$/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa:/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb$b/cc$", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc$c", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc$c/dd", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc\\/dd", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc//dd", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc/dd", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("//aa/bb/cc/\\dd", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("//aa/../", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa//", preserveUnc, false, func, 0);
|
||||
CreateTestDataWithParentDirs("//aa/bb..", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//aa/bb../", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("/\\\\aa/bb/cc/..", preserveUnc, true, func);
|
||||
|
||||
CreateTestDataWithParentDirs("c:aa\\bb/cc", preserveUnc, false, func);
|
||||
CreateTestDataWithParentDirs("c:\\//\\aa\\bb", preserveUnc, false, func, 1);
|
||||
|
||||
CreateTestDataWithParentDirs("mount://////a/bb/c", preserveUnc, true, func, 2);
|
||||
|
||||
CreateTestDataWithParentDirs("//", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//a", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//a", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//a/", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//a/b", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//a/b/", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("//a/b/c", preserveUnc, false, func, 2);
|
||||
CreateTestDataWithParentDirs("//a/b/c/", preserveUnc, false, func, 2);
|
||||
|
||||
CreateTestDataWithParentDirs("\\\\", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a/", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a/b", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a/b/", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a/b/c", preserveUnc, false, func, 2);
|
||||
CreateTestDataWithParentDirs("\\\\a/b/c/", preserveUnc, false, func, 2);
|
||||
|
||||
CreateTestDataWithParentDirs("\\\\", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a\\", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a\\b", preserveUnc, false, func, 1);
|
||||
CreateTestDataWithParentDirs("\\\\a\\b\\", preserveUnc, false, func, 1); // "\\a\b\/../.." crashes nnsdk
|
||||
CreateTestDataWithParentDirs("\\\\a\\b\\c", preserveUnc, false, func, 2);
|
||||
CreateTestDataWithParentDirs("\\\\a\\b\\c\\", preserveUnc, false, func, 2);
|
||||
}
|
||||
|
||||
svcOutputDebugString(Buf, BufPos);
|
||||
}
|
||||
|
||||
extern "C" void nnMain(void) {
|
||||
//setAllocators();
|
||||
nn::fs::detail::IsEnabledAccessLog(); // Adds the sdk version to the output when not calling setAllocators
|
||||
CreateTestData(CreateNormalizeTestData);
|
||||
CreateTestData(CreateIsNormalizedTestData);
|
||||
}
|
817
tests/LibHac.Tests/Fs/PathToolTests.cs
Normal file
817
tests/LibHac.Tests/Fs/PathToolTests.cs
Normal file
@ -0,0 +1,817 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Fs
|
||||
{
|
||||
public class PathToolTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(NormalizeTestItems))]
|
||||
public static void Normalize(string path, bool preserveUnc, bool hasMountName, string expectedNormalized,
|
||||
long expectedLength, Result expectedResult)
|
||||
{
|
||||
var buffer = new byte[0x301];
|
||||
|
||||
Result result = PathTool.Normalize(buffer, out long normalizedLength, path.ToU8Span(), preserveUnc,
|
||||
hasMountName);
|
||||
|
||||
Assert.Equal(expectedResult, result);
|
||||
Assert.Equal(expectedNormalized, StringUtils.Utf8ZToString(buffer));
|
||||
Assert.Equal(expectedLength, normalizedLength);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(IsNormalizedTestItems))]
|
||||
public static void IsNormalized(string path, bool preserveUnc, bool hasMountName, bool expectedIsNormalized,
|
||||
Result expectedResult)
|
||||
{
|
||||
Result result = PathTool.IsNormalized(out bool isNormalized, path.ToU8Span(), preserveUnc, hasMountName);
|
||||
|
||||
Assert.Equal(expectedResult, result);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
Assert.Equal(expectedIsNormalized, isNormalized);
|
||||
}
|
||||
}
|
||||
|
||||
public static object[][] NormalizeTestItems =
|
||||
{
|
||||
new object[] {@"", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"/", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"/.", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"/a/b/c", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"/a/b/../c", false, false, @"/a/c", 4, Result.Success},
|
||||
new object[] {@"/a/b/c/..", false, false, @"/a/b", 4, Result.Success},
|
||||
new object[] {@"/a/b/c/.", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"/a/../../..", false, false, @"/a/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"/a/../../../a/b/c", false, false, @"/a/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"//a/b//.//c", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"/../a/b/c/.", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"/./aaa/bbb/ccc/.", false, false, @"/aaa/bbb/ccc", 12, Result.Success},
|
||||
new object[] {@"/a/b/c/", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"a/b/c/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"/aa/./bb/../cc/", false, false, @"/aa/cc", 6, Result.Success},
|
||||
new object[] {@"/./b/../c/", false, false, @"/c", 2, Result.Success},
|
||||
new object[] {@"/a/../../../", false, false, @"/a/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"//a/b//.//c/", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"/tmp/../", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"a", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"a/../../../a/b/c", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"./b/../c/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@".", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"../a/b/c/.", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"./a/b/c/.", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"abc", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a/b/../c", false, true, @"mount:/a/c", 10, Result.Success},
|
||||
new object[] {@"a:/a/b/c", false, true, @"a:/a/b/c", 8, Result.Success},
|
||||
new object[] {@"mount:/a/b/../c", false, true, @"mount:/a/c", 10, Result.Success},
|
||||
new object[] {@"mount:\a/b/../c", false, true, @"mount:\a/c", 10, Result.Success},
|
||||
new object[] {@"mount:\a/b\../c", false, true, @"mount:/a/c", 10, Result.Success},
|
||||
new object[] {@"mount:\a/b/c", false, true, @"mount:\a/b/c", 12, Result.Success},
|
||||
new object[] {@"mount:/a\../b\..c", false, true, @"mount:/b\..c", 12, Result.Success},
|
||||
new object[] {@"mount:/a\../b/..cd", false, true, @"mount:/b/..cd", 13, Result.Success},
|
||||
new object[] {@"mount:/a\..d/b/c\..", false, true, @"mount:/a\..d/b", 14, Result.Success},
|
||||
new object[] {@"mount:", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"abc:/a/../../../a/b/c", false, true, @"abc:/a/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"abc:/./b/../c/", false, true, @"abc:/c", 6, Result.Success},
|
||||
new object[] {@"abc:/.", false, true, @"abc:/", 5, Result.Success},
|
||||
new object[] {@"abc:/..", false, true, @"abc:/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"abc:/", false, true, @"abc:/", 5, Result.Success},
|
||||
new object[] {@"abc://a/b//.//c", false, true, @"abc:/a/b/c", 10, Result.Success},
|
||||
new object[] {@"abc:/././/././a/b//.//c", false, true, @"abc:/a/b/c", 10, Result.Success},
|
||||
new object[] {@"mount:/d./aa", false, true, @"mount:/d./aa", 12, Result.Success},
|
||||
new object[] {@"mount:/d/..", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb\..\h/ddd", false, false, @"/path/aaa/h/ddd", 15, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb/../h/ddd", false, false, @"/path/aaa/h/ddd", 15, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb\.\h/ddd", false, false, @"/path/aaa/bbb\.\h/ddd", 21, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb\./h/ddd", false, false, @"/path/aaa/bbb\./h/ddd", 21, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb/./h/ddd", false, false, @"/path/aaa/bbb/h/ddd", 19, Result.Success},
|
||||
new object[] {@"mount:abcd", false, true, @"mount:abcd", 10, Result.Success},
|
||||
new object[] {@"mount:", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:/", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:\..", false, true, @"mount:/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/a/b\..", false, true, @"mount:/a", 8, Result.Success},
|
||||
new object[] {@"mount:/dir", false, true, @"mount:/dir", 10, Result.Success},
|
||||
new object[] {@"mount:/dir/", false, true, @"mount:/dir", 10, Result.Success},
|
||||
new object[] {@"mount:\", false, true, @"mount:\", 7, Result.Success},
|
||||
new object[] {@"mo.unt:\", false, true, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"mount.:\", false, true, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"mount:./aa/bb", false, true, @"mount:aa/bb", 11, Result.Success},
|
||||
new object[] {@"mount:.../aa/bb", false, true, @"mount:.../aa/bb", 15, Result.Success},
|
||||
new object[] {@"mount:...aa/bb", false, true, @"mount:...aa/bb", 14, Result.Success},
|
||||
new object[] {@"...aa/bb", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount01234567890/aa/bb", false, true, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount01234567890:/aa/bb", false, true, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount0123456789:/aa/bb", false, true, @"mount0123456789:/aa/bb", 22, Result.Success},
|
||||
new object[] {@"mount012345678:/aa/bb", false, true, @"mount012345678:/aa/bb", 21, Result.Success},
|
||||
new object[] {@"mount:aa/..\bb", false, true, @"mount:aa/..\bb", 14, Result.Success},
|
||||
new object[] {@"mount:..\bb", false, true, @"mount:..\bb", 11, Result.Success},
|
||||
new object[] {@"mount:/..\bb", false, true, @"mount:/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/.\bb", false, true, @"mount:/.\bb", 11, Result.Success},
|
||||
new object[] {@"mount:\..\bb", false, true, @"mount:/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:\.\bb", false, true, @"mount:\.\bb", 11, Result.Success},
|
||||
new object[] {@"mount:/a\..\bb", false, true, @"mount:/bb", 9, Result.Success},
|
||||
new object[] {@"mount:/a\.\bb", false, true, @"mount:/a\.\bb", 13, Result.Success},
|
||||
new object[] {@"//$abc/bb", false, false, @"/$abc/bb", 8, Result.Success},
|
||||
new object[] {@"//:abc/bb", false, false, @"/:abc/bb", 8, Result.Success},
|
||||
new object[] {@"\\\asd", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/asd", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\//asd", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"///..", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"\\a/b/cc/../d", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d/../../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:/aa/bb", false, true, @"c:/aa/bb", 8, Result.Success},
|
||||
new object[] {@"c:/aa/bb/..", false, true, @"c:/aa", 5, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../..", false, true, @"c:/", 3, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../../..", false, true, @"c:/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:\c:/aa", false, true, @"mount:\c:/aa", 12, Result.Success},
|
||||
new object[] {@"mount:\c:/aa/..", false, true, @"mount:\c:", 9, Result.Success},
|
||||
new object[] {@"mount:\c:/aa/../..", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:\c:/aa/../../..", false, true, @"mount:/c:/aa/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/c:\aa/bb", false, true, @"mount:/c:\aa/bb", 15, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/..", false, true, @"mount:/c:\aa", 12, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../..", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../../..", false, true, @"mount:/c:\aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:////c:\aa/bb", false, true, @"mount:/c:\aa/bb", 15, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/..", false, true, @"mount:/c:\aa", 12, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../..", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../../..", false, true, @"mount:/c:\aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/\aa/bb", false, true, @"mount:/\aa/bb", 13, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/..", false, true, @"mount:/\aa", 10, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../..", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../../..", false, true, @"mount:/\aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/c:/aa/bb", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", false, true, @"mount:c:/aa/bb", 14, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b", false, true, @"mount:/b", 8, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/..", false, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/../..", false, true, @"mount:/b/a/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount://aa/bb", false, true, @"mount:/aa/bb", 12, Result.Success},
|
||||
new object[] {@"mount://aa/bb/..", false, true, @"mount:/aa", 9, Result.Success},
|
||||
new object[] {@"//aa/bb", false, true, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/..", false, true, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa/bb", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"/aa/bb", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"/aa/bb/..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"/aa/bb/../..", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"/aa/bb/../../..", false, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"c:/aa", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:/aa/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:/aa/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb/../../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"///aa", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa//bb", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa//bb/..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//./bb", false, false, @"/bb", 3, Result.Success},
|
||||
new object[] {@"//../bb", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"//.../bb", false, false, @"/.../bb", 7, Result.Success},
|
||||
new object[] {@"//aa$abc/bb", false, false, @"/aa$abc/bb", 10, Result.Success},
|
||||
new object[] {@"//aa$/bb", false, false, @"/aa$/bb", 7, Result.Success},
|
||||
new object[] {@"//aa:/bb", false, false, @"/aa:/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb$b/cc$", false, false, @"/aa/bb$b/cc$", 12, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c", false, false, @"/aa/bb/cc$c", 11, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/..", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/dd", false, false, @"/aa/bb/cc$c/dd", 14, Result.Success},
|
||||
new object[] {@"//aa/bb", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", false, false, @"/aa/bb/cc/dd", 12, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/..", false, false, @"/aa/bb/cc", 9, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../..", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../../..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa/bb/cc\/dd", false, false, @"/aa/bb/cc\/dd", 13, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", false, false, @"/aa/bb/cc/dd", 12, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd", false, false, @"/aa/bb/cc/dd", 12, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/..", false, false, @"/aa/bb/cc", 9, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../..", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../../..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd", false, false, @"/aa/bb/cc/\dd", 13, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/..", false, false, @"/aa/bb/cc", 9, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../..", false, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../../..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa/../", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"//aa//", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa/bb..", false, false, @"/aa/bb..", 8, Result.Success},
|
||||
new object[] {@"//aa/bb../..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"//aa/bb../", false, false, @"/aa/bb..", 8, Result.Success},
|
||||
new object[] {@"//aa/bb..//..", false, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/..", false, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../..", false, true, @"/\\aa", 5, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../..", false, true, @"/", 1, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../../..", false, true, @"/\\aa/bb/cc/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"c:aa\bb/cc", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:aa\bb/cc/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:aa\bb/cc/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:aa\bb/cc/../../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:\//\aa\bb", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:\//\aa\bb/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount://////a/bb/c", false, true, @"mount:/a/bb/c", 13, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/..", false, true, @"mount:/a/bb", 11, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/../..", false, true, @"mount:/a", 8, Result.Success},
|
||||
new object[] {@"//", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"///..", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"//a", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/..", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"//a", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/..", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"//a/", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a//..", false, false, @"/", 1, Result.Success},
|
||||
new object[] {@"//a/b", false, false, @"/a/b", 4, Result.Success},
|
||||
new object[] {@"//a/b/..", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/b/", false, false, @"/a/b", 4, Result.Success},
|
||||
new object[] {@"//a/b//..", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/b/c", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"//a/b/c/..", false, false, @"/a/b", 4, Result.Success},
|
||||
new object[] {@"//a/b/c/../..", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/b/c/", false, false, @"/a/b/c", 6, Result.Success},
|
||||
new object[] {@"//a/b/c//..", false, false, @"/a/b", 4, Result.Success},
|
||||
new object[] {@"//a/b/c//../..", false, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"\\", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a//..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b//..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c//..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c//../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c\", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c\/..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c\/../..", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//$abc/bb", true, false, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//:abc/bb", true, false, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"\\\asd", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/asd", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\//asd", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//", true, false, @"/", 1, Result.Success},
|
||||
new object[] {@"///..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d", true, false, @"\\a/b/d", 7, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d/../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d/../../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"c:/aa/bb", true, true, @"c:/aa/bb", 8, Result.Success},
|
||||
new object[] {@"c:/aa/bb/..", true, true, @"c:/aa", 5, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../..", true, true, @"c:/", 3, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../../..", true, true, @"c:/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:\c:/aa", true, true, @"mount:\c:/aa", 12, Result.Success},
|
||||
new object[] {@"mount:\c:/aa/..", true, true, @"mount:\c:", 9, Result.Success},
|
||||
new object[] {@"mount:\c:/aa/../..", true, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:\c:/aa/../../..", true, true, @"mount:/c:/aa/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/c:\aa/bb", true, true, @"mount:/c:\aa/bb", 15, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/..", true, true, @"mount:/c:\aa", 12, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../..", true, true, @"mount:/c:", 9, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../../..", true, true, @"mount:/c:", 9, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb", true, true, @"mount:////c:\aa/bb", 18, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/..", true, true, @"mount:////c:\aa", 15, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../..", true, true, @"mount:////c:", 12, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../../..", true, true, @"mount:////c:", 12, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb", true, true, @"mount:/\aa/bb", 13, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/..", true, true, @"mount:/\aa", 10, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../..", true, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../../..", true, true, @"mount:/\aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount:/c:/aa/bb", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", true, true, @"mount:c:/aa/bb", 14, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b", true, true, @"mount:/b", 8, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/..", true, true, @"mount:/", 7, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/../..", true, true, @"mount:/b/a/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"mount://aa/bb", true, true, @"mount:/\\aa/bb", 14, Result.Success},
|
||||
new object[] {@"mount://aa/bb/..", true, true, @"mount:/\\aa/bb", 14, Result.Success},
|
||||
new object[] {@"//aa/bb", true, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"//aa/bb/..", true, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"//aa/bb", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"/aa/bb", true, false, @"/aa/bb", 6, Result.Success},
|
||||
new object[] {@"/aa/bb/..", true, false, @"/aa", 3, Result.Success},
|
||||
new object[] {@"/aa/bb/../..", true, false, @"/", 1, Result.Success},
|
||||
new object[] {@"/aa/bb/../../..", true, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value},
|
||||
new object[] {@"c:/aa", true, false, @"c:/aa", 5, Result.Success},
|
||||
new object[] {@"c:/aa/..", true, false, @"c:", 2, Result.Success},
|
||||
new object[] {@"c:/aa/../..", true, false, @"c:", 2, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb", true, false, @"c:abcde/aa/bb", 13, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb/..", true, false, @"c:abcde/aa", 10, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb/../..", true, false, @"c:abcde", 7, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb/../../..", true, false, @"c:abcde", 7, Result.Success},
|
||||
new object[] {@"c:abcde", true, false, @"c:abcde", 7, Result.Success},
|
||||
new object[] {@"c:abcde/..", true, false, @"c:abcde", 7, Result.Success},
|
||||
new object[] {@"c:abcde/", true, false, @"c:abcde", 7, Result.Success},
|
||||
new object[] {@"///aa", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa//bb", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa//bb/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//./bb", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//../bb", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//.../bb", true, false, @"\\.../bb", 8, Result.Success},
|
||||
new object[] {@"//aa$abc/bb", true, false, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//aa$/bb", true, false, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//aa:/bb", true, false, @"", 0, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//aa/bb$b/cc$", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa/bb/cc$c", true, false, @"\\aa/bb/cc$c", 12, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/dd", true, false, @"\\aa/bb/cc$c/dd", 15, Result.Success},
|
||||
new object[] {@"//aa/bb", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", true, false, @"\\aa/bb/cc/dd", 13, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/..", true, false, @"\\aa/bb/cc", 10, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../../..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc\/dd", true, false, @"\\aa/bb/cc\/dd", 14, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", true, false, @"\\aa/bb/cc/dd", 13, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd", true, false, @"\\aa/bb/cc/dd", 13, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/..", true, false, @"\\aa/bb/cc", 10, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../../..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd", true, false, @"\\aa/bb/cc/\dd", 14, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/..", true, false, @"\\aa/bb/cc", 10, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../../..", true, false, @"\\aa/bb", 7, Result.Success},
|
||||
new object[] {@"//aa/../", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa//", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa/bb..", true, false, @"\\aa/bb..", 9, Result.Success},
|
||||
new object[] {@"//aa/bb../..", true, false, @"\\aa/bb..", 9, Result.Success},
|
||||
new object[] {@"//aa/bb../", true, false, @"\\aa/bb..", 9, Result.Success},
|
||||
new object[] {@"//aa/bb..//..", true, false, @"\\aa/bb..", 9, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/..", true, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../..", true, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../..", true, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../../..", true, true, @"/\\aa/bb", 8, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc", true, false, @"c:aa\bb/cc", 10, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc/..", true, false, @"c:aa\bb", 7, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc/../..", true, false, @"c:aa", 4, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc/../../..", true, false, @"c:aa", 4, Result.Success},
|
||||
new object[] {@"c:\//\aa\bb", true, false, @"c:\/\aa\bb", 10, Result.Success},
|
||||
new object[] {@"c:\//\aa\bb/..", true, false, @"c:\", 3, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c", true, true, @"mount:/\\a/bb/c", 15, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/..", true, true, @"mount:/\\a/bb", 13, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/../..", true, true, @"mount:/\\a/bb", 13, Result.Success},
|
||||
new object[] {@"//", true, false, @"/", 1, Result.Success},
|
||||
new object[] {@"///..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a", true, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a", true, false, @"/a", 2, Result.Success},
|
||||
new object[] {@"//a/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a/", true, false, @"\\a/", 4, Result.Success},
|
||||
new object[] {@"//a//..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a/b", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b/", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b//..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b/c", true, false, @"\\a/b/c", 7, Result.Success},
|
||||
new object[] {@"//a/b/c/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b/c/../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b/c/", true, false, @"\\a/b/c", 7, Result.Success},
|
||||
new object[] {@"//a/b/c//..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"//a/b/c//../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\", true, false, @"\\", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", true, false, @"\\", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/", true, false, @"\\a/", 4, Result.Success},
|
||||
new object[] {@"\\a//..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b//..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/c", true, false, @"\\a/b/c", 7, Result.Success},
|
||||
new object[] {@"\\a/b/c/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/c/../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/c/", true, false, @"\\a/b/c", 7, Result.Success},
|
||||
new object[] {@"\\a/b/c//..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a/b/c//../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\", true, false, @"\\", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", true, false, @"\\", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\", true, false, @"\\a/", 4, Result.Success},
|
||||
new object[] {@"\\a\/..", true, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a\b/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a\b\", true, false, @"\\a/b\", 6, Result.Success},
|
||||
new object[] {@"\\a\b\/..", true, false, @"\\a", 3, Result.Success},
|
||||
new object[] {@"\\a\b\c", true, false, @"\\a/b\c", 7, Result.Success},
|
||||
new object[] {@"\\a\b\c/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a\b\c/../..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a\b\c\", true, false, @"\\a/b\c\", 8, Result.Success},
|
||||
new object[] {@"\\a\b\c\/..", true, false, @"\\a/b", 5, Result.Success},
|
||||
new object[] {@"\\a\b\c\/../..", true, false, @"\\a/b", 5, Result.Success}
|
||||
};
|
||||
|
||||
public static object[][] IsNormalizedTestItems =
|
||||
{
|
||||
new object[] {@"", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"/", false, false, true, Result.Success},
|
||||
new object[] {@"/.", false, false, false, Result.Success},
|
||||
new object[] {@"/a/b/c", false, false, true, Result.Success},
|
||||
new object[] {@"/a/b/../c", false, false, false, Result.Success},
|
||||
new object[] {@"/a/b/c/..", false, false, false, Result.Success},
|
||||
new object[] {@"/a/b/c/.", false, false, false, Result.Success},
|
||||
new object[] {@"/a/../../..", false, false, false, Result.Success},
|
||||
new object[] {@"/a/../../../a/b/c", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b//.//c", false, false, false, Result.Success},
|
||||
new object[] {@"/../a/b/c/.", false, false, false, Result.Success},
|
||||
new object[] {@"/./aaa/bbb/ccc/.", false, false, false, Result.Success},
|
||||
new object[] {@"/a/b/c/", false, false, false, Result.Success},
|
||||
new object[] {@"a/b/c/", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"/aa/./bb/../cc/", false, false, false, Result.Success},
|
||||
new object[] {@"/./b/../c/", false, false, false, Result.Success},
|
||||
new object[] {@"/a/../../../", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b//.//c/", false, false, false, Result.Success},
|
||||
new object[] {@"/tmp/../", false, false, false, Result.Success},
|
||||
new object[] {@"a", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"a/../../../a/b/c", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"./b/../c/", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@".", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"../a/b/c/.", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"./a/b/c/.", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"abc", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a/b/../c", false, true, false, Result.Success},
|
||||
new object[] {@"a:/a/b/c", false, true, true, Result.Success},
|
||||
new object[] {@"mount:/a/b/../c", false, true, false, Result.Success},
|
||||
new object[] {@"mount:\a/b/../c", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\a/b\../c", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\a/b/c", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a\../b\..c", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/a\../b/..cd", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/a\..d/b/c\..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"abc:/a/../../../a/b/c", false, true, false, Result.Success},
|
||||
new object[] {@"abc:/./b/../c/", false, true, false, Result.Success},
|
||||
new object[] {@"abc:/.", false, true, false, Result.Success},
|
||||
new object[] {@"abc:/..", false, true, false, Result.Success},
|
||||
new object[] {@"abc:/", false, true, true, Result.Success},
|
||||
new object[] {@"abc://a/b//.//c", false, true, false, Result.Success},
|
||||
new object[] {@"abc:/././/././a/b//.//c", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/d./aa", false, true, true, Result.Success},
|
||||
new object[] {@"mount:/d/..", false, true, false, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb\..\h/ddd", false, false, false, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb/../h/ddd", false, false, false, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb\.\h/ddd", false, false, true, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb\./h/ddd", false, false, true, Result.Success},
|
||||
new object[] {@"/path/aaa/bbb/./h/ddd", false, false, false, Result.Success},
|
||||
new object[] {@"mount:abcd", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/", false, true, true, Result.Success},
|
||||
new object[] {@"mount:\..", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a/b\..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/dir", false, true, true, Result.Success},
|
||||
new object[] {@"mount:/dir/", false, true, false, Result.Success},
|
||||
new object[] {@"mount:\", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mo.unt:\", false, true, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"mount.:\", false, true, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"mount:./aa/bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:.../aa/bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:...aa/bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"...aa/bb", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount01234567890/aa/bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount01234567890:/aa/bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount0123456789:/aa/bb", false, true, true, Result.Success},
|
||||
new object[] {@"mount012345678:/aa/bb", false, true, true, Result.Success},
|
||||
new object[] {@"mount:aa/..\bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:..\bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/..\bb", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/.\bb", false, true, true, Result.Success},
|
||||
new object[] {@"mount:\..\bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\.\bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a\..\bb", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/a\.\bb", false, true, true, Result.Success},
|
||||
new object[] {@"//$abc/bb", false, false, false, Result.Success},
|
||||
new object[] {@"//:abc/bb", false, false, false, Result.Success},
|
||||
new object[] {@"\\\asd", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/asd", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\//asd", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//", false, false, false, Result.Success},
|
||||
new object[] {@"///..", false, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d/../../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:/aa/bb", false, true, true, Result.Success},
|
||||
new object[] {@"c:/aa/bb/..", false, true, false, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../..", false, true, false, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:\c:/aa", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\c:/aa/..", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\c:/aa/../..", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\c:/aa/../../..", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/c:\aa/bb", false, true, true, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb", false, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb", false, true, true, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/c:/aa/bb", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/\aa/../b", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/..", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/../..", false, true, false, Result.Success},
|
||||
new object[] {@"mount://aa/bb", false, true, false, Result.Success},
|
||||
new object[] {@"mount://aa/bb/..", false, true, false, Result.Success},
|
||||
new object[] {@"//aa/bb", false, true, false, Result.Success},
|
||||
new object[] {@"//aa/bb/..", false, true, false, Result.Success},
|
||||
new object[] {@"//aa/bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/..", false, false, false, Result.Success},
|
||||
new object[] {@"/aa/bb", false, false, true, Result.Success},
|
||||
new object[] {@"/aa/bb/..", false, false, false, Result.Success},
|
||||
new object[] {@"/aa/bb/../..", false, false, false, Result.Success},
|
||||
new object[] {@"/aa/bb/../../..", false, false, false, Result.Success},
|
||||
new object[] {@"c:/aa", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:/aa/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:/aa/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/aa/bb/../../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:abcde/", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"///aa", false, false, false, Result.Success},
|
||||
new object[] {@"//aa//bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa//bb/..", false, false, false, Result.Success},
|
||||
new object[] {@"//./bb", false, false, false, Result.Success},
|
||||
new object[] {@"//../bb", false, false, false, Result.Success},
|
||||
new object[] {@"//.../bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa$abc/bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa$/bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa:/bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb$b/cc$", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/dd", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc\/dd", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/../", false, false, false, Result.Success},
|
||||
new object[] {@"//aa//", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb../..", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb../", false, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb..//..", false, false, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/..", false, true, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../..", false, true, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../..", false, true, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../../..", false, true, false, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:aa\bb/cc/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:aa\bb/cc/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:aa\bb/cc/../../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:\//\aa\bb", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"c:\//\aa\bb/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount://////a/bb/c", false, true, false, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/..", false, true, false, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/../..", false, true, false, Result.Success},
|
||||
new object[] {@"//", false, false, false, Result.Success},
|
||||
new object[] {@"///..", false, false, false, Result.Success},
|
||||
new object[] {@"//a", false, false, false, Result.Success},
|
||||
new object[] {@"//a/..", false, false, false, Result.Success},
|
||||
new object[] {@"//a", false, false, false, Result.Success},
|
||||
new object[] {@"//a/..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/", false, false, false, Result.Success},
|
||||
new object[] {@"//a//..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b//..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c/..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c/../..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c/", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c//..", false, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c//../..", false, false, false, Result.Success},
|
||||
new object[] {@"\\", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a//..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b//..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c/", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c//..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/c//../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c\", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c\/..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\b\c\/../..", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//$abc/bb", true, false, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//:abc/bb", true, false, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"\\\asd", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/asd", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\//asd", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//", true, false, false, Result.Success},
|
||||
new object[] {@"///..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b/cc/../d", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d/../..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/cc/../d/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:/aa/bb", true, true, true, Result.Success},
|
||||
new object[] {@"c:/aa/bb/..", true, true, false, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../..", true, true, false, Result.Success},
|
||||
new object[] {@"c:/aa/bb/../../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:\c:/aa", true, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\c:/aa/..", true, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\c:/aa/../..", true, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:\c:/aa/../../..", true, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/c:\aa/bb", true, true, true, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/c:\aa/bb/../../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb", true, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:////c:\aa/bb/../../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb", true, true, true, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/bb/../../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/c:/aa/bb", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:c:/aa/bb", true, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/\aa/../b", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/..", true, true, false, Result.Success},
|
||||
new object[] {@"mount:/\aa/../b/../..", true, true, false, Result.Success},
|
||||
new object[] {@"mount://aa/bb", true, true, false, Result.Success},
|
||||
new object[] {@"mount://aa/bb/..", true, true, false, Result.Success},
|
||||
new object[] {@"//aa/bb", true, true, false, Result.Success},
|
||||
new object[] {@"//aa/bb/..", true, true, false, Result.Success},
|
||||
new object[] {@"//aa/bb", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/..", true, false, false, Result.Success},
|
||||
new object[] {@"/aa/bb", true, false, true, Result.Success},
|
||||
new object[] {@"/aa/bb/..", true, false, false, Result.Success},
|
||||
new object[] {@"/aa/bb/../..", true, false, false, Result.Success},
|
||||
new object[] {@"/aa/bb/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:/aa", true, false, true, Result.Success},
|
||||
new object[] {@"c:/aa/..", true, false, false, Result.Success},
|
||||
new object[] {@"c:/aa/../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb", true, false, true, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb/..", true, false, false, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb/../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:abcde/aa/bb/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:abcde", true, false, true, Result.Success},
|
||||
new object[] {@"c:abcde/..", true, false, false, Result.Success},
|
||||
new object[] {@"c:abcde/", true, false, false, Result.Success},
|
||||
new object[] {@"///aa", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa//bb", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa//bb/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//./bb", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//../bb", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//.../bb", true, false, false, Result.Success},
|
||||
new object[] {@"//aa$abc/bb", true, false, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//aa$/bb", true, false, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//aa:/bb", true, false, false, ResultFs.InvalidCharacter.Value},
|
||||
new object[] {@"//aa/bb$b/cc$", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa/bb/cc$c", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc$c/dd", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc\/dd", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc//dd", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/dd/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb/cc/\dd/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/../", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa//", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//aa/bb..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb../..", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb../", true, false, false, Result.Success},
|
||||
new object[] {@"//aa/bb..//..", true, false, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/..", true, true, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../..", true, true, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../..", true, true, false, Result.Success},
|
||||
new object[] {@"/\\aa/bb/cc/../../../..", true, true, false, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc", true, false, true, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc/..", true, false, false, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc/../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:aa\bb/cc/../../..", true, false, false, Result.Success},
|
||||
new object[] {@"c:\//\aa\bb", true, false, false, Result.Success},
|
||||
new object[] {@"c:\//\aa\bb/..", true, false, false, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c", true, true, false, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/..", true, true, false, Result.Success},
|
||||
new object[] {@"mount://////a/bb/c/../..", true, true, false, Result.Success},
|
||||
new object[] {@"//", true, false, false, Result.Success},
|
||||
new object[] {@"///..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a", true, false, false, Result.Success},
|
||||
new object[] {@"//a/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a", true, false, false, Result.Success},
|
||||
new object[] {@"//a/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a/", true, false, false, Result.Success},
|
||||
new object[] {@"//a//..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"//a/b", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/..", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b//..", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c/..", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c/../..", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c/", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c//..", true, false, false, Result.Success},
|
||||
new object[] {@"//a/b/c//../..", true, false, false, Result.Success},
|
||||
new object[] {@"\\", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/", true, false, true, Result.Success},
|
||||
new object[] {@"\\a//..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/b", true, false, true, Result.Success},
|
||||
new object[] {@"\\a/b/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b//..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/c", true, false, true, Result.Success},
|
||||
new object[] {@"\\a/b/c/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/c/../..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/c/", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/c//..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a/b/c//../..", true, false, false, Result.Success},
|
||||
new object[] {@"\\", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a/..", true, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"\\a\", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\c", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\c/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\c/../..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\c\", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\c\/..", true, false, false, Result.Success},
|
||||
new object[] {@"\\a\b\c\/../..", true, false, false, Result.Success}
|
||||
};
|
||||
}
|
||||
}
|
30
tests/LibHac.Tests/LibHacTestFramework.cs
Normal file
30
tests/LibHac.Tests/LibHacTestFramework.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Diagnostics;
|
||||
using LibHac.Tests;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
[assembly: Xunit.TestFramework("LibHac.Tests." + nameof(LibHacTestFramework), "LibHac.Tests")]
|
||||
|
||||
namespace LibHac.Tests
|
||||
{
|
||||
public class LibHacTestFramework : XunitTestFramework
|
||||
{
|
||||
public LibHacTestFramework(IMessageSink messageSink)
|
||||
: base(messageSink)
|
||||
{
|
||||
SetDebugHandler();
|
||||
SetResultNames();
|
||||
}
|
||||
|
||||
private static void SetDebugHandler()
|
||||
{
|
||||
Trace.Listeners.Clear();
|
||||
Trace.Listeners.Add(new DebugAssertHandler());
|
||||
}
|
||||
|
||||
private static void SetResultNames()
|
||||
{
|
||||
Result.SetNameResolver(new ResultNameResolver());
|
||||
}
|
||||
}
|
||||
}
|
36
tests/LibHac.Tests/ResultNameResolver.cs
Normal file
36
tests/LibHac.Tests/ResultNameResolver.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LibHac.Tests
|
||||
{
|
||||
internal class ResultNameResolver : Result.IResultNameResolver
|
||||
{
|
||||
private Lazy<Dictionary<Result, string>> ResultNames { get; } = new Lazy<Dictionary<Result, string>>(GetResultNames);
|
||||
|
||||
public bool TryResolveName(Result result, out string name)
|
||||
{
|
||||
return ResultNames.Value.TryGetValue(result, out name);
|
||||
}
|
||||
|
||||
private static Dictionary<Result, string> GetResultNames()
|
||||
{
|
||||
var dict = new Dictionary<Result, string>();
|
||||
|
||||
Assembly assembly = typeof(Result).Assembly;
|
||||
|
||||
foreach (TypeInfo type in assembly.DefinedTypes.Where(x => x.Name.Contains("Result")))
|
||||
foreach (PropertyInfo property in type.DeclaredProperties
|
||||
.Where(x => x.PropertyType == typeof(Result.Base) && x.GetMethod.IsStatic && x.SetMethod == null))
|
||||
{
|
||||
Result value = ((Result.Base)property.GetValue(null, null)).Value;
|
||||
string name = $"{type.Name}{property.Name}";
|
||||
|
||||
dict[value] = name;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user