2020-10-09 09:32:10 +03:00
|
|
|
using System;
|
2016-04-21 16:40:25 +03:00
|
|
|
using System.IO;
|
|
|
|
using System.Runtime.InteropServices;
|
2021-05-21 23:29:08 +03:00
|
|
|
using System.Text;
|
2016-04-21 16:40:25 +03:00
|
|
|
|
2020-10-09 09:32:10 +03:00
|
|
|
namespace Xamarin.Utils
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
public static class PathUtils
|
|
|
|
{
|
|
|
|
static bool IsSeparator (char c)
|
|
|
|
{
|
|
|
|
return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar || c == Path.VolumeSeparatorChar;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char ToOrdinalIgnoreCase (char c)
|
|
|
|
{
|
|
|
|
return (((uint) c - 'a') <= ((uint) 'z' - 'a')) ? (char) (c - 0x20) : c;
|
|
|
|
}
|
|
|
|
|
|
|
|
[DllImport ("/usr/lib/libc.dylib")]
|
|
|
|
static extern IntPtr realpath (string path, IntPtr buffer);
|
|
|
|
|
2016-10-10 11:24:41 +03:00
|
|
|
public static string ResolveSymbolicLinks (string path)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
2016-10-10 11:24:41 +03:00
|
|
|
if (string.IsNullOrEmpty (path))
|
|
|
|
return path;
|
|
|
|
|
2016-04-21 16:40:25 +03:00
|
|
|
if (Path.DirectorySeparatorChar == '\\')
|
|
|
|
return Path.GetFullPath (path);
|
|
|
|
|
|
|
|
const int PATHMAX = 4096 + 1;
|
2016-10-10 11:24:41 +03:00
|
|
|
var buffer = IntPtr.Zero;
|
2016-04-21 16:40:25 +03:00
|
|
|
|
|
|
|
try {
|
|
|
|
buffer = Marshal.AllocHGlobal (PATHMAX);
|
|
|
|
var result = realpath (path, buffer);
|
2017-03-12 21:51:28 +03:00
|
|
|
return result == IntPtr.Zero ? path : Marshal.PtrToStringAuto (buffer);
|
2016-04-21 16:40:25 +03:00
|
|
|
} finally {
|
|
|
|
if (buffer != IntPtr.Zero)
|
|
|
|
Marshal.FreeHGlobal (buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string AbsoluteToRelative (string baseDirectory, string absolute)
|
|
|
|
{
|
2017-03-12 21:51:28 +03:00
|
|
|
if (string.IsNullOrEmpty (baseDirectory))
|
2016-04-21 16:40:25 +03:00
|
|
|
return absolute;
|
|
|
|
|
|
|
|
// canonicalize the paths
|
|
|
|
baseDirectory = Path.GetFullPath (baseDirectory).TrimEnd (Path.DirectorySeparatorChar);
|
|
|
|
absolute = Path.GetFullPath (absolute);
|
|
|
|
|
|
|
|
int baseDirectoryStartIndex = baseDirectory.Length;
|
|
|
|
int absoluteStartIndex = absolute.Length;
|
|
|
|
int separators = 0;
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
while (index < absolute.Length) {
|
|
|
|
if (ToOrdinalIgnoreCase (absolute[index]) != ToOrdinalIgnoreCase (baseDirectory[index]))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (IsSeparator (absolute[index])) {
|
|
|
|
baseDirectoryStartIndex = index;
|
|
|
|
absoluteStartIndex = index + 1;
|
|
|
|
separators++;
|
|
|
|
}
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (index >= baseDirectory.Length) {
|
|
|
|
if (index >= absolute.Length || IsSeparator (absolute[index])) {
|
|
|
|
baseDirectoryStartIndex = index;
|
|
|
|
absoluteStartIndex = index + 1;
|
|
|
|
separators++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (separators == 0)
|
|
|
|
return absolute;
|
|
|
|
|
|
|
|
if (absoluteStartIndex >= absolute.Length)
|
|
|
|
return ".";
|
|
|
|
|
|
|
|
if (index >= absolute.Length && IsSeparator (baseDirectory[index])) {
|
|
|
|
absoluteStartIndex = index + 1;
|
|
|
|
baseDirectoryStartIndex = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parentDirCount = 0;
|
|
|
|
while (baseDirectoryStartIndex < baseDirectory.Length) {
|
|
|
|
if (IsSeparator (baseDirectory[baseDirectoryStartIndex]))
|
|
|
|
parentDirCount++;
|
|
|
|
baseDirectoryStartIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
var size = (parentDirCount * 3) + (absolute.Length - absoluteStartIndex);
|
|
|
|
var result = new char [size];
|
|
|
|
index = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < parentDirCount; i++) {
|
|
|
|
result[index++] = '.';
|
|
|
|
result[index++] = '.';
|
|
|
|
result[index++] = Path.DirectorySeparatorChar;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (absoluteStartIndex < absolute.Length)
|
|
|
|
result[index++] = absolute[absoluteStartIndex++];
|
|
|
|
|
|
|
|
return new string (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string RelativeToAbsolute (string baseDirectory, string relative)
|
|
|
|
{
|
|
|
|
return Path.GetFullPath (Path.Combine (baseDirectory, relative));
|
|
|
|
}
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
[DllImport ("/usr/lib/libSystem.dylib", SetLastError = true)]
|
|
|
|
static extern int symlink (string path1, string path2);
|
|
|
|
|
|
|
|
public static bool Symlink (string target, string symlink)
|
|
|
|
{
|
|
|
|
return PathUtils.symlink (target, symlink) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void CreateSymlink (string symlink, string target)
|
|
|
|
{
|
|
|
|
FileDelete (symlink); // Delete any existing symlinks.
|
|
|
|
var rv = PathUtils.symlink (target, symlink);
|
|
|
|
if (rv != 0)
|
|
|
|
throw new Exception (string.Format ("Could not create the symlink '{0}': {1}", symlink, Marshal.GetLastWin32Error ()));
|
|
|
|
}
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
[DllImport ("/usr/lib/libSystem.dylib", SetLastError = true)]
|
|
|
|
static extern int readlink (string path, [Out] byte[] buffer, IntPtr len);
|
|
|
|
|
|
|
|
public static string GetSymlinkTarget (string path)
|
|
|
|
{
|
|
|
|
byte [] buffer = null;
|
|
|
|
int rv;
|
|
|
|
do {
|
|
|
|
buffer = new byte [(buffer?.Length ?? 0) + 1024];
|
|
|
|
rv = readlink (path, buffer, (IntPtr) (buffer.Length - 1));
|
|
|
|
} while (rv == buffer.Length - 1);
|
|
|
|
|
|
|
|
if (rv == -1)
|
|
|
|
throw new Exception (string.Format ("Could not readlink '{0}': {1}", path, Marshal.GetLastWin32Error ()));
|
|
|
|
|
|
|
|
return Encoding.UTF8.GetString (buffer, 0, rv);
|
|
|
|
}
|
|
|
|
|
2021-05-21 23:29:08 +03:00
|
|
|
[DllImport ("/usr/lib/libSystem.dylib")]
|
|
|
|
static extern int unlink (string pathname);
|
|
|
|
|
|
|
|
// File.Delete can't always delete symlinks (in particular if the symlink points to a file that doesn't exist).
|
|
|
|
public static void FileDelete (string file)
|
|
|
|
{
|
|
|
|
unlink (file);
|
|
|
|
// ignore any errors.
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec {
|
|
|
|
public IntPtr tv_sec;
|
|
|
|
public IntPtr tv_nsec;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is defined */
|
|
|
|
public uint st_dev;
|
|
|
|
public ushort st_mode;
|
|
|
|
public ushort st_nlink;
|
|
|
|
public ulong st_ino;
|
|
|
|
public uint st_uid;
|
|
|
|
public uint st_gid;
|
|
|
|
public uint st_rdev;
|
|
|
|
public timespec st_atimespec;
|
|
|
|
public timespec st_mtimespec;
|
|
|
|
public timespec st_ctimespec;
|
|
|
|
public timespec st_birthtimespec;
|
|
|
|
public ulong st_size;
|
|
|
|
public ulong st_blocks;
|
|
|
|
public uint st_blksize;
|
|
|
|
public uint st_flags;
|
|
|
|
public uint st_gen;
|
|
|
|
public uint st_lspare;
|
|
|
|
public ulong st_qspare_1;
|
|
|
|
public ulong st_qspare_2;
|
|
|
|
}
|
|
|
|
|
2021-08-16 17:21:21 +03:00
|
|
|
[DllImport ("/usr/lib/libc.dylib", EntryPoint = "lstat$INODE64", SetLastError = true)]
|
|
|
|
static extern int lstat_x64 (string file_name, out stat buf);
|
|
|
|
|
|
|
|
[DllImport ("/usr/lib/libc.dylib", EntryPoint = "lstat", SetLastError = true)]
|
|
|
|
static extern int lstat_arm64 (string file_name, out stat buf);
|
|
|
|
|
|
|
|
static int lstat (string path, out stat buf)
|
|
|
|
{
|
|
|
|
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) {
|
|
|
|
return lstat_arm64 (path, out buf);
|
|
|
|
} else {
|
|
|
|
return lstat_x64 (path, out buf);
|
|
|
|
}
|
|
|
|
}
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
public static bool IsSymlink (string file)
|
|
|
|
{
|
|
|
|
stat buf;
|
|
|
|
var rv = lstat (file, out buf);
|
|
|
|
if (rv != 0)
|
|
|
|
throw new Exception (string.Format ("Could not lstat '{0}': {1}", file, Marshal.GetLastWin32Error ()));
|
|
|
|
const int S_IFLNK = 40960;
|
|
|
|
return (buf.st_mode & S_IFLNK) == S_IFLNK;
|
|
|
|
}
|
2016-04-21 16:40:25 +03:00
|
|
|
}
|
|
|
|
}
|