mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
204 lines
6.1 KiB
C
204 lines
6.1 KiB
C
#include "packages.h"
|
|
#include "common/processing.h"
|
|
#include "common/strutil.h"
|
|
#include "common/path.h"
|
|
#include "common/windows/unicode.h"
|
|
#include "common/mallocHelper.h"
|
|
#include "common/io.h"
|
|
|
|
#include <stdalign.h>
|
|
#include <windows.h>
|
|
#include "common/windows/nt.h"
|
|
#include <ntstatus.h>
|
|
#include <shlobj.h>
|
|
|
|
static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore) {
|
|
FF_AUTO_CLOSE_FD HANDLE dfd = CreateFileA(searchPath, FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
if (dfd == INVALID_HANDLE_VALUE) {
|
|
return 0;
|
|
}
|
|
|
|
bool flag = ignore == NULL;
|
|
uint32_t counter = 0;
|
|
alignas(8) uint8_t buffer[64 * 1024];
|
|
BOOLEAN firstScan = TRUE;
|
|
|
|
size_t ignoreLen = ignore ? wcslen(ignore) : 0;
|
|
|
|
while (true) {
|
|
IO_STATUS_BLOCK ioStatus = {};
|
|
NTSTATUS status = NtQueryDirectoryFile(
|
|
dfd,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ioStatus,
|
|
buffer,
|
|
ARRAY_SIZE(buffer),
|
|
FileDirectoryInformation,
|
|
FALSE,
|
|
NULL,
|
|
firstScan);
|
|
firstScan = FALSE;
|
|
|
|
if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) {
|
|
break;
|
|
}
|
|
|
|
for (FILE_DIRECTORY_INFORMATION* entry = (FILE_DIRECTORY_INFORMATION*) buffer;
|
|
;
|
|
entry = (FILE_DIRECTORY_INFORMATION*) ((uint8_t*) entry + entry->NextEntryOffset)) {
|
|
if (!(entry->FileAttributes & type)) {
|
|
continue;
|
|
}
|
|
|
|
if (!flag &&
|
|
ignoreLen == entry->FileNameLength / sizeof(*entry->FileName) &&
|
|
_wcsnicmp(entry->FileName, ignore, ignoreLen) == 0) {
|
|
flag = true;
|
|
continue;
|
|
}
|
|
|
|
counter++;
|
|
|
|
if (entry->NextEntryOffset == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
break; // No next page
|
|
}
|
|
}
|
|
|
|
if (type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) {
|
|
counter -= 2; // accounting for . and ..
|
|
}
|
|
|
|
return counter;
|
|
}
|
|
|
|
static inline void wrapYyjsonFree(yyjson_doc** doc) {
|
|
assert(doc);
|
|
if (*doc) {
|
|
yyjson_doc_free(*doc);
|
|
}
|
|
}
|
|
|
|
static void detectScoop(FFPackagesResult* result) {
|
|
FF_STRBUF_AUTO_DESTROY scoopPath = ffStrbufCreateA(MAX_PATH + 3);
|
|
ffStrbufAppend(&scoopPath, &instance.state.platform.homeDir);
|
|
ffStrbufAppendS(&scoopPath, ".config/scoop/config.json");
|
|
|
|
yyjson_val* root = NULL;
|
|
|
|
yyjson_doc* FF_A_CLEANUP(wrapYyjsonFree) doc = yyjson_read_file(scoopPath.chars, 0, NULL, NULL);
|
|
if (doc) {
|
|
root = yyjson_doc_get_root(doc);
|
|
if (!yyjson_is_obj(root)) {
|
|
root = NULL;
|
|
}
|
|
}
|
|
|
|
{
|
|
ffStrbufClear(&scoopPath);
|
|
if (root) {
|
|
ffStrbufSetJsonVal(&scoopPath, yyjson_obj_get(root, "root_path"));
|
|
}
|
|
if (scoopPath.length == 0) {
|
|
ffStrbufSet(&scoopPath, &instance.state.platform.homeDir);
|
|
ffStrbufAppendS(&scoopPath, "/scoop");
|
|
}
|
|
ffStrbufAppendS(&scoopPath, "/apps/");
|
|
result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop");
|
|
}
|
|
|
|
{
|
|
ffStrbufClear(&scoopPath);
|
|
if (root) {
|
|
ffStrbufSetJsonVal(&scoopPath, yyjson_obj_get(root, "global_path"));
|
|
}
|
|
if (scoopPath.length == 0) {
|
|
PWSTR pPath = NULL;
|
|
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramData, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
|
ffStrbufSetWS(&scoopPath, pPath);
|
|
CoTaskMemFree(pPath);
|
|
}
|
|
ffStrbufAppendS(&scoopPath, "/scoop");
|
|
}
|
|
ffStrbufAppendS(&scoopPath, "/apps/");
|
|
result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop");
|
|
}
|
|
}
|
|
|
|
static void detectChoco(FF_A_UNUSED FFPackagesResult* result) {
|
|
const char* chocoInstall = getenv("ChocolateyInstall");
|
|
if (!chocoInstall || chocoInstall[0] == '\0') {
|
|
return;
|
|
}
|
|
|
|
char chocoPath[MAX_PATH + 3];
|
|
char* pend = ffStrCopy(chocoPath, chocoInstall, ARRAY_SIZE(chocoPath));
|
|
ffStrCopy(pend, "/lib/", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath));
|
|
result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, L"choco");
|
|
}
|
|
|
|
static void detectPacman(FFPackagesResult* result) {
|
|
const char* msystemPrefix = getenv("MSYSTEM_PREFIX");
|
|
if (!msystemPrefix) {
|
|
return;
|
|
}
|
|
|
|
// MSYS2
|
|
char pacmanPath[MAX_PATH + 3];
|
|
char* pend = ffStrCopy(pacmanPath, msystemPrefix, ARRAY_SIZE(pacmanPath));
|
|
ffStrCopy(pend, "/../var/lib/pacman/local/", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath));
|
|
result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY, NULL);
|
|
}
|
|
|
|
static void detectWinget(FFPackagesResult* result) {
|
|
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
|
if (ffProcessAppendStdOut(&buffer, (char*[]) {
|
|
"winget.exe",
|
|
"list",
|
|
"--disable-interactivity",
|
|
NULL,
|
|
})) {
|
|
return;
|
|
}
|
|
|
|
uint32_t index = ffStrbufFirstIndexS(&buffer, "--\r\n"); // Ignore garbage and table headers
|
|
if (index == buffer.length) {
|
|
return;
|
|
}
|
|
|
|
uint32_t count = 0;
|
|
for (
|
|
index += strlen("--\r\n");
|
|
(index = ffStrbufNextIndexC(&buffer, index, '\n')) < buffer.length;
|
|
++index) {
|
|
++count;
|
|
}
|
|
|
|
if (buffer.chars[buffer.length - 1] != '\n') { // count last line
|
|
++count;
|
|
}
|
|
|
|
result->winget = count;
|
|
}
|
|
|
|
void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options) {
|
|
if (FF_PACKAGES_IS_ENABLED(options, SCOOP)) {
|
|
detectScoop(result);
|
|
}
|
|
if (FF_PACKAGES_IS_ENABLED(options, CHOCO)) {
|
|
detectChoco(result);
|
|
}
|
|
if (FF_PACKAGES_IS_ENABLED(options, PACMAN)) {
|
|
detectPacman(result);
|
|
}
|
|
if (FF_PACKAGES_IS_ENABLED(options, WINGET)) {
|
|
detectWinget(result);
|
|
}
|
|
}
|