mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 10:24:58 +02:00
278 lines
8.0 KiB
C
278 lines
8.0 KiB
C
#include "common/path.h"
|
|
#include "common/io.h"
|
|
#include "common/arrutil.h"
|
|
|
|
#if !_WIN32
|
|
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
|
|
char* path = getenv("PATH");
|
|
if (!path) {
|
|
return "$PATH not set";
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
const bool appendExe = !ffStrEndsWithIgnCase(name, ".exe");
|
|
#endif
|
|
|
|
for (char* token = path; *token; path = token + 1) {
|
|
token = strchr(path,
|
|
#ifdef _WIN32
|
|
';'
|
|
#else
|
|
':'
|
|
#endif
|
|
);
|
|
if (!token) {
|
|
token = path + strlen(path);
|
|
}
|
|
|
|
ffStrbufSetNS(result, (uint32_t) (token - path), path);
|
|
ffStrbufEnsureEndsWithC(result,
|
|
#ifdef _WIN32
|
|
'\\'
|
|
#else
|
|
'/'
|
|
#endif
|
|
);
|
|
ffStrbufAppendS(result, name);
|
|
#ifdef _WIN32
|
|
if (appendExe) {
|
|
ffStrbufAppendS(result, ".exe");
|
|
}
|
|
if (!ffPathExists(result->chars, FF_PATHTYPE_FILE)) {
|
|
continue;
|
|
}
|
|
#else
|
|
if (access(result->chars, X_OK) != 0) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
ffStrbufClear(result);
|
|
return "Executable not found";
|
|
}
|
|
#else
|
|
#include <windows.h>
|
|
#include <winioctl.h>
|
|
#include <errno.h>
|
|
#include <stdalign.h>
|
|
|
|
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
|
|
char buffer[MAX_PATH + 1];
|
|
DWORD length = SearchPathA(nullptr, name, ".exe", sizeof(buffer), buffer, nullptr);
|
|
if (length == 0) {
|
|
ffStrbufClear(result);
|
|
return "Executable not found";
|
|
}
|
|
ffStrbufSetS(result, buffer);
|
|
return nullptr;
|
|
}
|
|
|
|
static inline int winerr2Errno(DWORD err) {
|
|
switch (err) {
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case ERROR_INVALID_NAME:
|
|
return ENOENT;
|
|
case ERROR_ACCESS_DENIED:
|
|
case ERROR_SHARING_VIOLATION:
|
|
case ERROR_LOCK_VIOLATION:
|
|
return EACCES;
|
|
case ERROR_BUFFER_OVERFLOW:
|
|
case ERROR_INSUFFICIENT_BUFFER:
|
|
return ENAMETOOLONG;
|
|
case ERROR_INVALID_PARAMETER:
|
|
case ERROR_NOT_A_REPARSE_POINT:
|
|
return EINVAL;
|
|
default:
|
|
return EIO;
|
|
}
|
|
}
|
|
|
|
char* frealpath(HANDLE hFile, char* resolved_name) {
|
|
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false)) {
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
|
|
wchar_t resolvedNameW[MAX_PATH + 4]; /* +4 for "\\\\?\\" prefix */
|
|
DWORD lenW = GetFinalPathNameByHandleW(hFile, resolvedNameW, (DWORD) ARRAY_SIZE(resolvedNameW), FILE_NAME_NORMALIZED);
|
|
|
|
if (lenW == 0) {
|
|
errno = winerr2Errno(GetLastError());
|
|
return nullptr;
|
|
}
|
|
if (lenW >= ARRAY_SIZE(resolvedNameW)) {
|
|
errno = E2BIG;
|
|
return nullptr;
|
|
}
|
|
lenW++; // Include null terminator
|
|
|
|
wchar_t* srcW = resolvedNameW;
|
|
DWORD srcLenW = lenW;
|
|
|
|
if (srcLenW >= 8 && wcsncmp(resolvedNameW, L"\\\\?\\UNC\\", 8) == 0) {
|
|
/* Convert "\\?\UNC\server\share" to "\\server\share" */
|
|
srcW += 6;
|
|
srcLenW -= 6;
|
|
*srcW = L'\\';
|
|
} else if (srcLenW >= 4 && wcsncmp(resolvedNameW, L"\\\\?\\", 4) == 0) {
|
|
srcW += 4;
|
|
srcLenW -= 4;
|
|
}
|
|
|
|
if (resolved_name) {
|
|
ULONG outBytes = 0;
|
|
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG) (srcLenW * sizeof(wchar_t))))) {
|
|
errno = E2BIG;
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
/* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
|
|
char tmp[(MAX_PATH + 4) * 4];
|
|
ULONG outBytes = 0;
|
|
|
|
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG) sizeof(tmp), &outBytes, srcW, (ULONG) (srcLenW * sizeof(wchar_t))))) {
|
|
errno = E2BIG;
|
|
return nullptr;
|
|
}
|
|
|
|
resolved_name = (char*) malloc(outBytes);
|
|
if (!resolved_name) {
|
|
errno = ENOMEM;
|
|
return nullptr;
|
|
}
|
|
|
|
memcpy(resolved_name, tmp, outBytes);
|
|
}
|
|
|
|
return resolved_name;
|
|
}
|
|
|
|
char* realpath(const char* __restrict file_name, char* __restrict resolved_name) {
|
|
if (!file_name) {
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
|
|
wchar_t fileNameW[MAX_PATH];
|
|
ULONG lenBytes = 0;
|
|
|
|
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &lenBytes, file_name, (ULONG) strlen(file_name) + 1))) {
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
|
|
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
|
|
fileNameW,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
nullptr);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
errno = winerr2Errno(GetLastError());
|
|
return nullptr;
|
|
}
|
|
|
|
return frealpath(hFile, resolved_name);
|
|
}
|
|
|
|
ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz) {
|
|
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0, false)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
alignas(REPARSE_DATA_BUFFER) BYTE reparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
|
DWORD bytesReturned = 0;
|
|
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, nullptr)) {
|
|
errno = winerr2Errno(GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
REPARSE_DATA_BUFFER* rp = (REPARSE_DATA_BUFFER*) reparseBuf;
|
|
const wchar_t* targetW = nullptr;
|
|
USHORT targetBytes = 0;
|
|
|
|
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
|
|
if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 0) {
|
|
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
|
(rp->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
|
targetBytes = rp->SymbolicLinkReparseBuffer.PrintNameLength;
|
|
} else {
|
|
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
|
(rp->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
|
targetBytes = rp->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
|
|
|
if (targetBytes >= 8 &&
|
|
wcsncmp(targetW, L"\\??\\", 4) == 0) {
|
|
targetW += 4;
|
|
targetBytes -= 8;
|
|
}
|
|
}
|
|
} else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
|
|
if (rp->MountPointReparseBuffer.PrintNameLength > 0) {
|
|
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
|
(rp->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
|
targetBytes = rp->MountPointReparseBuffer.PrintNameLength;
|
|
} else {
|
|
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
|
(rp->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
|
targetBytes = rp->MountPointReparseBuffer.SubstituteNameLength;
|
|
|
|
if (targetBytes >= 8 &&
|
|
wcsncmp(targetW, L"\\??\\", 4) == 0) {
|
|
targetW += 4;
|
|
targetBytes -= 8;
|
|
}
|
|
}
|
|
} else {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
ULONG outBytes = 0;
|
|
if (!NT_SUCCESS(RtlUnicodeToUTF8N(buf, (ULONG) bufsiz, &outBytes, targetW, targetBytes))) {
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
// Not null-terminated
|
|
return (ssize_t) outBytes;
|
|
}
|
|
|
|
ssize_t readlink(const char* path, char* buf, size_t bufsiz) {
|
|
if (!path || !buf || bufsiz == 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
wchar_t pathW[MAX_PATH];
|
|
ULONG pathWBytes = 0;
|
|
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), &pathWBytes, path, (ULONG) strlen(path) + 1))) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
|
|
pathW,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
|
nullptr);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
errno = winerr2Errno(GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
return freadlink(hFile, buf, bufsiz);
|
|
}
|
|
#endif
|