From af6b7dd81b6fe64334d9f2cda3a43aa07dcc5d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 8 Oct 2022 17:41:25 +0800 Subject: [PATCH] Packages: add support for Windows --- CMakeLists.txt | 3 + src/common/io.h | 2 + src/detection/packages/packages.h | 32 ++ src/detection/packages/packages_linux.c | 448 ++++++++++++++++++++ src/detection/packages/packages_windows.c | 41 ++ src/fastfetch.c | 3 +- src/modules/packages.c | 477 +--------------------- 7 files changed, 540 insertions(+), 466 deletions(-) create mode 100644 src/detection/packages/packages.h create mode 100644 src/detection/packages/packages_linux.c create mode 100644 src/detection/packages/packages_windows.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3877d1a81..ee36893a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,7 @@ if(LINUX OR APPLE OR ANDROID OR BSD) src/common/processing_linux.c src/detection/disk/disk.c src/detection/terminalShell/terminalShell_linux.c + src/detection/packages/packages_linux.c ) endif() @@ -337,6 +338,8 @@ if(WIN_MSYS) src/detection/font/font_windows.cpp src/detection/terminalShell/terminalShell_linux.c src/detection/terminalShell/terminalShell_windows.cpp + src/detection/packages/packages_linux.c + src/detection/packages/packages_windows.c src/util/windows/wmi.cpp # Shared diff --git a/src/common/io.h b/src/common/io.h index 35a70675f..a6ecbc5ac 100644 --- a/src/common/io.h +++ b/src/common/io.h @@ -3,6 +3,8 @@ #ifndef FF_INCLUDED_common_io #define FF_INCLUDED_common_io +#include "fastfetch.h" + #include //mode_t bool ffWriteFDBuffer(int fd, const FFstrbuf* content); diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h new file mode 100644 index 000000000..9ecfdb7e9 --- /dev/null +++ b/src/detection/packages/packages.h @@ -0,0 +1,32 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_packages_packages +#define FF_INCLUDED_detection_packages_packages + +#include "fastfetch.h" + +typedef struct FFPackageCounts +{ + uint32_t pacman; + uint32_t dpkg; + uint32_t rpm; + uint32_t emerge; + uint32_t xbps; + uint32_t nixSystem; + uint32_t nixDefault; + uint32_t apk; + uint32_t pkg; + uint32_t flatpak; + uint32_t snap; + uint32_t brew; + uint32_t port; + uint32_t scoop; + + FFstrbuf pacmanBranch; + + uint32_t nixUser; +} FFPackageCounts; + +void ffDetectPackages(FFinstance* instance, FFPackageCounts* counts); + +#endif diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c new file mode 100644 index 000000000..cf20d28dd --- /dev/null +++ b/src/detection/packages/packages_linux.c @@ -0,0 +1,448 @@ +#include "common/io.h" +#include "common/properties.h" +#include "common/settings.h" +#include "common/processing.h" +#include "common/parsing.h" +#include "detection/os/os.h" + +#include "packages.h" + +#include +#include +#include +#include + +static uint32_t getNumElements(const char* dirname, unsigned char type) +{ + DIR* dirp = opendir(dirname); + if(dirp == NULL) + return 0; + + uint32_t num_elements = 0; + + struct dirent *entry; + while((entry = readdir(dirp)) != NULL) { + if(entry->d_type == type) + ++num_elements; + } + + if(type == DT_DIR) + num_elements -= 2; // accounting for . and .. + + closedir(dirp); + + return num_elements; +} + +#ifndef __APPLE__ + +static uint32_t getNumStrings(const char* filename, const char* needle) +{ + FILE* file = fopen(filename, "r"); + if(file == NULL) + return 0; + + uint32_t count = 0; + + char* line = NULL; + size_t len = 0; + + while(getline(&line, &len, file) != EOF) + { + if(strstr(line, needle) != NULL) + ++count; + } + + if(line != NULL) + free(line); + + fclose(file); + + return count; +} + +#ifndef __ANDROID__ + +static uint32_t countFilesRecursive(FFstrbuf* baseDirPath, const char* filename) +{ + uint32_t baseDirPathLength = baseDirPath->length; + + ffStrbufAppendC(baseDirPath, '/'); + ffStrbufAppendS(baseDirPath, filename); + bool exists = ffFileExists(baseDirPath->chars, S_IFREG); + ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); + if(exists) + return 1; + + DIR* dirp = opendir(baseDirPath->chars); + if(dirp == NULL) + return 0; + + ffStrbufAppendC(baseDirPath, '/'); + baseDirPathLength = baseDirPath->length; + + uint32_t sum = 0; + + struct dirent *entry; + while((entry = readdir(dirp)) != NULL) { + // According to the PMS, neither category nor package name can begin with '.', so no need to check for . or .. specifically + if(entry->d_type != DT_DIR || entry->d_name[0] == '.') + continue; + + ffStrbufAppendS(baseDirPath, entry->d_name); + sum += countFilesRecursive(baseDirPath, filename); + ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); + } + + closedir(dirp); + return sum; +} + +static uint32_t getNixPackages(char* path) +{ + //Nix detection is kinda slow, so we only do it if the dir exists + if(!ffFileExists(path, S_IFDIR)) + return 0; + + FFstrbuf output; + ffStrbufInitA(&output, 128); + + //https://github.com/LinusDierheimer/fastfetch/issues/195#issuecomment-1191748222 + FFstrbuf command; + ffStrbufInitA(&command, 255); + ffStrbufAppendS(&command, "for x in $(nix-store --query --requisites "); + ffStrbufAppendS(&command, path); + ffStrbufAppendS(&command, "); do if [ -d $x ]; then echo $x ; fi ; done | cut -d- -f2- | egrep '([0-9]{1,}\\.)+[0-9]{1,}' | egrep -v '\\-doc$|\\-man$|\\-info$|\\-dev$|\\-bin$|^nixos-system-nixos-' | uniq"); + + ffProcessAppendStdOut(&output, (char* const[]) { + "sh", + "-c", + command.chars, + NULL + }); + + //Each package is a new line in the output. If at least one line is found, add 1 for the last line. + uint32_t result = ffStrbufCountC(&output, '\n'); + if(result > 0) + result++; + + ffStrbufDestroy(&output); + return result; +} + +static uint32_t getXBPS(FFstrbuf* baseDir) +{ + DIR* dir = opendir(baseDir->chars); + if(dir == NULL) + return 0; + + uint32_t result = 0; + + struct dirent *entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_type != DT_REG || strncasecmp(entry->d_name, "pkgdb-", 6) != 0) + continue; + + ffStrbufAppendC(baseDir, '/'); + ffStrbufAppendS(baseDir, entry->d_name); + result = getNumStrings(baseDir->chars, "installed"); + break; + } + + closedir(dir); + return result; +} + +#endif // !__ANDROID__ + +#else // !__APPLE__ + +static uint32_t countBrewPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + ffStrbufAppendS(baseDir, "/Caskroom"); + result += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + ffStrbufAppendS(baseDir, "/Cellar"); + result += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +static uint32_t getBrewPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + const char* prefix = getenv("HOMEBREW_PREFIX"); + bool prefixSet = ffStrSet(prefix); + + if(prefixSet) + { + ffStrbufAppendS(baseDir, prefix); + result += countBrewPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + } + + ffStrbufAppendS(baseDir, "/opt/homebrew"); + if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) + result += countBrewPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + ffStrbufAppendS(baseDir, "/usr/local"); + if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) + result += countBrewPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +static uint32_t countMacPortsPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + ffStrbufAppendS(baseDir, "/var/macports/software"); + result += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +static uint32_t getMacPortsPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + const char* prefix = getenv("MACPORTS_PREFIX"); + bool prefixSet = ffStrSet(prefix); + + if(prefixSet) + { + ffStrbufAppendS(baseDir, prefix); + result += countMacPortsPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + } + + ffStrbufAppendS(baseDir, "/opt/local"); + if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) + result += countMacPortsPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +#endif // __APPLE__ + +#ifdef FF_HAVE_RPM +#include "common/library.h" +#include +#include +#include +#include + +static uint32_t getRpmFromLibrpm(const FFinstance* instance) +{ + FF_LIBRARY_LOAD(rpm, &instance->config.librpm, 0, "librpm.so", 12) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0) + + // Don't print any error messages + ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG)); + + if(ffrpmReadConfigFiles(NULL, NULL) != 0) + { + dlclose(rpm); + return 0; + } + + rpmts ts = ffrpmtsCreate(); + if(ts == NULL) + { + dlclose(rpm); + return 0; + } + + rpmdbMatchIterator mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0); + if(mi == NULL) + { + ffrpmtsFree(ts); + dlclose(rpm); + return 0; + } + + int count = ffrpmdbGetIteratorCount(mi); + + ffrpmdbFreeIterator(mi); + ffrpmtsFree(ts); + dlclose(rpm); + + return count > 0 ? (uint32_t) count : 0; +} + +#endif //FF_HAVE_RPM + +static void getPackageCounts(const FFinstance* instance, FFstrbuf* baseDir, FFPackageCounts* packageCounts) +{ + #if defined(__APPLE__) || defined(__ANDROID__) + FF_UNUSED(instance); + #endif + + #ifndef __APPLE__ //Linux desktop and Android + + uint32_t baseDirLength = baseDir->length; + + //pacman + ffStrbufAppendS(baseDir, "/var/lib/pacman/local"); + packageCounts->pacman += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //dpkg + ffStrbufAppendS(baseDir, "/var/lib/dpkg/status"); + packageCounts->dpkg += getNumStrings(baseDir->chars, "Status: "); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + #ifndef __ANDROID__ //Linux Desktop + + //rpm + ffStrbufAppendS(baseDir, "/var/lib/rpm/rmpdb.sqlite"); + packageCounts->rpm += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(blob) FROM Packages"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //emerge + ffStrbufAppendS(baseDir, "/var/db/pkg"); + packageCounts->emerge += countFilesRecursive(baseDir, "SIZE"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //xps + ffStrbufAppendS(baseDir, "/var/db/xbps"); + packageCounts->xbps += getXBPS(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //nix system + ffStrbufAppendS(baseDir, "/run/current-system"); + packageCounts->nixSystem += getNixPackages(baseDir->chars); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //nix default + ffStrbufAppendS(baseDir, "/nix/var/nix/profiles/default"); + packageCounts->nixDefault += getNixPackages(baseDir->chars); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //apk + ffStrbufAppendS(baseDir, "/lib/apk/db/installed"); + packageCounts->apk += getNumStrings(baseDir->chars, "C:Q"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //flatpak + ffStrbufAppendS(baseDir, "/var/lib/flatpak/app"); + packageCounts->flatpak += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //snap + ffStrbufAppendS(baseDir, "/snap"); + uint32_t snap = getNumElements(baseDir->chars, DT_DIR); + if(snap > 0) + packageCounts->snap += (snap - 1); //Accounting for the /snap/bin folder + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //pacman branch + ffStrbufAppendS(baseDir, "/etc/pacman-mirrors.conf"); + if(ffParsePropFile(baseDir->chars, "Branch =", &packageCounts->pacmanBranch) && packageCounts->pacmanBranch.length == 0) + ffStrbufAppendS(&packageCounts->pacmanBranch, "stable"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + #endif // !__ANDROID__ + + #else // !__APPLE__ + + //brew + packageCounts->brew += getBrewPackages(baseDir); + packageCounts->port += getMacPortsPackages(baseDir); + + #endif // __APPLE__ + + #ifdef __FreeBSD__ + + ffStrbufAppendS(baseDir, "/var/db/pkg/local.sqlite"); + packageCounts->pkg += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(id) FROM packages"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + #endif // __FreeBSD__ +} + +static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDir, FFPackageCounts* packageCounts) +{ + uint32_t baseDirLength = baseDir->length; + + ffStrbufAppendS(baseDir, "/bedrock/strata"); + + DIR* dir = opendir(baseDir->chars); + if(dir == NULL) + { + ffStrbufSubstrBefore(baseDir, baseDirLength); + getPackageCounts(instance, baseDir, packageCounts); + return; + } + + ffStrbufAppendC(baseDir, '/'); + baseDirLength = baseDir->length; + + struct dirent* entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_type != DT_DIR) + continue; + + ffStrbufAppendS(baseDir, entry->d_name); + getPackageCounts(instance, baseDir, packageCounts); + ffStrbufSubstrBefore(baseDir, baseDirLength); + } + + closedir(dir); +} + +void +#ifdef __MSYS__ +ffDetectPackagesPosix +#else +ffDetectPackages +#endif +(FFinstance* instance, FFPackageCounts* counts) +{ + FFstrbuf baseDir; + ffStrbufInitA(&baseDir, 512); + ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT); + + if(ffStrbufIgnCaseCompS(&ffDetectOS(instance)->id, "bedrock") == 0) + getPackageCountsBedrock(instance, &baseDir, counts); + else + getPackageCounts(instance, &baseDir, counts); + + // If SQL failed, we can still try with librpm. + // This is needed on openSUSE, which seems to use a proprietary database file + // This method doesn't work on bedrock, so we do it here. + #ifdef FF_HAVE_RPM + if(counts->rpm == 0) + counts->rpm = getRpmFromLibrpm(instance); + #endif + + #if !defined(__ANDROID__) && !defined(__APPLE__) + //nix user + ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir); + ffStrbufAppendS(&baseDir, "/.nix-profile"); + counts->nixUser = getNixPackages(baseDir.chars); + #endif + + ffStrbufDestroy(&baseDir); +} diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c new file mode 100644 index 000000000..53d45d351 --- /dev/null +++ b/src/detection/packages/packages_windows.c @@ -0,0 +1,41 @@ +#include "packages.h" + +#include +#include + +static uint32_t getNumElements(const char* searchPath /* including `\*` suffix */, DWORD type) +{ + uint32_t counter = 0; + WIN32_FIND_DATAA wfd; + HANDLE hFind = FindFirstFileA(searchPath, &wfd); + + if (hFind != INVALID_HANDLE_VALUE) + { + do // Managed to locate and create an handle to that folder. + { + if(wfd.dwFileAttributes & type) + counter++; + } while (FindNextFileA(hFind, &wfd) == TRUE); + FindClose(hFind); + } + + return counter; +} + +#ifdef __MSYS__ + void ffDetectPackagesPosix(const FFinstance* instance, FFPackageCounts* counts); +#endif + +void ffDetectPackages(FFinstance* instance, FFPackageCounts* counts) +{ + #ifdef __MSYS__ + //We have pacman and maybe others in MSYS, but not package managers for Windows + if(getenv("MSYSTEM")) + return ffDetectPackagesPosix(instance, counts); + #endif + + FFstrbuf scoopPath; + ffStrbufInitF(&scoopPath, "%s/scoop/apps/*", getenv("USERPROFILE")); + counts->scoop = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY) - 3; // . .. scoop + ffStrbufDestroy(&scoopPath); +} diff --git a/src/fastfetch.c b/src/fastfetch.c index 35ce52507..6b1553a4b 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -124,7 +124,7 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "packages-format") == 0) { - constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (xbps), {8} (nix-system), {9} (nix-user), {10} (nix-default), {11} (apk), {12} (pkg), {13} (flatpak), {14} (snap), {15} (brew), {16} (port)", 16, + constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (xbps), {8} (nix-system), {9} (nix-user), {10} (nix-default), {11} (apk), {12} (pkg), {13} (flatpak), {14} (snap), {15} (brew), {16} (port), {17} (scoop)", 17, "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", @@ -141,6 +141,7 @@ static inline void printCommandHelp(const char* command) "Number of snap packages", "Number of brew packages", "Number of macports packages" + "Number of scoop packages" ); } else if(strcasecmp(command, "shell-format") == 0) diff --git a/src/modules/packages.c b/src/modules/packages.c index 5ddba446a..cacfa5cc5 100644 --- a/src/modules/packages.c +++ b/src/modules/packages.c @@ -1,473 +1,18 @@ #include "fastfetch.h" -#include "common/io.h" -#include "common/properties.h" #include "common/printing.h" -#include "common/settings.h" -#include "common/processing.h" -#include "common/parsing.h" -#include "detection/os/os.h" - -#include -#include -#include -#include +#include "detection/packages/packages.h" #define FF_PACKAGES_MODULE_NAME "Packages" -#define FF_PACKAGES_NUM_FORMAT_ARGS 16 +#define FF_PACKAGES_NUM_FORMAT_ARGS 17 -typedef struct PackageCounts -{ - uint32_t pacman; - uint32_t dpkg; - uint32_t rpm; - uint32_t emerge; - uint32_t xbps; - uint32_t nixSystem; - uint32_t nixDefault; - uint32_t apk; - uint32_t pkg; - uint32_t flatpak; - uint32_t snap; - uint32_t brew; - uint32_t port; - - FFstrbuf pacmanBranch; -} PackageCounts; - -static uint32_t getNumElements(const char* dirname, unsigned char type) -{ - DIR* dirp = opendir(dirname); - if(dirp == NULL) - return 0; - - uint32_t num_elements = 0; - - struct dirent *entry; - while((entry = readdir(dirp)) != NULL) { - if(entry->d_type == type) - ++num_elements; - } - - if(type == DT_DIR) - num_elements -= 2; // accounting for . and .. - - closedir(dirp); - - return num_elements; -} - -#ifndef __APPLE__ - -static uint32_t getNumStrings(const char* filename, const char* needle) -{ - FILE* file = fopen(filename, "r"); - if(file == NULL) - return 0; - - uint32_t count = 0; - - char* line = NULL; - size_t len = 0; - - while(getline(&line, &len, file) != EOF) - { - if(strstr(line, needle) != NULL) - ++count; - } - - if(line != NULL) - free(line); - - fclose(file); - - return count; -} - -#ifndef __ANDROID__ - -static uint32_t countFilesRecursive(FFstrbuf* baseDirPath, const char* filename) -{ - uint32_t baseDirPathLength = baseDirPath->length; - - ffStrbufAppendC(baseDirPath, '/'); - ffStrbufAppendS(baseDirPath, filename); - bool exists = ffFileExists(baseDirPath->chars, S_IFREG); - ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); - if(exists) - return 1; - - DIR* dirp = opendir(baseDirPath->chars); - if(dirp == NULL) - return 0; - - ffStrbufAppendC(baseDirPath, '/'); - baseDirPathLength = baseDirPath->length; - - uint32_t sum = 0; - - struct dirent *entry; - while((entry = readdir(dirp)) != NULL) { - // According to the PMS, neither category nor package name can begin with '.', so no need to check for . or .. specifically - if(entry->d_type != DT_DIR || entry->d_name[0] == '.') - continue; - - ffStrbufAppendS(baseDirPath, entry->d_name); - sum += countFilesRecursive(baseDirPath, filename); - ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); - } - - closedir(dirp); - return sum; -} - -static uint32_t getNixPackages(char* path) -{ - //Nix detection is kinda slow, so we only do it if the dir exists - if(!ffFileExists(path, S_IFDIR)) - return 0; - - FFstrbuf output; - ffStrbufInitA(&output, 128); - - //https://github.com/LinusDierheimer/fastfetch/issues/195#issuecomment-1191748222 - FFstrbuf command; - ffStrbufInitA(&command, 255); - ffStrbufAppendS(&command, "for x in $(nix-store --query --requisites "); - ffStrbufAppendS(&command, path); - ffStrbufAppendS(&command, "); do if [ -d $x ]; then echo $x ; fi ; done | cut -d- -f2- | egrep '([0-9]{1,}\\.)+[0-9]{1,}' | egrep -v '\\-doc$|\\-man$|\\-info$|\\-dev$|\\-bin$|^nixos-system-nixos-' | uniq"); - - ffProcessAppendStdOut(&output, (char* const[]) { - "sh", - "-c", - command.chars, - NULL - }); - - //Each package is a new line in the output. If at least one line is found, add 1 for the last line. - uint32_t result = ffStrbufCountC(&output, '\n'); - if(result > 0) - result++; - - ffStrbufDestroy(&output); - return result; -} - -static uint32_t getXBPS(FFstrbuf* baseDir) -{ - DIR* dir = opendir(baseDir->chars); - if(dir == NULL) - return 0; - - uint32_t result = 0; - - struct dirent *entry; - while((entry = readdir(dir)) != NULL) - { - if(entry->d_type != DT_REG || strncasecmp(entry->d_name, "pkgdb-", 6) != 0) - continue; - - ffStrbufAppendC(baseDir, '/'); - ffStrbufAppendS(baseDir, entry->d_name); - result = getNumStrings(baseDir->chars, "installed"); - break; - } - - closedir(dir); - return result; -} - -#endif // !__ANDROID__ - -#else // !__APPLE__ - -static uint32_t countBrewPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/Caskroom"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - ffStrbufAppendS(baseDir, "/Cellar"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t getBrewPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - const char* prefix = getenv("HOMEBREW_PREFIX"); - bool prefixSet = ffStrSet(prefix); - - if(prefixSet) - { - ffStrbufAppendS(baseDir, prefix); - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - ffStrbufAppendS(baseDir, "/opt/homebrew"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - ffStrbufAppendS(baseDir, "/usr/local"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t countMacPortsPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/var/macports/software"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t getMacPortsPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - const char* prefix = getenv("MACPORTS_PREFIX"); - bool prefixSet = ffStrSet(prefix); - - if(prefixSet) - { - ffStrbufAppendS(baseDir, prefix); - result += countMacPortsPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - ffStrbufAppendS(baseDir, "/opt/local"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countMacPortsPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -#endif // __APPLE__ - -#ifdef FF_HAVE_RPM -#include "common/library.h" -#include -#include -#include -#include - -static uint32_t getRpmFromLibrpm(const FFinstance* instance) -{ - FF_LIBRARY_LOAD(rpm, &instance->config.librpm, 0, "librpm.so", 12) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0) - - // Don't print any error messages - ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG)); - - if(ffrpmReadConfigFiles(NULL, NULL) != 0) - { - dlclose(rpm); - return 0; - } - - rpmts ts = ffrpmtsCreate(); - if(ts == NULL) - { - dlclose(rpm); - return 0; - } - - rpmdbMatchIterator mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0); - if(mi == NULL) - { - ffrpmtsFree(ts); - dlclose(rpm); - return 0; - } - - int count = ffrpmdbGetIteratorCount(mi); - - ffrpmdbFreeIterator(mi); - ffrpmtsFree(ts); - dlclose(rpm); - - return count > 0 ? (uint32_t) count : 0; -} - -#endif //FF_HAVE_RPM - -static void getPackageCounts(const FFinstance* instance, FFstrbuf* baseDir, PackageCounts* packageCounts) -{ - #if defined(__APPLE__) || defined(__ANDROID__) - FF_UNUSED(instance); - #endif - - #ifndef __APPLE__ //Linux desktop and Android - - uint32_t baseDirLength = baseDir->length; - - //pacman - ffStrbufAppendS(baseDir, "/var/lib/pacman/local"); - packageCounts->pacman += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //dpkg - ffStrbufAppendS(baseDir, "/var/lib/dpkg/status"); - packageCounts->dpkg += getNumStrings(baseDir->chars, "Status: "); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #ifndef __ANDROID__ //Linux Desktop - - //rpm - ffStrbufAppendS(baseDir, "/var/lib/rpm/rmpdb.sqlite"); - packageCounts->rpm += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(blob) FROM Packages"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //emerge - ffStrbufAppendS(baseDir, "/var/db/pkg"); - packageCounts->emerge += countFilesRecursive(baseDir, "SIZE"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //xps - ffStrbufAppendS(baseDir, "/var/db/xbps"); - packageCounts->xbps += getXBPS(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //nix system - ffStrbufAppendS(baseDir, "/run/current-system"); - packageCounts->nixSystem += getNixPackages(baseDir->chars); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //nix default - ffStrbufAppendS(baseDir, "/nix/var/nix/profiles/default"); - packageCounts->nixDefault += getNixPackages(baseDir->chars); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //apk - ffStrbufAppendS(baseDir, "/lib/apk/db/installed"); - packageCounts->apk += getNumStrings(baseDir->chars, "C:Q"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //flatpak - ffStrbufAppendS(baseDir, "/var/lib/flatpak/app"); - packageCounts->flatpak += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //snap - ffStrbufAppendS(baseDir, "/snap"); - uint32_t snap = getNumElements(baseDir->chars, DT_DIR); - if(snap > 0) - packageCounts->snap += (snap - 1); //Accounting for the /snap/bin folder - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //pacman branch - ffStrbufAppendS(baseDir, "/etc/pacman-mirrors.conf"); - if(ffParsePropFile(baseDir->chars, "Branch =", &packageCounts->pacmanBranch) && packageCounts->pacmanBranch.length == 0) - ffStrbufAppendS(&packageCounts->pacmanBranch, "stable"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #endif // !__ANDROID__ - - #else // !__APPLE__ - - //brew - packageCounts->brew += getBrewPackages(baseDir); - packageCounts->port += getMacPortsPackages(baseDir); - - #endif // __APPLE__ - - #ifdef __FreeBSD__ - - ffStrbufAppendS(baseDir, "/var/db/pkg/local.sqlite"); - packageCounts->pkg += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(id) FROM packages"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #endif // __FreeBSD__ -} - -static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDir, PackageCounts* packageCounts) -{ - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/bedrock/strata"); - - DIR* dir = opendir(baseDir->chars); - if(dir == NULL) - { - ffStrbufSubstrBefore(baseDir, baseDirLength); - getPackageCounts(instance, baseDir, packageCounts); - return; - } - - ffStrbufAppendC(baseDir, '/'); - baseDirLength = baseDir->length; - - struct dirent* entry; - while((entry = readdir(dir)) != NULL) - { - if(entry->d_type != DT_DIR) - continue; - - ffStrbufAppendS(baseDir, entry->d_name); - getPackageCounts(instance, baseDir, packageCounts); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - closedir(dir); -} void ffPrintPackages(FFinstance* instance) { - PackageCounts counts = {0}; + FFPackageCounts counts = {0}; ffStrbufInit(&counts.pacmanBranch); + ffDetectPackages(instance, &counts); - FFstrbuf baseDir; - ffStrbufInitA(&baseDir, 512); - ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT); - - if(ffStrbufIgnCaseCompS(&ffDetectOS(instance)->id, "bedrock") == 0) - getPackageCountsBedrock(instance, &baseDir, &counts); - else - getPackageCounts(instance, &baseDir, &counts); - - // If SQL failed, we can still try with librpm. - // This is needed on openSUSE, which seems to use a proprietary database file - // This method doesn't work on bedrock, so we do it here. - #ifdef FF_HAVE_RPM - if(counts.rpm == 0) - counts.rpm = getRpmFromLibrpm(instance); - #endif - - #if !defined(__ANDROID__) && !defined(__APPLE__) - //nix user - ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir); - ffStrbufAppendS(&baseDir, "/.nix-profile"); - uint32_t nixUser = getNixPackages(baseDir.chars); - #else - uint32_t nixUser = 0; - #endif - - ffStrbufDestroy(&baseDir); - - uint32_t all = counts.pacman + counts.dpkg + counts.rpm + counts.emerge + counts.xbps + counts.nixSystem + nixUser + counts.nixDefault + counts.apk + counts.pkg + counts.flatpak + counts.snap + counts.brew + counts.port; + uint32_t all = counts.pacman + counts.dpkg + counts.rpm + counts.emerge + counts.xbps + counts.nixSystem + counts.nixUser + counts.nixDefault + counts.apk + counts.pkg + counts.flatpak + counts.snap + counts.brew + counts.port + counts.scoop; if(all == 0) { ffPrintError(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packages, "No packages from known package managers found"); @@ -507,10 +52,10 @@ void ffPrintPackages(FFinstance* instance) printf(", "); } - if(nixUser > 0) + if(counts.nixUser > 0) { - printf("%u (nix-user)", nixUser); - if((all = all - nixUser) > 0) + printf("%u (nix-user)", counts.nixUser); + if((all = all - counts.nixUser) > 0) printf(", "); } @@ -527,6 +72,7 @@ void ffPrintPackages(FFinstance* instance) FF_PRINT_PACKAGE(snap) FF_PRINT_PACKAGE(brew) FF_PRINT_PACKAGE(port) + FF_PRINT_PACKAGE(scoop) //Fix linter warning of unused value of all (void) all; @@ -546,14 +92,15 @@ void ffPrintPackages(FFinstance* instance) {FF_FORMAT_ARG_TYPE_UINT, &counts.emerge}, {FF_FORMAT_ARG_TYPE_UINT, &counts.xbps}, {FF_FORMAT_ARG_TYPE_UINT, &counts.nixSystem}, - {FF_FORMAT_ARG_TYPE_UINT, &nixUser}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.nixUser}, {FF_FORMAT_ARG_TYPE_UINT, &counts.nixDefault}, {FF_FORMAT_ARG_TYPE_UINT, &counts.apk}, {FF_FORMAT_ARG_TYPE_UINT, &counts.pkg}, {FF_FORMAT_ARG_TYPE_UINT, &counts.flatpak}, {FF_FORMAT_ARG_TYPE_UINT, &counts.snap}, {FF_FORMAT_ARG_TYPE_UINT, &counts.brew}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.port} + {FF_FORMAT_ARG_TYPE_UINT, &counts.port}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.scoop} }); }