2022-10-13 21:43:31 +02:00
#include "packages.h"
2023-01-28 12:40:12 +01:00
#include "common/io/io.h"
2022-10-13 22:02:58 +02:00
#include "common/parsing.h"
#include "common/processing.h"
2022-10-08 17:41:25 +08:00
#include "common/properties.h"
#include "common/settings.h"
#include "detection/os/os.h"
2023-06-19 15:38:37 +08:00
#include "util/stringUtils.h"
2022-10-08 17:41:25 +08:00
2025-01-17 15:01:23 +08:00
static uint32_t getNumElements ( FFstrbuf * baseDir , const char * dirname , bool isdir )
2022-10-13 21:43:31 +02:00
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , dirname );
2025-01-17 15:01:23 +08:00
uint32_t num_elements = ffPackagesGetNumElements ( baseDir -> chars , isdir );
2022-10-13 21:43:31 +02:00
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return num_elements ;
}
2022-10-08 17:41:25 +08:00
2022-10-13 21:43:31 +02:00
static uint32_t getNumStringsImpl ( const char * filename , const char * needle )
2022-10-08 17:41:25 +08:00
{
2023-12-06 19:06:44 +09:00
FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate ();
if ( ! ffReadFileBuffer ( filename , & content ))
2022-10-08 17:41:25 +08:00
return 0 ;
uint32_t count = 0 ;
2023-12-06 19:06:44 +09:00
char * iter = content . chars ;
size_t needleLength = strlen ( needle );
while (( iter = memmem ( iter , content . length - ( size_t )( iter - content . chars ), needle , needleLength )) != NULL )
2022-10-08 17:41:25 +08:00
{
2023-12-06 19:06:44 +09:00
++ count ;
iter += needleLength ;
2022-10-08 17:41:25 +08:00
}
return count ;
}
2024-09-12 15:39:22 +08:00
static uint32_t getNumStrings ( FFstrbuf * baseDir , const char * filename , const char * needle , const char * packageId )
2022-10-13 21:43:31 +02:00
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , filename );
2024-09-12 15:39:22 +08:00
FF_STRBUF_AUTO_DESTROY cacheDir = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreate ();
uint32_t num_elements ;
if ( ffPackagesReadCache ( & cacheDir , & cacheContent , baseDir -> chars , packageId , & num_elements ))
{
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return num_elements ;
}
num_elements = getNumStringsImpl ( baseDir -> chars , needle );
2022-10-13 21:43:31 +02:00
ffStrbufSubstrBefore ( baseDir , baseDirLength );
2024-09-12 15:39:22 +08:00
ffPackagesWriteCache ( & cacheDir , & cacheContent , num_elements );
2022-10-13 21:43:31 +02:00
return num_elements ;
}
2024-09-12 15:39:22 +08:00
static uint32_t getSQLite3Int ( FFstrbuf * baseDir , const char * dbPath , const char * query , const char * packageId )
2022-10-13 21:43:31 +02:00
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , dbPath );
2024-09-12 15:39:22 +08:00
FF_STRBUF_AUTO_DESTROY cacheDir = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreate ();
uint32_t num_elements ;
if ( ffPackagesReadCache ( & cacheDir , & cacheContent , baseDir -> chars , packageId , & num_elements ))
{
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return num_elements ;
}
num_elements = ( uint32_t ) ffSettingsGetSQLite3Int ( baseDir -> chars , query );
2022-10-13 21:43:31 +02:00
ffStrbufSubstrBefore ( baseDir , baseDirLength );
2024-09-12 15:39:22 +08:00
ffPackagesWriteCache ( & cacheDir , & cacheContent , num_elements );
2022-10-13 21:43:31 +02:00
return num_elements ;
}
2022-10-08 17:41:25 +08:00
2022-10-13 21:43:31 +02:00
static uint32_t countFilesRecursiveImpl ( FFstrbuf * baseDirPath , const char * filename )
2022-10-08 17:41:25 +08:00
{
uint32_t baseDirPathLength = baseDirPath -> length ;
ffStrbufAppendC ( baseDirPath , '/' );
ffStrbufAppendS ( baseDirPath , filename );
2023-01-28 11:52:23 +01:00
bool exists = ffPathExists ( baseDirPath -> chars , FF_PATHTYPE_FILE );
2022-10-08 17:41:25 +08:00
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 );
2022-10-13 21:43:31 +02:00
sum += countFilesRecursiveImpl ( baseDirPath , filename );
2022-10-08 17:41:25 +08:00
ffStrbufSubstrBefore ( baseDirPath , baseDirPathLength );
}
closedir ( dirp );
return sum ;
}
2022-10-13 21:43:31 +02:00
static uint32_t countFilesRecursive ( FFstrbuf * baseDir , const char * dirname , const char * filename )
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , dirname );
uint32_t sum = countFilesRecursiveImpl ( baseDir , filename );
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return sum ;
}
2024-04-28 20:59:05 -05:00
static bool isValidNixPkg ( FFstrbuf * pkg )
{
if ( ! ffPathExists ( pkg -> chars , FF_PATHTYPE_DIRECTORY ))
return false ;
ffStrbufSubstrAfterLastC ( pkg , '/' );
if (
ffStrbufStartsWithS ( pkg , "nixos-system-nixos-" ) ||
ffStrbufEndsWithS ( pkg , "-doc" ) ||
ffStrbufEndsWithS ( pkg , "-man" ) ||
ffStrbufEndsWithS ( pkg , "-info" ) ||
ffStrbufEndsWithS ( pkg , "-dev" ) ||
ffStrbufEndsWithS ( pkg , "-bin" )
) return false ;
enum { START , DIGIT , DOT , MATCH } state = START ;
for ( uint32_t i = 0 ; i < pkg -> length ; i ++ )
{
char c = pkg -> chars [ i ];
switch ( state )
{
case START :
2024-05-28 15:34:59 +08:00
if ( ffCharIsDigit ( c ))
2024-04-28 20:59:05 -05:00
state = DIGIT ;
break ;
case DIGIT :
2024-05-28 15:34:59 +08:00
if ( ffCharIsDigit ( c ))
2024-04-28 20:59:05 -05:00
continue ;
if ( c == '.' )
state = DOT ;
else
state = START ;
break ;
case DOT :
2024-05-28 15:34:59 +08:00
if ( ffCharIsDigit ( c ))
2024-04-28 20:59:05 -05:00
state = MATCH ;
else
state = START ;
break ;
case MATCH :
break ;
}
}
return state == MATCH ;
}
static bool checkNixCache ( FFstrbuf * cacheDir , FFstrbuf * hash , uint32_t * count )
{
if ( ! ffPathExists ( cacheDir -> chars , FF_PATHTYPE_FILE ))
return false ;
2024-04-29 10:04:20 +08:00
FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreate ();
2024-04-28 20:59:05 -05:00
if ( ! ffReadFileBuffer ( cacheDir -> chars , & cacheContent ))
return false ;
// Format: <hash>\n<count>
uint32_t split = ffStrbufFirstIndexC ( & cacheContent , '\n' );
if ( split == cacheContent . length )
return false ;
ffStrbufSetNS ( hash , split , cacheContent . chars );
* count = ( uint32_t ) atoi ( cacheContent . chars + split + 1 );
return true ;
}
static bool writeNixCache ( FFstrbuf * cacheDir , FFstrbuf * hash , uint32_t count )
{
2024-04-29 10:04:20 +08:00
FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreateCopy ( hash );
ffStrbufAppendF ( & cacheContent , " \n %u" , count );
2024-04-28 20:59:05 -05:00
return ffWriteFileBuffer ( cacheDir -> chars , & cacheContent );
}
2022-10-13 21:43:31 +02:00
static uint32_t getNixPackagesImpl ( char * path )
2022-10-08 17:41:25 +08:00
{
//Nix detection is kinda slow, so we only do it if the dir exists
2023-01-28 11:52:23 +01:00
if ( ! ffPathExists ( path , FF_PATHTYPE_DIRECTORY ))
2022-10-08 17:41:25 +08:00
return 0 ;
2024-04-29 10:04:20 +08:00
FF_STRBUF_AUTO_DESTROY cacheDir = ffStrbufCreateCopy ( & instance . state . platform . cacheDir );
2024-04-28 20:59:05 -05:00
ffStrbufEnsureEndsWithC ( & cacheDir , '/' );
ffStrbufAppendS ( & cacheDir , "fastfetch/packages/nix" );
ffStrbufAppendS ( & cacheDir , path );
//Check the hash first to determine if we need to recompute the count
FF_STRBUF_AUTO_DESTROY hash = ffStrbufCreateA ( 64 );
FF_STRBUF_AUTO_DESTROY cacheHash = ffStrbufCreateA ( 64 );
uint32_t count = 0 ;
ffProcessAppendStdOut ( & hash , ( char * const []) {
"nix-store" ,
"--query" ,
"--hash" ,
path ,
NULL
});
2022-10-08 17:41:25 +08:00
2024-04-28 20:59:05 -05:00
if ( checkNixCache ( & cacheDir , & cacheHash , & count ) && ffStrbufEqual ( & hash , & cacheHash ))
return count ;
//Cache is invalid, recompute the count
count = 0 ;
//Implementation based on bash script from here:
2023-06-12 17:26:37 +02:00
//https://github.com/fastfetch-cli/fastfetch/issues/195#issuecomment-1191748222
2024-04-29 10:04:20 +08:00
2024-04-28 20:59:05 -05:00
FF_STRBUF_AUTO_DESTROY output = ffStrbufCreateA ( 1024 );
2022-10-08 17:41:25 +08:00
ffProcessAppendStdOut ( & output , ( char * const []) {
2024-04-28 20:59:05 -05:00
"nix-store" ,
"--query" ,
"--requisites" ,
path ,
2022-10-08 17:41:25 +08:00
NULL
});
2024-04-28 20:59:05 -05:00
uint32_t lineLength = 0 ;
for ( uint32_t i = 0 ; i < output . length ; i ++ )
{
if ( output . chars [ i ] != '\n' )
{
lineLength ++ ;
continue ;
}
output . chars [ i ] = '\0' ;
FFstrbuf line = {
. allocated = 0 ,
. length = lineLength ,
. chars = output . chars + i - lineLength
};
if ( isValidNixPkg ( & line ))
count ++ ;
lineLength = 0 ;
}
2024-04-29 10:04:20 +08:00
2024-04-28 20:59:05 -05:00
writeNixCache ( & cacheDir , & hash , count );
return count ;
2022-10-08 17:41:25 +08:00
}
2022-10-13 21:43:31 +02:00
static uint32_t getNixPackages ( FFstrbuf * baseDir , const char * dirname )
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , dirname );
uint32_t num_elements = getNixPackagesImpl ( baseDir -> chars );
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return num_elements ;
}
static uint32_t getXBPSImpl ( FFstrbuf * baseDir )
2022-10-08 17:41:25 +08:00
{
DIR * dir = opendir ( baseDir -> chars );
if ( dir == NULL )
return 0 ;
uint32_t result = 0 ;
struct dirent * entry ;
while (( entry = readdir ( dir )) != NULL )
{
2023-06-19 15:38:37 +08:00
if ( entry -> d_type != DT_REG || ! ffStrStartsWithIgnCase ( entry -> d_name , "pkgdb-" ))
2022-10-08 17:41:25 +08:00
continue ;
ffStrbufAppendC ( baseDir , '/' );
ffStrbufAppendS ( baseDir , entry -> d_name );
2022-10-13 21:43:31 +02:00
result = getNumStringsImpl ( baseDir -> chars , "<string>installed</string>" );
2022-10-08 17:41:25 +08:00
break ;
}
closedir ( dir );
return result ;
}
2022-10-13 21:43:31 +02:00
static uint32_t getXBPS ( FFstrbuf * baseDir , const char * dirname )
2022-10-08 17:41:25 +08:00
{
uint32_t baseDirLength = baseDir -> length ;
2022-10-13 21:43:31 +02:00
ffStrbufAppendS ( baseDir , dirname );
uint32_t result = getXBPSImpl ( baseDir );
2022-10-08 17:41:25 +08:00
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return result ;
}
2022-10-13 21:43:31 +02:00
static uint32_t getSnap ( FFstrbuf * baseDir )
2022-10-08 17:41:25 +08:00
{
2025-01-17 15:01:23 +08:00
uint32_t result = getNumElements ( baseDir , "/snap" , true );
2022-10-08 17:41:25 +08:00
2023-09-20 16:00:09 +08:00
if ( result == 0 )
2025-01-17 15:01:23 +08:00
result = getNumElements ( baseDir , "/var/lib/snapd/snap" , true );
2023-09-20 16:00:09 +08:00
2022-10-13 21:43:31 +02:00
//Accounting for the /snap/bin folder
return result > 0 ? result - 1 : 0 ;
2022-10-08 17:41:25 +08:00
}
#ifdef FF_HAVE_RPM
#include "common/library.h"
#include <rpm/rpmlib.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
#include <rpm/rpmlog.h>
2023-06-24 10:59:53 +08:00
static uint32_t getRpmFromLibrpm ( void )
2022-10-08 17:41:25 +08:00
{
2024-09-04 15:28:35 +08:00
FF_LIBRARY_LOAD ( rpm , 0 , "librpm" FF_LIBRARY_EXTENSION , 12 )
2022-10-08 17:41:25 +08:00
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 )
return 0 ;
rpmts ts = ffrpmtsCreate ();
if ( ts == NULL )
return 0 ;
rpmdbMatchIterator mi = ffrpmtsInitIterator ( ts , RPMDBI_LABEL , NULL , 0 );
if ( mi == NULL )
{
ffrpmtsFree ( ts );
return 0 ;
}
int count = ffrpmdbGetIteratorCount ( mi );
ffrpmdbFreeIterator ( mi );
ffrpmtsFree ( ts );
return count > 0 ? ( uint32_t ) count : 0 ;
}
#endif //FF_HAVE_RPM
2025-01-08 09:44:45 +08:00
static uint32_t getAMPackages ( FFstrbuf * baseDir )
{
uint32_t baseLength = baseDir -> length ;
FF_AUTO_CLOSE_DIR DIR * dirp = opendir ( baseDir -> chars );
if ( ! dirp ) return 0 ;
uint32_t result = 0 ;
struct dirent * entry ;
while (( entry = readdir ( dirp )) != NULL )
{
if ( entry -> d_name [ 0 ] == '.' ) continue ;
if ( entry -> d_type == DT_DIR )
{
ffStrbufAppendF ( baseDir , "/%s/remove" , entry -> d_name );
if ( ffPathExists ( baseDir -> chars , FF_PATHTYPE_FILE ))
++ result ;
ffStrbufSubstrBefore ( baseDir , baseLength );
}
}
return result ;
}
static uint32_t getAMSystem ( FFstrbuf * baseDir )
2024-04-03 21:08:33 +08:00
{
// #771
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , "/opt" );
uint32_t optDirLength = baseDir -> length ;
uint32_t result = 0 ;
ffStrbufAppendS ( baseDir , "/am/APP-MANAGER" );
if ( ffPathExists ( baseDir -> chars , FF_PATHTYPE_FILE ))
{
++ result ; // `am` itself is counted as a package too
ffStrbufSubstrBefore ( baseDir , optDirLength );
2025-01-08 09:44:45 +08:00
result = getAMPackages ( baseDir );
2024-04-03 21:08:33 +08:00
}
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return result ;
}
2025-01-08 09:44:45 +08:00
static uint32_t getAMUser ( void )
{
// check if $XDG_CONFIG_HOME/appman/appman-config exists
FFstrbuf * baseDir = FF_LIST_GET ( FFstrbuf , instance . state . platform . configDirs , 0 );
uint32_t baseLen = baseDir -> length ;
ffStrbufAppendS ( baseDir , "appman/appman-config" );
FF_STRBUF_AUTO_DESTROY packagesPath = ffStrbufCreate ();
2025-01-08 10:28:13 +08:00
if ( ffReadFileBuffer ( baseDir -> chars , & packagesPath ))
ffStrbufTrimRightSpace ( & packagesPath );
2025-01-08 09:44:45 +08:00
ffStrbufSubstrBefore ( baseDir , baseLen );
return packagesPath . length > 0 ? getAMPackages ( & packagesPath ) : 0 ;
}
2024-06-06 12:06:56 +02:00
static int compareHash ( const void * a , const void * b )
{
return memcmp ( a , b , 32 );
}
2024-05-28 02:50:18 +02:00
2024-06-06 12:06:56 +02:00
static uint32_t getGuixPackagesImpl ( char * filename )
2024-05-28 02:50:18 +02:00
{
2024-06-06 12:06:56 +02:00
FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate ();
if ( ! ffAppendFileBuffer ( filename , & content ))
return 0 ;
2024-05-28 02:50:18 +02:00
2024-06-06 12:06:56 +02:00
// Count number of unique /gnu/store/ paths in PROFILE/manifest based on their hash value.
// Contains packages explicitly installed and their propagated inputs.
char * pend = content . chars ;
for ( const char * pattern = content . chars ; ( pattern = strstr ( pattern , "/gnu/store/" )); pattern += 32 )
{
pattern += strlen ( "/gnu/store/" );
memmove ( pend , pattern , 32 );
pend += 32 ;
}
if ( pend == content . chars )
return 0 ;
2024-05-28 02:50:18 +02:00
2024-06-06 12:06:56 +02:00
qsort ( content . chars , ( size_t ) ( pend - content . chars ) / 32 , 32 , compareHash );
2024-05-28 02:50:18 +02:00
2024-06-06 12:06:56 +02:00
uint32_t count = 1 ;
for ( const char * p = content . chars + 32 ; p < pend ; p += 32 )
count += compareHash ( p - 32 , p ) != 0 ;
2024-05-28 02:50:18 +02:00
return count ;
}
static uint32_t getGuixPackages ( FFstrbuf * baseDir , const char * dirname )
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , dirname );
2024-06-06 12:06:56 +02:00
ffStrbufAppendS ( baseDir , "/manifest" );
2024-05-28 02:50:18 +02:00
uint32_t num_elements = getGuixPackagesImpl ( baseDir -> chars );
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return num_elements ;
}
2024-09-22 12:21:56 +08:00
static inline uint32_t getFlatpakRuntimePackagesArch ( FFstrbuf * baseDir )
{
FF_AUTO_CLOSE_DIR DIR * dirp = opendir ( baseDir -> chars );
if ( dirp == NULL )
return 0 ;
uint32_t num_elements = 0 ;
struct dirent * entry ;
while (( entry = readdir ( dirp )) != NULL )
{
if ( entry -> d_type == DT_DIR && entry -> d_name [ 0 ] != '.' )
{
2025-01-17 15:01:23 +08:00
num_elements += getNumElements ( baseDir , entry -> d_name , true );
2024-09-22 12:21:56 +08:00
}
}
return num_elements ;
}
static inline uint32_t getFlatpakRuntimePackages ( FFstrbuf * baseDir )
{
ffStrbufAppendS ( baseDir , "runtime/" );
FF_AUTO_CLOSE_DIR DIR * dirp = opendir ( baseDir -> chars );
if ( dirp == NULL )
return 0 ;
uint32_t runtimeDirLength = baseDir -> length ;
uint32_t num_elements = 0 ;
struct dirent * entry ;
while (( entry = readdir ( dirp )) != NULL )
{
if ( entry -> d_type == DT_DIR && entry -> d_name [ 0 ] != '.' )
{
// `flatpak list` ignores `.Locale` and `.Debug` packages, and maybe others
const char * dot = strrchr ( entry -> d_name , '.' );
if ( __builtin_expect ( ! dot , false )) continue ;
dot ++ ;
if ( ffStrEquals ( dot , "Locale" ) || ffStrEquals ( dot , "Debug" ))
continue ;
ffStrbufAppendS ( baseDir , entry -> d_name );
ffStrbufAppendC ( baseDir , '/' );
num_elements += getFlatpakRuntimePackagesArch ( baseDir );
ffStrbufSubstrBefore ( baseDir , runtimeDirLength );
}
}
return num_elements ;
}
static uint32_t getFlatpakPackages ( FFstrbuf * baseDir , const char * dirname )
{
uint32_t num_elements = 0 ;
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , dirname );
ffStrbufAppendS ( baseDir , "/flatpak/" );
uint32_t flatpakDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , "app" );
2025-01-17 15:01:23 +08:00
num_elements += ffPackagesGetNumElements ( baseDir -> chars , true );
2024-09-22 12:21:56 +08:00
ffStrbufSubstrBefore ( baseDir , flatpakDirLength );
num_elements += getFlatpakRuntimePackages ( baseDir );
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return num_elements ;
}
2024-02-21 15:56:52 +08:00
static void getPackageCounts ( FFstrbuf * baseDir , FFPackagesResult * packageCounts , FFPackagesOptions * options )
2022-10-08 17:41:25 +08:00
{
2024-09-12 15:39:22 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_APK_BIT )) packageCounts -> apk += getNumStrings ( baseDir , "/lib/apk/db/installed" , "C:Q" , "apk" );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_DPKG_BIT )) packageCounts -> dpkg += getNumStrings ( baseDir , "/var/lib/dpkg/status" , "Status: install ok installed" , "dpkg" );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_LPKG_BIT )) packageCounts -> lpkg += getNumStrings ( baseDir , "/opt/Loc-OS-LPKG/installed-lpkg/Listinstalled-lpkg.list" , " \n " , "lpkg" );
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_EMERGE_BIT )) packageCounts -> emerge += countFilesRecursive ( baseDir , "/var/db/pkg" , "SIZE" );
2025-01-17 15:01:23 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_EOPKG_BIT )) packageCounts -> eopkg += getNumElements ( baseDir , "/var/lib/eopkg/package" , true );
2024-09-22 12:21:56 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_FLATPAK_BIT )) packageCounts -> flatpakSystem += getFlatpakPackages ( baseDir , "/var/lib" );
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_NIX_BIT ))
{
packageCounts -> nixDefault += getNixPackages ( baseDir , "/nix/var/nix/profiles/default" );
packageCounts -> nixSystem += getNixPackages ( baseDir , "/run/current-system" );
}
2025-01-17 15:01:23 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PACMAN_BIT )) packageCounts -> pacman += getNumElements ( baseDir , "/var/lib/pacman/local" , true );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_LPKGBUILD_BIT )) packageCounts -> lpkgbuild += getNumElements ( baseDir , "/opt/Loc-OS-LPKG/lpkgbuild/remove" , false );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PKGTOOL_BIT )) packageCounts -> pkgtool += getNumElements ( baseDir , "/var/log/packages" , false );
2025-06-03 10:53:02 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_RPM_BIT ))
{
// `Sigmd5` is the only table that doesn't contain the virtual `gpg-pubkey` package
packageCounts -> rpm += getSQLite3Int ( baseDir , "/var/lib/rpm/rpmdb.sqlite" , "SELECT count(*) FROM Sigmd5" , "rpm" );
}
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_SNAP_BIT )) packageCounts -> snap += getSnap ( baseDir );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_XBPS_BIT )) packageCounts -> xbps += getXBPS ( baseDir , "/var/db/xbps" );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_BREW_BIT ))
{
2025-01-17 15:01:23 +08:00
packageCounts -> brewCask += getNumElements ( baseDir , "/home/linuxbrew/.linuxbrew/Caskroom" , true );
packageCounts -> brew += getNumElements ( baseDir , "/home/linuxbrew/.linuxbrew/Cellar" , true );
2024-02-21 15:56:52 +08:00
}
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PALUDIS_BIT )) packageCounts -> paludis += countFilesRecursive ( baseDir , "/var/db/paludis/repositories" , "environment.bz2" );
2024-09-12 15:39:22 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_OPKG_BIT )) packageCounts -> opkg += getNumStrings ( baseDir , "/usr/lib/opkg/status" , "Package:" , "opkg" ); // openwrt
2025-01-08 09:44:45 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_AM_BIT )) packageCounts -> amSystem = getAMSystem ( baseDir );
2024-09-12 15:39:22 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_SORCERY_BIT )) packageCounts -> sorcery += getNumStrings ( baseDir , "/var/state/sorcery/packages" , ":installed:" , "sorcery" );
2024-05-28 02:50:18 +02:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_GUIX_BIT ))
{
packageCounts -> guixSystem += getGuixPackages ( baseDir , "/run/current-system/profile" );
}
2025-01-17 15:01:23 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_LINGLONG_BIT )) packageCounts -> linglong += getNumElements ( baseDir , "/var/lib/linglong/repo/refs/heads/main" , true );
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PACSTALL_BIT )) packageCounts -> pacstall += getNumElements ( baseDir , "/var/lib/pacstall/metadata" , false );
2024-11-10 23:32:22 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_QI_BIT )) packageCounts -> qi += getNumStrings ( baseDir , "/var/qi/installed_packages.list" , " \n " , "qi" );
2025-02-24 10:29:14 +03:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PISI_BIT )) packageCounts -> pisi += getNumElements ( baseDir , "/var/lib/pisi/package" , true );
2025-03-19 14:59:59 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PKGSRC_BIT )) packageCounts -> pkgsrc += getNumElements ( baseDir , "/usr/pkg/pkgdb" , DT_DIR );
2022-10-13 21:43:31 +02:00
}
2022-10-08 17:41:25 +08:00
2024-02-21 15:56:52 +08:00
static void getPackageCountsRegular ( FFstrbuf * baseDir , FFPackagesResult * packageCounts , FFPackagesOptions * options )
2022-10-13 21:43:31 +02:00
{
2024-02-21 15:56:52 +08:00
getPackageCounts ( baseDir , packageCounts , options );
2022-10-08 17:41:25 +08:00
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_PACMAN_BIT ))
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , FASTFETCH_TARGET_DIR_ETC "/pacman-mirrors.conf" );
if ( ffParsePropFile ( baseDir -> chars , "Branch =" , & packageCounts -> pacmanBranch ) && packageCounts -> pacmanBranch . length == 0 )
ffStrbufAppendS ( & packageCounts -> pacmanBranch , "stable" );
ffStrbufSubstrBefore ( baseDir , baseDirLength );
}
2022-10-08 17:41:25 +08:00
}
2024-02-21 15:56:52 +08:00
static void getPackageCountsBedrock ( FFstrbuf * baseDir , FFPackagesResult * packageCounts , FFPackagesOptions * options )
2022-10-08 17:41:25 +08:00
{
uint32_t baseDirLength = baseDir -> length ;
ffStrbufAppendS ( baseDir , "/bedrock/strata" );
2024-02-21 15:40:04 +08:00
FF_AUTO_CLOSE_DIR DIR * dir = opendir ( baseDir -> chars );
2022-10-08 17:41:25 +08:00
if ( dir == NULL )
{
ffStrbufSubstrBefore ( baseDir , baseDirLength );
return ;
}
ffStrbufAppendC ( baseDir , '/' );
2022-10-13 21:43:31 +02:00
uint32_t baseDirLength2 = baseDir -> length ;
2022-10-08 17:41:25 +08:00
struct dirent * entry ;
while (( entry = readdir ( dir )) != NULL )
{
if ( entry -> d_type != DT_DIR )
continue ;
2024-10-15 10:16:15 +08:00
if ( entry -> d_name [ 0 ] == '.' )
2023-01-09 10:45:51 +08:00
continue ;
2022-10-08 17:41:25 +08:00
ffStrbufAppendS ( baseDir , entry -> d_name );
2024-02-21 15:56:52 +08:00
getPackageCounts ( baseDir , packageCounts , options );
2022-10-13 21:43:31 +02:00
ffStrbufSubstrBefore ( baseDir , baseDirLength2 );
2022-10-08 17:41:25 +08:00
}
2022-10-13 21:43:31 +02:00
ffStrbufSubstrBefore ( baseDir , baseDirLength );
2022-10-08 17:41:25 +08:00
}
2024-02-21 15:56:52 +08:00
void ffDetectPackagesImpl ( FFPackagesResult * result , FFPackagesOptions * options )
2022-10-08 17:41:25 +08:00
{
2023-04-15 15:29:54 +08:00
FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateA ( 512 );
2022-10-08 17:41:25 +08:00
ffStrbufAppendS ( & baseDir , FASTFETCH_TARGET_DIR_ROOT );
2023-06-24 10:59:53 +08:00
if ( ffStrbufIgnCaseEqualS ( & ffDetectOS () -> id , "bedrock" ))
2024-02-21 15:56:52 +08:00
getPackageCountsBedrock ( & baseDir , result , options );
2022-10-08 17:41:25 +08:00
else
2024-02-21 15:56:52 +08:00
getPackageCountsRegular ( & baseDir , result , options );
2022-10-08 17:41:25 +08:00
// 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
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_RPM_BIT ) && result -> rpm == 0 )
2023-06-24 10:59:53 +08:00
result -> rpm = getRpmFromLibrpm ();
2022-10-08 17:41:25 +08:00
#endif
2023-06-24 10:59:53 +08:00
ffStrbufSet ( & baseDir , & instance . state . platform . homeDir );
2025-07-11 12:33:54 -05:00
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_NIX_BIT ))
2024-05-02 09:55:50 -05:00
{
2025-07-11 12:33:54 -05:00
// Count packages from $HOME/.nix-profile
result -> nixUser += getNixPackages ( & baseDir , ".nix-profile" );
2024-05-20 19:14:32 +08:00
2025-07-11 12:33:54 -05:00
// Check in $XDG_STATE_HOME/nix/profile
FF_STRBUF_AUTO_DESTROY stateHome = ffStrbufCreate ();
const char * stateHomeEnv = getenv ( "XDG_STATE_HOME" );
if ( ffStrSet ( stateHomeEnv ))
2024-05-20 19:14:32 +08:00
{
2025-07-11 12:33:54 -05:00
ffStrbufSetS ( & stateHome , stateHomeEnv );
ffStrbufEnsureEndsWithC ( & stateHome , '/' );
2024-05-20 19:14:32 +08:00
}
2024-05-02 09:55:50 -05:00
else
{
2025-07-11 12:33:54 -05:00
ffStrbufSet ( & stateHome , & instance . state . platform . homeDir );
ffStrbufAppendS ( & stateHome , ".local/state/" );
2024-05-02 09:55:50 -05:00
}
2025-07-11 12:33:54 -05:00
result -> nixUser += getNixPackages ( & stateHome , "nix/profile" );
2024-05-20 19:14:32 +08:00
2025-07-11 12:33:54 -05:00
// Check in /etc/profiles/per-user/$USER
2025-07-11 12:18:55 -05:00
FF_STRBUF_AUTO_DESTROY userPkgsDir = ffStrbufCreateStatic ( "/etc/profiles/per-user/" );
result -> nixUser += getNixPackages ( & userPkgsDir , instance . state . platform . userName . chars );
2024-05-02 09:55:50 -05:00
}
2024-05-20 19:14:32 +08:00
2024-05-28 02:50:18 +02:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_GUIX_BIT ))
{
result -> guixUser += getGuixPackages ( & baseDir , ".guix-profile" );
result -> guixHome += getGuixPackages ( & baseDir , ".guix-home/profile" );
}
2024-02-21 15:56:52 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_FLATPAK_BIT ))
2024-09-22 12:21:56 +08:00
result -> flatpakUser = getFlatpakPackages ( & baseDir , "/.local/share" );
2025-01-08 09:44:45 +08:00
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_AM_BIT ))
result -> amUser = getAMUser ();
2025-04-05 21:39:27 +05:45
if ( ! ( options -> disabled & FF_PACKAGES_FLAG_SOAR_BIT ))
result -> soar += getSQLite3Int ( & baseDir , ".local/share/soar/db/soar.db" , "SELECT COUNT(DISTINCT pkg_id || pkg_name) FROM packages WHERE is_installed = true" , "soar" );
2022-10-08 17:41:25 +08:00
}