From 5f16c185ca6db562147ef48cb3b9f6998df54a3e Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 17:36:30 +0800 Subject: [PATCH 01/18] Packages (emerge): differentiate `@world` set and `@system` set from packages count --- src/detection/packages/packages.h | 3 + src/detection/packages/packages_linux.c | 131 +++++++++++++++++++++++- src/modules/packages/packages.c | 23 ++++- 3 files changed, 154 insertions(+), 3 deletions(-) diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index 8b8a435d95..e3bee2dc31 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -13,6 +13,9 @@ typedef struct FFPackagesResult uint32_t choco; uint32_t dpkg; uint32_t emerge; + uint32_t emergeDeps; + uint32_t emergeSys; + uint32_t emergeWorld; uint32_t eopkg; uint32_t flatpakSystem; uint32_t flatpakUser; diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 8d9c9b0beb..17c94c045d 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -451,12 +451,141 @@ static uint32_t getPacmanPackages(FFstrbuf* baseDir) return getNumElements(baseDir, dbPath.chars, true); } +static uint32_t getProfSysPackages(FFstrbuf* profileDir, uint32_t depth) +{ + if (depth > 64) + return 0; + + ffStrbufEnsureEndsWithC(profileDir, '/'); + uint32_t profileDirLen = profileDir->length; + + uint32_t count = 0; + { + ffStrbufAppendS(profileDir, "packages"); + FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); + if (ffReadFileBuffer(profileDir->chars, &content)) + { + for (const char* line = content.chars; *line; ) + { + const char* eol = strchr(line, '\n'); + if (!eol) eol = content.chars + content.length; + if (*line == '*') ++count; + else if (*line == '-' && line + 1 < eol && line[1] == '*' && count > 0) --count; + line = (*eol) ? eol + 1 : eol; + } + } + ffStrbufSubstrBefore(profileDir, profileDirLen); + } + + ffStrbufAppendS(profileDir, "parent"); + FF_STRBUF_AUTO_DESTROY parentContent = ffStrbufCreate(); + bool ok = ffReadFileBuffer(profileDir->chars, &parentContent); + ffStrbufSubstrBefore(profileDir, profileDirLen); + + if (!ok) + return count; + + for (const char* line = parentContent.chars; *line; ) + { + const char* eol = strchr(line, '\n'); + if (!eol) + eol = parentContent.chars + parentContent.length; + + uint32_t lineLen = (uint32_t)(eol - line); + if (lineLen > 0 && *line != '#') + { + FF_STRBUF_AUTO_DESTROY parentPath = ffStrbufCreate(); + if (*line == '/') + ffStrbufSetNS(&parentPath, lineLen, line); + else + { + ffStrbufSet(&parentPath, profileDir); + ffStrbufAppendNS(&parentPath, lineLen, line); + } + + char resolved[PATH_MAX] = {0}; + if (realpath(parentPath.chars, resolved)) + { + FF_STRBUF_AUTO_DESTROY resolvedBuf = ffStrbufCreateS(resolved); + count += getProfSysPackages(&resolvedBuf, depth + 1); + } + } + + line = (*eol) ? eol + 1 : eol; + } + + return count; +} + +static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageCounts) +{ + uint32_t total = countFilesRecursive(baseDir, "/var/db/pkg", "SIZE"); + if (total == 0) + return; + + uint32_t world = 0; + { + uint32_t baseDirLen = baseDir->length; + ffStrbufAppendS(baseDir, "/var/lib/portage/world"); + FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); + if (ffReadFileBuffer(baseDir->chars, &content)) + { + for (const char* line = content.chars; *line; ) + { + const char* eol = strchr(line, '\n'); + if (!eol) eol = content.chars + content.length; + if (*line != '#') ++world; + line = (*eol) ? eol + 1 : eol; + } + } + ffStrbufSubstrBefore(baseDir, baseDirLen); + } + + uint32_t system = 0; + { + uint32_t baseDirLen = baseDir->length; + ffStrbufAppendS(baseDir, FASTFETCH_TARGET_DIR_ROOT "/etc/portage/make.profile"); + char resolved[PATH_MAX] = {0}; + if (realpath(baseDir->chars, resolved)) + { + FF_STRBUF_AUTO_DESTROY profileDir = ffStrbufCreateS(resolved); + system = getProfSysPackages(&profileDir, 0); + } + ffStrbufSubstrBefore(baseDir, baseDirLen); + } + + { + uint32_t baseDirLen = baseDir->length; + ffStrbufAppendS(baseDir, "/etc/portage/profile/packages"); + FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); + if (ffReadFileBuffer(baseDir->chars, &content)) + { + for (const char* line = content.chars; *line; ) + { + const char* eol = strchr(line, '\n'); + if (!eol) eol = content.chars + content.length; + if (*line == '*') ++system; + else if (*line == '-' && line + 1 < eol && line[1] == '*' && system > 0) --system; + line = (*eol) ? eol + 1 : eol; + } + } + ffStrbufSubstrBefore(baseDir, baseDirLen); + } + + uint32_t deps = (world + system < total) ? total - world - system : 0; + + packageCounts->emergeWorld += world; + packageCounts->emergeSys += system; + packageCounts->emergeDeps += deps; + packageCounts->emerge += total; +} + static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options) { 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"); - if (!(options->disabled & FF_PACKAGES_FLAG_EMERGE_BIT)) packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE"); + if (!(options->disabled & FF_PACKAGES_FLAG_EMERGE_BIT)) getPackageCountsEmerge(baseDir, packageCounts); if (!(options->disabled & FF_PACKAGES_FLAG_EOPKG_BIT)) packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", true); if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) packageCounts->flatpakSystem += getFlatpakPackages(baseDir, "/var/lib"); if (!(options->disabled & FF_PACKAGES_FLAG_KISS_BIT)) packageCounts->kiss += getNumElements(baseDir, "/var/db/kiss/installed", true); diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 6552d53d07..56e4baecf9 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -17,6 +17,7 @@ bool ffPrintPackages(FFPackagesOptions* options) return false; } + uint32_t emergeAll = counts.emerge; uint32_t nixAll = counts.nixDefault + counts.nixSystem + counts.nixUser; uint32_t flatpakAll = counts.flatpakSystem + counts.flatpakUser; uint32_t brewAll = counts.brew + counts.brewCask; @@ -60,7 +61,16 @@ bool ffPrintPackages(FFPackagesOptions* options) }; FF_PRINT_PACKAGE(dpkg) FF_PRINT_PACKAGE(rpm) - FF_PRINT_PACKAGE(emerge) + if (options->combined) + { + FF_PRINT_PACKAGE_ALL(emerge); + } + else + { + FF_PRINT_PACKAGE_NAME(emergeWorld, "emerge-world") + FF_PRINT_PACKAGE_NAME(emergeSys, "emerge-system") + FF_PRINT_PACKAGE_NAME(emergeDeps, "emerge-deps") + } FF_PRINT_PACKAGE(eopkg) FF_PRINT_PACKAGE(xbps) if (options->combined) @@ -160,6 +170,9 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_ARG(counts.dpkg, "dpkg"), FF_ARG(counts.rpm, "rpm"), FF_ARG(counts.emerge, "emerge"), + FF_ARG(counts.emergeWorld, "emerge-world"), + FF_ARG(counts.emergeSys, "emerge-system"), + FF_ARG(counts.emergeDeps, "emerge-deps"), FF_ARG(counts.eopkg, "eopkg"), FF_ARG(counts.xbps, "xbps"), FF_ARG(counts.nixSystem, "nix-system"), @@ -408,6 +421,9 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(choco) FF_APPEND_PACKAGE_COUNT(dpkg) FF_APPEND_PACKAGE_COUNT(emerge) + FF_APPEND_PACKAGE_COUNT(emergeDeps) + FF_APPEND_PACKAGE_COUNT(emergeSys) + FF_APPEND_PACKAGE_COUNT(emergeWorld) FF_APPEND_PACKAGE_COUNT(eopkg) FF_APPEND_PACKAGE_COUNT(flatpakSystem) FF_APPEND_PACKAGE_COUNT(flatpakUser) @@ -473,7 +489,10 @@ FFModuleBaseInfo ffPackagesModuleInfo = { {"Pacman branch on manjaro", "pacman-branch"}, {"Number of dpkg packages", "dpkg"}, {"Number of rpm packages", "rpm"}, - {"Number of emerge packages", "emerge"}, + {"Number of all combined emerge packages", "emerge"}, + {"Number of @world emerge set packages", "emerge-world"}, + {"Number of @system emerge set packages", "emerge-system"}, + {"Number of emerged dependencies", "emerge-deps"}, {"Number of eopkg packages", "eopkg"}, {"Number of xbps packages", "xbps"}, {"Number of nix-system packages", "nix-system"}, From a673849fbfa2331efb02d7e30a727dd3221c70cf Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 17:43:42 +0800 Subject: [PATCH 02/18] Schema: update `json_schema.json` to accomodate the emerge set differentiation --- doc/json_schema.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index c3e1d76bc9..722a821fc4 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -441,11 +441,11 @@ "description": "Output format of the module `OS`. See Wiki for formatting syntax\n 1. {sysname}: Name of the kernel\n 2. {name}: Name of the OS\n 3. {pretty-name}: Pretty name of the OS, if available\n 4. {id}: ID of the OS\n 5. {id-like}: ID like of the OS\n 6. {variant}: Variant of the OS\n 7. {variant-id}: Variant ID of the OS\n 8. {version}: Version of the OS\n 9. {version-id}: Version ID of the OS\n 10. {codename}: Version codename of the OS\n 11. {build-id}: Build ID of the OS\n 12. {arch}: Architecture of the OS", "type": "string" }, - "packagesFormat": { - "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {kiss}: Number of kiss packages\n 44. {moss}: Number of moss packages\n 45. {nix-all}: Total number of all nix packages\n 46. {flatpak-all}: Total number of all flatpak app packages\n 47. {brew-all}: Total number of all brew packages\n 48. {guix-all}: Total number of all guix packages\n 49. {hpkg-all}: Total number of all hpkg packages", + "packagesFormat": { + "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of all combined emerge packages\n 7. {emerge-world}: Number of @world emerge packages\n 8. {emerge-system}: Number of @system emerge packages\n 9. {emerge-deps}: Number of emerged dependencies\n 10. {eopkg}: Number of eopkg packages\n 11. {xbps}: Number of xbps packages\n 12. {nix-system}: Number of nix-system packages\n 13. {nix-user}: Number of nix-user packages\n 14. {nix-default}: Number of nix-default packages\n 15. {apk}: Number of apk packages\n 16. {pkg}: Number of pkg packages\n 17. {flatpak-system}: Number of flatpak-system app packages\n 18. {flatpak-user}: Number of flatpak-user app packages\n 19. {snap}: Number of snap packages\n 20. {brew}: Number of brew packages\n 21. {brew-cask}: Number of brew-cask packages\n 22. {macports}: Number of macports packages\n 23. {scoop-user}: Number of scoop-user packages\n 24. {scoop-global}: Number of scoop-global packages\n 25. {choco}: Number of choco packages\n 26. {pkgtool}: Number of pkgtool packages\n 27. {paludis}: Number of paludis packages\n 28. {winget}: Number of winget packages\n 29. {opkg}: Number of opkg packages\n 30. {am-system}: Number of am-system packages\n 31. {sorcery}: Number of sorcery packages\n 32. {lpkg}: Number of lpkg packages\n 33. {lpkgbuild}: Number of lpkgbuild packages\n 34. {guix-system}: Number of guix-system packages\n 35. {guix-user}: Number of guix-user packages\n 36. {guix-home}: Number of guix-home packages\n 37. {linglong}: Number of linglong packages\n 38. {pacstall}: Number of pacstall packages\n 39. {mport}: Number of mport packages\n 40. {am-user}: Number of am-user (aka appman) packages\n 41. {pkgsrc}: Number of pkgsrc packages\n 42. {hpkg-system}: Number of hpkg-system packages\n 43. {hpkg-user}: Number of hpkg-user packages\n 44. {pisi}: Number of pisi packages\n 45. {soar}: Number of soar packages\n 46. {kiss}: Number of kiss packages\n 47. {moss}: Number of moss packages\n 48. {nix-all}: Total number of all nix packages\n 49. {flatpak-all}: Total number of all flatpak app packages\n 50. {brew-all}: Total number of all brew packages\n 51. {guix-all}: Total number of all guix packages\n 52. {hpkg-all}: Total number of all hpkg packages", "type": "string" }, - "physicaldiskFormat": { + "physicaldiskFormat": { "description": "Output format of the module `PhysicalDisk`. See Wiki for formatting syntax\n 1. {size}: Device size (formatted)\n 2. {name}: Device name\n 3. {interconnect}: Device interconnect type\n 4. {dev-path}: Device raw file path\n 5. {serial}: Serial number\n 6. {physical-type}: Device kind (SSD or HDD)\n 7. {removable-type}: Device kind (Removable or Fixed)\n 8. {readonly-type}: Device kind (Read-only or Read-write)\n 9. {revision}: Product revision\n 10. {temperature}: Device temperature (formatted)", "type": "string" }, From 5146f6f9c1d241dc7d8d5c65b7ed0e176c393d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anthony=20=E2=85=82L=2E?= <112951819+hydrospherics@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:49:18 +0800 Subject: [PATCH 03/18] Fix: apply suggestions by Copilot Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/detection/packages/packages.h | 5 +-- src/detection/packages/packages_linux.c | 41 +++++++++++++++++++++---- src/modules/packages/packages.c | 4 +-- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index e3bee2dc31..50a1fb5a04 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -12,7 +12,6 @@ typedef struct FFPackagesResult uint32_t brewCask; uint32_t choco; uint32_t dpkg; - uint32_t emerge; uint32_t emergeDeps; uint32_t emergeSys; uint32_t emergeWorld; @@ -20,6 +19,7 @@ typedef struct FFPackagesResult uint32_t flatpakSystem; uint32_t flatpakUser; uint32_t guixHome; + uint32_t guixHome; uint32_t guixSystem; uint32_t guixUser; uint32_t hpkgSystem; @@ -51,7 +51,8 @@ typedef struct FFPackagesResult uint32_t winget; uint32_t xbps; - uint32_t all; //Make sure this goes last + uint32_t all; // Make sure this goes last among package counter fields + uint32_t emerge; FFstrbuf pacmanBranch; } FFPackagesResult; diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 17c94c045d..444a8bfff9 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -1,3 +1,4 @@ +#include #include "packages.h" #include "common/io.h" #include "common/parsing.h" @@ -5,6 +6,7 @@ #include "common/settings.h" #include "common/stringUtils.h" #include "detection/os/os.h" +#include static uint32_t getNumElements(FFstrbuf* baseDir, const char* dirname, bool isdir) { @@ -468,9 +470,21 @@ static uint32_t getProfSysPackages(FFstrbuf* profileDir, uint32_t depth) for (const char* line = content.chars; *line; ) { const char* eol = strchr(line, '\n'); - if (!eol) eol = content.chars + content.length; - if (*line == '*') ++count; - else if (*line == '-' && line + 1 < eol && line[1] == '*' && count > 0) --count; + if (!eol) + eol = content.chars + content.length; + + const char* p = line; + while (p < eol && isspace((unsigned char)*p)) + ++p; + + if (p < eol && *p != '#') + { + if (*p == '*') + ++count; + else if (*p == '-' && count > 0) + --count; + } + line = (*eol) ? eol + 1 : eol; } } @@ -534,7 +548,16 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC { const char* eol = strchr(line, '\n'); if (!eol) eol = content.chars + content.length; - if (*line != '#') ++world; + + /* Skip leading whitespace on the current line */ + const char* p = line; + while (p < eol && isspace((unsigned char)*p)) + ++p; + + /* Count only non-empty, non-comment lines */ + if (p < eol && *p != '#') + ++world; + line = (*eol) ? eol + 1 : eol; } } @@ -544,7 +567,7 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC uint32_t system = 0; { uint32_t baseDirLen = baseDir->length; - ffStrbufAppendS(baseDir, FASTFETCH_TARGET_DIR_ROOT "/etc/portage/make.profile"); + ffStrbufAppendS(baseDir, "/etc/portage/make.profile"); char resolved[PATH_MAX] = {0}; if (realpath(baseDir->chars, resolved)) { @@ -572,7 +595,13 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC ffStrbufSubstrBefore(baseDir, baseDirLen); } - uint32_t deps = (world + system < total) ? total - world - system : 0; + if (world > total) + world = total; + + if (system > total - world) + system = total - world; + + uint32_t deps = total - world - system; packageCounts->emergeWorld += world; packageCounts->emergeSys += system; diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 56e4baecf9..65d5dd4146 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -17,7 +17,7 @@ bool ffPrintPackages(FFPackagesOptions* options) return false; } - uint32_t emergeAll = counts.emerge; + uint32_t emergeAll = counts.emergeWorld + counts.emergeSys + counts.emergeDeps; uint32_t nixAll = counts.nixDefault + counts.nixSystem + counts.nixUser; uint32_t flatpakAll = counts.flatpakSystem + counts.flatpakUser; uint32_t brewAll = counts.brew + counts.brewCask; @@ -169,7 +169,7 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_ARG(counts.pacmanBranch, "pacman-branch"), FF_ARG(counts.dpkg, "dpkg"), FF_ARG(counts.rpm, "rpm"), - FF_ARG(counts.emerge, "emerge"), + FF_ARG(emergeAll, "emerge"), FF_ARG(counts.emergeWorld, "emerge-world"), FF_ARG(counts.emergeSys, "emerge-system"), FF_ARG(counts.emergeDeps, "emerge-deps"), From 3eae5768de7b38a35018b4ff47fddf846d8b59bf Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 20:59:16 +0800 Subject: [PATCH 04/18] fix: indentation from tab to space --- doc/json_schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index 722a821fc4..47595b871e 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -441,11 +441,11 @@ "description": "Output format of the module `OS`. See Wiki for formatting syntax\n 1. {sysname}: Name of the kernel\n 2. {name}: Name of the OS\n 3. {pretty-name}: Pretty name of the OS, if available\n 4. {id}: ID of the OS\n 5. {id-like}: ID like of the OS\n 6. {variant}: Variant of the OS\n 7. {variant-id}: Variant ID of the OS\n 8. {version}: Version of the OS\n 9. {version-id}: Version ID of the OS\n 10. {codename}: Version codename of the OS\n 11. {build-id}: Build ID of the OS\n 12. {arch}: Architecture of the OS", "type": "string" }, - "packagesFormat": { + "packagesFormat": { "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of all combined emerge packages\n 7. {emerge-world}: Number of @world emerge packages\n 8. {emerge-system}: Number of @system emerge packages\n 9. {emerge-deps}: Number of emerged dependencies\n 10. {eopkg}: Number of eopkg packages\n 11. {xbps}: Number of xbps packages\n 12. {nix-system}: Number of nix-system packages\n 13. {nix-user}: Number of nix-user packages\n 14. {nix-default}: Number of nix-default packages\n 15. {apk}: Number of apk packages\n 16. {pkg}: Number of pkg packages\n 17. {flatpak-system}: Number of flatpak-system app packages\n 18. {flatpak-user}: Number of flatpak-user app packages\n 19. {snap}: Number of snap packages\n 20. {brew}: Number of brew packages\n 21. {brew-cask}: Number of brew-cask packages\n 22. {macports}: Number of macports packages\n 23. {scoop-user}: Number of scoop-user packages\n 24. {scoop-global}: Number of scoop-global packages\n 25. {choco}: Number of choco packages\n 26. {pkgtool}: Number of pkgtool packages\n 27. {paludis}: Number of paludis packages\n 28. {winget}: Number of winget packages\n 29. {opkg}: Number of opkg packages\n 30. {am-system}: Number of am-system packages\n 31. {sorcery}: Number of sorcery packages\n 32. {lpkg}: Number of lpkg packages\n 33. {lpkgbuild}: Number of lpkgbuild packages\n 34. {guix-system}: Number of guix-system packages\n 35. {guix-user}: Number of guix-user packages\n 36. {guix-home}: Number of guix-home packages\n 37. {linglong}: Number of linglong packages\n 38. {pacstall}: Number of pacstall packages\n 39. {mport}: Number of mport packages\n 40. {am-user}: Number of am-user (aka appman) packages\n 41. {pkgsrc}: Number of pkgsrc packages\n 42. {hpkg-system}: Number of hpkg-system packages\n 43. {hpkg-user}: Number of hpkg-user packages\n 44. {pisi}: Number of pisi packages\n 45. {soar}: Number of soar packages\n 46. {kiss}: Number of kiss packages\n 47. {moss}: Number of moss packages\n 48. {nix-all}: Total number of all nix packages\n 49. {flatpak-all}: Total number of all flatpak app packages\n 50. {brew-all}: Total number of all brew packages\n 51. {guix-all}: Total number of all guix packages\n 52. {hpkg-all}: Total number of all hpkg packages", "type": "string" }, - "physicaldiskFormat": { + "physicaldiskFormat": { "description": "Output format of the module `PhysicalDisk`. See Wiki for formatting syntax\n 1. {size}: Device size (formatted)\n 2. {name}: Device name\n 3. {interconnect}: Device interconnect type\n 4. {dev-path}: Device raw file path\n 5. {serial}: Serial number\n 6. {physical-type}: Device kind (SSD or HDD)\n 7. {removable-type}: Device kind (Removable or Fixed)\n 8. {readonly-type}: Device kind (Read-only or Read-write)\n 9. {revision}: Product revision\n 10. {temperature}: Device temperature (formatted)", "type": "string" }, From 9058dfbedbce1e551b95c816a03e4f3d89e1e47e Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 21:02:11 +0800 Subject: [PATCH 05/18] chore: rm dupes --- src/detection/packages/packages.h | 1 - src/detection/packages/packages_linux.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index 50a1fb5a04..f87f5ff304 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -19,7 +19,6 @@ typedef struct FFPackagesResult uint32_t flatpakSystem; uint32_t flatpakUser; uint32_t guixHome; - uint32_t guixHome; uint32_t guixSystem; uint32_t guixUser; uint32_t hpkgSystem; diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 444a8bfff9..b5aff46f9f 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -6,7 +6,6 @@ #include "common/settings.h" #include "common/stringUtils.h" #include "detection/os/os.h" -#include static uint32_t getNumElements(FFstrbuf* baseDir, const char* dirname, bool isdir) { From b464d24c65b07bd27c8e73f89e0c4460d4f993f2 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 21:11:00 +0800 Subject: [PATCH 06/18] fix: harden `emerge` parsing and align struct layout --- src/detection/packages/packages_linux.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index b5aff46f9f..2515076877 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -586,8 +586,19 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC { const char* eol = strchr(line, '\n'); if (!eol) eol = content.chars + content.length; - if (*line == '*') ++system; - else if (*line == '-' && line + 1 < eol && line[1] == '*' && system > 0) --system; + + const char* p = line; + while (p < eol && isspace((unsigned char)*p)) + ++p; + + if (p < eol && *p != '#') + { + if (*p == '*') + ++system; + else if (*p == '-' && system > 0) + --system; + } + line = (*eol) ? eol + 1 : eol; } } From 2f45a5e0f50d53b130c8dfc8cf21476029b74a82 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 22:33:53 +0800 Subject: [PATCH 07/18] Fix: lower depth num to 16 --- src/detection/packages/packages_linux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 2515076877..08c6fc7e0c 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -454,7 +454,7 @@ static uint32_t getPacmanPackages(FFstrbuf* baseDir) static uint32_t getProfSysPackages(FFstrbuf* profileDir, uint32_t depth) { - if (depth > 64) + if (depth > 16) return 0; ffStrbufEnsureEndsWithC(profileDir, '/'); @@ -616,7 +616,6 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC packageCounts->emergeWorld += world; packageCounts->emergeSys += system; packageCounts->emergeDeps += deps; - packageCounts->emerge += total; } static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options) From df33abaf9be35c39732237208707cc76171262c5 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 22:41:57 +0800 Subject: [PATCH 08/18] Refactor: rename emerge to emerge-all --- src/detection/packages/packages.h | 1 - src/modules/packages/packages.c | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index f87f5ff304..d7aef5f7be 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -51,7 +51,6 @@ typedef struct FFPackagesResult uint32_t xbps; uint32_t all; // Make sure this goes last among package counter fields - uint32_t emerge; FFstrbuf pacmanBranch; } FFPackagesResult; diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 65d5dd4146..652a97cabe 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -169,7 +169,6 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_ARG(counts.pacmanBranch, "pacman-branch"), FF_ARG(counts.dpkg, "dpkg"), FF_ARG(counts.rpm, "rpm"), - FF_ARG(emergeAll, "emerge"), FF_ARG(counts.emergeWorld, "emerge-world"), FF_ARG(counts.emergeSys, "emerge-system"), FF_ARG(counts.emergeDeps, "emerge-deps"), @@ -216,6 +215,7 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_ARG(brewAll, "brew-all"), FF_ARG(guixAll, "guix-all"), FF_ARG(hpkgAll, "hpkg-all"), + FF_ARG(emergeAll, "emerge-all"), })); } @@ -420,7 +420,6 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(brewCask) FF_APPEND_PACKAGE_COUNT(choco) FF_APPEND_PACKAGE_COUNT(dpkg) - FF_APPEND_PACKAGE_COUNT(emerge) FF_APPEND_PACKAGE_COUNT(emergeDeps) FF_APPEND_PACKAGE_COUNT(emergeSys) FF_APPEND_PACKAGE_COUNT(emergeWorld) @@ -456,6 +455,7 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(sorcery) FF_APPEND_PACKAGE_COUNT(winget) FF_APPEND_PACKAGE_COUNT(xbps) + yyjson_mut_obj_add_uint(doc, obj, "emerge-all", counts.emergeWorld + counts.emergeSys + counts.emergeDeps); yyjson_mut_obj_add_strbuf(doc, obj, "pacmanBranch", &counts.pacmanBranch); return true; @@ -489,7 +489,6 @@ FFModuleBaseInfo ffPackagesModuleInfo = { {"Pacman branch on manjaro", "pacman-branch"}, {"Number of dpkg packages", "dpkg"}, {"Number of rpm packages", "rpm"}, - {"Number of all combined emerge packages", "emerge"}, {"Number of @world emerge set packages", "emerge-world"}, {"Number of @system emerge set packages", "emerge-system"}, {"Number of emerged dependencies", "emerge-deps"}, @@ -536,5 +535,6 @@ FFModuleBaseInfo ffPackagesModuleInfo = { {"Total number of all brew packages", "brew-all"}, {"Total number of all guix packages", "guix-all"}, {"Total number of all hpkg packages", "hpkg-all"}, + {"Total number of all emerge package", "emerge-all"}, })) }; From 2d129553ee4c24fe6a07458fb4cc0950469cd889 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 22:43:23 +0800 Subject: [PATCH 09/18] Fix: improve portage parent parsing and add the header I forgot to put in (why) --- src/detection/packages/packages_linux.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 08c6fc7e0c..42919a93d0 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -1,4 +1,5 @@ #include +#include #include "packages.h" #include "common/io.h" #include "common/parsing.h" @@ -504,16 +505,24 @@ static uint32_t getProfSysPackages(FFstrbuf* profileDir, uint32_t depth) if (!eol) eol = parentContent.chars + parentContent.length; - uint32_t lineLen = (uint32_t)(eol - line); - if (lineLen > 0 && *line != '#') + const char* p = line; + while (p < eol && isspace((unsigned char)*p)) + ++p; + + const char* end = eol; + while (end > p && isspace((unsigned char)*(end - 1))) + --end; + + if (p < end && *p != '#') { FF_STRBUF_AUTO_DESTROY parentPath = ffStrbufCreate(); - if (*line == '/') - ffStrbufSetNS(&parentPath, lineLen, line); + uint32_t pathLen = (uint32_t)(end - p); + if (*p == '/') + ffStrbufSetNS(&parentPath, pathLen, p); else { ffStrbufSet(&parentPath, profileDir); - ffStrbufAppendNS(&parentPath, lineLen, line); + ffStrbufAppendNS(&parentPath, pathLen, p); } char resolved[PATH_MAX] = {0}; From 66c702db9b499752dd15a4ac758f13cbd211362d Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 22:46:23 +0800 Subject: [PATCH 10/18] Doc: update json_schema to replace emerge with emerge-all --- doc/json_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index 47595b871e..af9b008688 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -442,7 +442,7 @@ "type": "string" }, "packagesFormat": { - "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of all combined emerge packages\n 7. {emerge-world}: Number of @world emerge packages\n 8. {emerge-system}: Number of @system emerge packages\n 9. {emerge-deps}: Number of emerged dependencies\n 10. {eopkg}: Number of eopkg packages\n 11. {xbps}: Number of xbps packages\n 12. {nix-system}: Number of nix-system packages\n 13. {nix-user}: Number of nix-user packages\n 14. {nix-default}: Number of nix-default packages\n 15. {apk}: Number of apk packages\n 16. {pkg}: Number of pkg packages\n 17. {flatpak-system}: Number of flatpak-system app packages\n 18. {flatpak-user}: Number of flatpak-user app packages\n 19. {snap}: Number of snap packages\n 20. {brew}: Number of brew packages\n 21. {brew-cask}: Number of brew-cask packages\n 22. {macports}: Number of macports packages\n 23. {scoop-user}: Number of scoop-user packages\n 24. {scoop-global}: Number of scoop-global packages\n 25. {choco}: Number of choco packages\n 26. {pkgtool}: Number of pkgtool packages\n 27. {paludis}: Number of paludis packages\n 28. {winget}: Number of winget packages\n 29. {opkg}: Number of opkg packages\n 30. {am-system}: Number of am-system packages\n 31. {sorcery}: Number of sorcery packages\n 32. {lpkg}: Number of lpkg packages\n 33. {lpkgbuild}: Number of lpkgbuild packages\n 34. {guix-system}: Number of guix-system packages\n 35. {guix-user}: Number of guix-user packages\n 36. {guix-home}: Number of guix-home packages\n 37. {linglong}: Number of linglong packages\n 38. {pacstall}: Number of pacstall packages\n 39. {mport}: Number of mport packages\n 40. {am-user}: Number of am-user (aka appman) packages\n 41. {pkgsrc}: Number of pkgsrc packages\n 42. {hpkg-system}: Number of hpkg-system packages\n 43. {hpkg-user}: Number of hpkg-user packages\n 44. {pisi}: Number of pisi packages\n 45. {soar}: Number of soar packages\n 46. {kiss}: Number of kiss packages\n 47. {moss}: Number of moss packages\n 48. {nix-all}: Total number of all nix packages\n 49. {flatpak-all}: Total number of all flatpak app packages\n 50. {brew-all}: Total number of all brew packages\n 51. {guix-all}: Total number of all guix packages\n 52. {hpkg-all}: Total number of all hpkg packages", + "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge-world}: Number of @world emerge packages\n 7. {emerge-system}: Number of @system emerge packages\n 8. {emerge-deps}: Number of emerged dependencies\n 9. {eopkg}: Number of eopkg packages\n 10. {xbps}: Number of xbps packages\n 11. {nix-system}: Number of nix-system packages\n 12. {nix-user}: Number of nix-user packages\n 13. {nix-default}: Number of nix-default packages\n 14. {apk}: Number of apk packages\n 15. {pkg}: Number of pkg packages\n 16. {flatpak-system}: Number of flatpak-system app packages\n 17. {flatpak-user}: Number of flatpak-user app packages\n 18. {snap}: Number of snap packages\n 19. {brew}: Number of brew packages\n 20. {brew-cask}: Number of brew-cask packages\n 21. {macports}: Number of macports packages\n 22. {scoop-user}: Number of scoop-user packages\n 23. {scoop-global}: Number of scoop-global packages\n 24. {choco}: Number of choco packages\n 25. {pkgtool}: Number of pkgtool packages\n 26. {paludis}: Number of paludis packages\n 27. {winget}: Number of winget packages\n 28. {opkg}: Number of opkg packages\n 29. {am-system}: Number of am-system packages\n 30. {sorcery}: Number of sorcery packages\n 31. {lpkg}: Number of lpkg packages\n 32. {lpkgbuild}: Number of lpkgbuild packages\n 33. {guix-system}: Number of guix-system packages\n 34. {guix-user}: Number of guix-user packages\n 35. {guix-home}: Number of guix-home packages\n 36. {linglong}: Number of linglong packages\n 37. {pacstall}: Number of pacstall packages\n 38. {mport}: Number of mport packages\n 39. {am-user}: Number of am-user (aka appman) packages\n 40. {pkgsrc}: Number of pkgsrc packages\n 41. {hpkg-system}: Number of hpkg-system packages\n 42. {hpkg-user}: Number of hpkg-user packages\n 43. {pisi}: Number of pisi packages\n 44. {soar}: Number of soar packages\n 45. {kiss}: Number of kiss packages\n 46. {moss}: Number of moss packages\n 47. {nix-all}: Total number of all nix packages\n 48. {flatpak-all}: Total number of all flatpak app packages\n 49. {brew-all}: Total number of all brew packages\n 50. {guix-all}: Total number of all guix packages\n 51. {hpkg-all}: Total number of all hpkg packages\n 52. {emerge-all}: Number of all combined emerge packages", "type": "string" }, "physicaldiskFormat": { From f2f008e4fb399cacd6597d0c050fbefb49882658 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 23:33:22 +0800 Subject: [PATCH 11/18] Doc: update json_schema (again) --- doc/json_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index af9b008688..60905a291e 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -442,7 +442,7 @@ "type": "string" }, "packagesFormat": { - "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge-world}: Number of @world emerge packages\n 7. {emerge-system}: Number of @system emerge packages\n 8. {emerge-deps}: Number of emerged dependencies\n 9. {eopkg}: Number of eopkg packages\n 10. {xbps}: Number of xbps packages\n 11. {nix-system}: Number of nix-system packages\n 12. {nix-user}: Number of nix-user packages\n 13. {nix-default}: Number of nix-default packages\n 14. {apk}: Number of apk packages\n 15. {pkg}: Number of pkg packages\n 16. {flatpak-system}: Number of flatpak-system app packages\n 17. {flatpak-user}: Number of flatpak-user app packages\n 18. {snap}: Number of snap packages\n 19. {brew}: Number of brew packages\n 20. {brew-cask}: Number of brew-cask packages\n 21. {macports}: Number of macports packages\n 22. {scoop-user}: Number of scoop-user packages\n 23. {scoop-global}: Number of scoop-global packages\n 24. {choco}: Number of choco packages\n 25. {pkgtool}: Number of pkgtool packages\n 26. {paludis}: Number of paludis packages\n 27. {winget}: Number of winget packages\n 28. {opkg}: Number of opkg packages\n 29. {am-system}: Number of am-system packages\n 30. {sorcery}: Number of sorcery packages\n 31. {lpkg}: Number of lpkg packages\n 32. {lpkgbuild}: Number of lpkgbuild packages\n 33. {guix-system}: Number of guix-system packages\n 34. {guix-user}: Number of guix-user packages\n 35. {guix-home}: Number of guix-home packages\n 36. {linglong}: Number of linglong packages\n 37. {pacstall}: Number of pacstall packages\n 38. {mport}: Number of mport packages\n 39. {am-user}: Number of am-user (aka appman) packages\n 40. {pkgsrc}: Number of pkgsrc packages\n 41. {hpkg-system}: Number of hpkg-system packages\n 42. {hpkg-user}: Number of hpkg-user packages\n 43. {pisi}: Number of pisi packages\n 44. {soar}: Number of soar packages\n 45. {kiss}: Number of kiss packages\n 46. {moss}: Number of moss packages\n 47. {nix-all}: Total number of all nix packages\n 48. {flatpak-all}: Total number of all flatpak app packages\n 49. {brew-all}: Total number of all brew packages\n 50. {guix-all}: Total number of all guix packages\n 51. {hpkg-all}: Total number of all hpkg packages\n 52. {emerge-all}: Number of all combined emerge packages", + "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge-world}: Number of @world emerge packages\n 7. {emerge-system}: Number of @system emerge packages\n 8. {eopkg}: Number of eopkg packages\n 9. {xbps}: Number of xbps packages\n 10. {nix-system}: Number of nix-system packages\n 11. {nix-user}: Number of nix-user packages\n 12. {nix-default}: Number of nix-default packages\n 13. {apk}: Number of apk packages\n 14. {pkg}: Number of pkg packages\n 15. {flatpak-system}: Number of flatpak-system app packages\n 16. {flatpak-user}: Number of flatpak-user app packages\n 17. {snap}: Number of snap packages\n 18. {brew}: Number of brew packages\n 19. {brew-cask}: Number of brew-cask packages\n 20. {macports}: Number of macports packages\n 21. {scoop-user}: Number of scoop-user packages\n 22. {scoop-global}: Number of scoop-global packages\n 23. {choco}: Number of choco packages\n 24. {pkgtool}: Number of pkgtool packages\n 25. {paludis}: Number of paludis packages\n 26. {winget}: Number of winget packages\n 27. {opkg}: Number of opkg packages\n 28. {am-system}: Number of am-system packages\n 29. {sorcery}: Number of sorcery packages\n 30. {lpkg}: Number of lpkg packages\n 31. {lpkgbuild}: Number of lpkgbuild packages\n 32. {guix-system}: Number of guix-system packages\n 33. {guix-user}: Number of guix-user packages\n 34. {guix-home}: Number of guix-home packages\n 35. {linglong}: Number of linglong packages\n 36. {pacstall}: Number of pacstall packages\n 37. {mport}: Number of mport packages\n 38. {am-user}: Number of am-user (aka appman) packages\n 39. {pkgsrc}: Number of pkgsrc packages\n 40. {hpkg-system}: Number of hpkg-system packages\n 41. {hpkg-user}: Number of hpkg-user packages\n 42. {pisi}: Number of pisi packages\n 43. {soar}: Number of soar packages\n 44. {kiss}: Number of kiss packages\n 45. {moss}: Number of moss packages\n 46. {nix-all}: Total number of all nix packages\n 47. {flatpak-all}: Total number of all flatpak app packages\n 48. {brew-all}: Total number of all brew packages\n 49. {guix-all}: Total number of all guix packages\n 50. {hpkg-all}: Total number of all hpkg packages\n 51. {emerge-all}: Number of all combined emerge packages", "type": "string" }, "physicaldiskFormat": { From d48251b7a502f93aae897fa368430f1064766a86 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 23:35:03 +0800 Subject: [PATCH 12/18] Refactor: obliterate old `@system` recursive parsing --- src/detection/packages/packages_linux.c | 155 +----------------------- 1 file changed, 2 insertions(+), 153 deletions(-) diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 42919a93d0..3363782c88 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -453,92 +453,6 @@ static uint32_t getPacmanPackages(FFstrbuf* baseDir) return getNumElements(baseDir, dbPath.chars, true); } -static uint32_t getProfSysPackages(FFstrbuf* profileDir, uint32_t depth) -{ - if (depth > 16) - return 0; - - ffStrbufEnsureEndsWithC(profileDir, '/'); - uint32_t profileDirLen = profileDir->length; - - uint32_t count = 0; - { - ffStrbufAppendS(profileDir, "packages"); - FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); - if (ffReadFileBuffer(profileDir->chars, &content)) - { - for (const char* line = content.chars; *line; ) - { - const char* eol = strchr(line, '\n'); - if (!eol) - eol = content.chars + content.length; - - const char* p = line; - while (p < eol && isspace((unsigned char)*p)) - ++p; - - if (p < eol && *p != '#') - { - if (*p == '*') - ++count; - else if (*p == '-' && count > 0) - --count; - } - - line = (*eol) ? eol + 1 : eol; - } - } - ffStrbufSubstrBefore(profileDir, profileDirLen); - } - - ffStrbufAppendS(profileDir, "parent"); - FF_STRBUF_AUTO_DESTROY parentContent = ffStrbufCreate(); - bool ok = ffReadFileBuffer(profileDir->chars, &parentContent); - ffStrbufSubstrBefore(profileDir, profileDirLen); - - if (!ok) - return count; - - for (const char* line = parentContent.chars; *line; ) - { - const char* eol = strchr(line, '\n'); - if (!eol) - eol = parentContent.chars + parentContent.length; - - const char* p = line; - while (p < eol && isspace((unsigned char)*p)) - ++p; - - const char* end = eol; - while (end > p && isspace((unsigned char)*(end - 1))) - --end; - - if (p < end && *p != '#') - { - FF_STRBUF_AUTO_DESTROY parentPath = ffStrbufCreate(); - uint32_t pathLen = (uint32_t)(end - p); - if (*p == '/') - ffStrbufSetNS(&parentPath, pathLen, p); - else - { - ffStrbufSet(&parentPath, profileDir); - ffStrbufAppendNS(&parentPath, pathLen, p); - } - - char resolved[PATH_MAX] = {0}; - if (realpath(parentPath.chars, resolved)) - { - FF_STRBUF_AUTO_DESTROY resolvedBuf = ffStrbufCreateS(resolved); - count += getProfSysPackages(&resolvedBuf, depth + 1); - } - } - - line = (*eol) ? eol + 1 : eol; - } - - return count; -} - static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageCounts) { uint32_t total = countFilesRecursive(baseDir, "/var/db/pkg", "SIZE"); @@ -551,80 +465,15 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC ffStrbufAppendS(baseDir, "/var/lib/portage/world"); FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); if (ffReadFileBuffer(baseDir->chars, &content)) - { - for (const char* line = content.chars; *line; ) - { - const char* eol = strchr(line, '\n'); - if (!eol) eol = content.chars + content.length; - - /* Skip leading whitespace on the current line */ - const char* p = line; - while (p < eol && isspace((unsigned char)*p)) - ++p; - - /* Count only non-empty, non-comment lines */ - if (p < eol && *p != '#') - ++world; - - line = (*eol) ? eol + 1 : eol; - } - } - ffStrbufSubstrBefore(baseDir, baseDirLen); - } - - uint32_t system = 0; - { - uint32_t baseDirLen = baseDir->length; - ffStrbufAppendS(baseDir, "/etc/portage/make.profile"); - char resolved[PATH_MAX] = {0}; - if (realpath(baseDir->chars, resolved)) - { - FF_STRBUF_AUTO_DESTROY profileDir = ffStrbufCreateS(resolved); - system = getProfSysPackages(&profileDir, 0); - } - ffStrbufSubstrBefore(baseDir, baseDirLen); - } - - { - uint32_t baseDirLen = baseDir->length; - ffStrbufAppendS(baseDir, "/etc/portage/profile/packages"); - FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); - if (ffReadFileBuffer(baseDir->chars, &content)) - { - for (const char* line = content.chars; *line; ) - { - const char* eol = strchr(line, '\n'); - if (!eol) eol = content.chars + content.length; - - const char* p = line; - while (p < eol && isspace((unsigned char)*p)) - ++p; - - if (p < eol && *p != '#') - { - if (*p == '*') - ++system; - else if (*p == '-' && system > 0) - --system; - } - - line = (*eol) ? eol + 1 : eol; - } - } + world = ffStrbufCountC(&content, '\n'); ffStrbufSubstrBefore(baseDir, baseDirLen); } if (world > total) world = total; - if (system > total - world) - system = total - world; - - uint32_t deps = total - world - system; - packageCounts->emergeWorld += world; - packageCounts->emergeSys += system; - packageCounts->emergeDeps += deps; + packageCounts->emergeSystem += total - world; } static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options) From 9d5078c7f73172b92ceff75373748138636681d8 Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Tue, 17 Mar 2026 23:36:09 +0800 Subject: [PATCH 13/18] Refactor: combine `emerge` package fields and rename labels --- src/detection/packages/packages.h | 3 +-- src/modules/packages/packages.c | 18 +++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index d7aef5f7be..1b31495de5 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -12,8 +12,7 @@ typedef struct FFPackagesResult uint32_t brewCask; uint32_t choco; uint32_t dpkg; - uint32_t emergeDeps; - uint32_t emergeSys; + uint32_t emergeSystem; uint32_t emergeWorld; uint32_t eopkg; uint32_t flatpakSystem; diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 652a97cabe..73645f1531 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -17,7 +17,7 @@ bool ffPrintPackages(FFPackagesOptions* options) return false; } - uint32_t emergeAll = counts.emergeWorld + counts.emergeSys + counts.emergeDeps; + uint32_t emergeAll = counts.emergeWorld + counts.emergeSystem; uint32_t nixAll = counts.nixDefault + counts.nixSystem + counts.nixUser; uint32_t flatpakAll = counts.flatpakSystem + counts.flatpakUser; uint32_t brewAll = counts.brew + counts.brewCask; @@ -63,13 +63,12 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(rpm) if (options->combined) { - FF_PRINT_PACKAGE_ALL(emerge); + FF_PRINT_PACKAGE_NAME(emergeAll, "emerge"); } else { FF_PRINT_PACKAGE_NAME(emergeWorld, "emerge-world") - FF_PRINT_PACKAGE_NAME(emergeSys, "emerge-system") - FF_PRINT_PACKAGE_NAME(emergeDeps, "emerge-deps") + FF_PRINT_PACKAGE_NAME(emergeSystem, "emerge-system") } FF_PRINT_PACKAGE(eopkg) FF_PRINT_PACKAGE(xbps) @@ -170,8 +169,7 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_ARG(counts.dpkg, "dpkg"), FF_ARG(counts.rpm, "rpm"), FF_ARG(counts.emergeWorld, "emerge-world"), - FF_ARG(counts.emergeSys, "emerge-system"), - FF_ARG(counts.emergeDeps, "emerge-deps"), + FF_ARG(counts.emergeSystem, "emerge-system"), FF_ARG(counts.eopkg, "eopkg"), FF_ARG(counts.xbps, "xbps"), FF_ARG(counts.nixSystem, "nix-system"), @@ -420,8 +418,7 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(brewCask) FF_APPEND_PACKAGE_COUNT(choco) FF_APPEND_PACKAGE_COUNT(dpkg) - FF_APPEND_PACKAGE_COUNT(emergeDeps) - FF_APPEND_PACKAGE_COUNT(emergeSys) + FF_APPEND_PACKAGE_COUNT(emergeSystem) FF_APPEND_PACKAGE_COUNT(emergeWorld) FF_APPEND_PACKAGE_COUNT(eopkg) FF_APPEND_PACKAGE_COUNT(flatpakSystem) @@ -455,7 +452,7 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(sorcery) FF_APPEND_PACKAGE_COUNT(winget) FF_APPEND_PACKAGE_COUNT(xbps) - yyjson_mut_obj_add_uint(doc, obj, "emerge-all", counts.emergeWorld + counts.emergeSys + counts.emergeDeps); + yyjson_mut_obj_add_uint(doc, obj, "emerge-all", counts.emergeWorld + counts.emergeSystem); yyjson_mut_obj_add_strbuf(doc, obj, "pacmanBranch", &counts.pacmanBranch); return true; @@ -490,8 +487,7 @@ FFModuleBaseInfo ffPackagesModuleInfo = { {"Number of dpkg packages", "dpkg"}, {"Number of rpm packages", "rpm"}, {"Number of @world emerge set packages", "emerge-world"}, - {"Number of @system emerge set packages", "emerge-system"}, - {"Number of emerged dependencies", "emerge-deps"}, + {"Number of non-world emerge set packages", "emerge-system"}, {"Number of eopkg packages", "eopkg"}, {"Number of xbps packages", "xbps"}, {"Number of nix-system packages", "nix-system"}, From 48d522ba566900df512dce8f799ebd54f6c7e649 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 18 Mar 2026 08:39:45 +0800 Subject: [PATCH 14/18] Remove 'emerge-all' count from JSON object Removed the addition of 'emerge-all' count to the JSON object. --- src/modules/packages/packages.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 73645f1531..bd821c4f30 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -452,7 +452,6 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(sorcery) FF_APPEND_PACKAGE_COUNT(winget) FF_APPEND_PACKAGE_COUNT(xbps) - yyjson_mut_obj_add_uint(doc, obj, "emerge-all", counts.emergeWorld + counts.emergeSystem); yyjson_mut_obj_add_strbuf(doc, obj, "pacmanBranch", &counts.pacmanBranch); return true; From 7686130abdf81f66a20cd9dfdfdf4f4d8063170b Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 18 Mar 2026 08:46:56 +0800 Subject: [PATCH 15/18] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/modules/packages/packages.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index bd821c4f30..62f431314d 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -530,6 +530,6 @@ FFModuleBaseInfo ffPackagesModuleInfo = { {"Total number of all brew packages", "brew-all"}, {"Total number of all guix packages", "guix-all"}, {"Total number of all hpkg packages", "hpkg-all"}, - {"Total number of all emerge package", "emerge-all"}, + {"Total number of all emerge packages", "emerge-all"}, })) }; From 06ad1e15da6c3e6b9fb8c31b6373b2d2c6356e34 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 18 Mar 2026 08:52:54 +0800 Subject: [PATCH 16/18] Replace FF_PRINT_PACKAGE_NAME with FF_PRINT_PACKAGE_ALL --- src/modules/packages/packages.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 62f431314d..d6391e90fe 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -63,7 +63,7 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(rpm) if (options->combined) { - FF_PRINT_PACKAGE_NAME(emergeAll, "emerge"); + FF_PRINT_PACKAGE_ALL(emerge); } else { From 5c70986ffcd8b3a71a8f49654fed820955abc8e7 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 17 Mar 2026 20:48:21 -0500 Subject: [PATCH 17/18] Fix reading world file and count lines correctly --- src/detection/packages/packages_linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 3363782c88..ce55b970f3 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -465,7 +465,11 @@ static void getPackageCountsEmerge(FFstrbuf* baseDir, FFPackagesResult* packageC ffStrbufAppendS(baseDir, "/var/lib/portage/world"); FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); if (ffReadFileBuffer(baseDir->chars, &content)) + { world = ffStrbufCountC(&content, '\n'); + if (!ffStrbufEndsWithC(&content, '\n')) + ++world; + } ffStrbufSubstrBefore(baseDir, baseDirLen); } From aa3a1412285894af3bb232f5931d4e62d0189a7d Mon Sep 17 00:00:00 2001 From: Anthony Lazara Date: Wed, 18 Mar 2026 17:29:24 +0800 Subject: [PATCH 18/18] Doc: correct `emerge-system` desc --- doc/json_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index 60905a291e..56d5aeb688 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -442,7 +442,7 @@ "type": "string" }, "packagesFormat": { - "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge-world}: Number of @world emerge packages\n 7. {emerge-system}: Number of @system emerge packages\n 8. {eopkg}: Number of eopkg packages\n 9. {xbps}: Number of xbps packages\n 10. {nix-system}: Number of nix-system packages\n 11. {nix-user}: Number of nix-user packages\n 12. {nix-default}: Number of nix-default packages\n 13. {apk}: Number of apk packages\n 14. {pkg}: Number of pkg packages\n 15. {flatpak-system}: Number of flatpak-system app packages\n 16. {flatpak-user}: Number of flatpak-user app packages\n 17. {snap}: Number of snap packages\n 18. {brew}: Number of brew packages\n 19. {brew-cask}: Number of brew-cask packages\n 20. {macports}: Number of macports packages\n 21. {scoop-user}: Number of scoop-user packages\n 22. {scoop-global}: Number of scoop-global packages\n 23. {choco}: Number of choco packages\n 24. {pkgtool}: Number of pkgtool packages\n 25. {paludis}: Number of paludis packages\n 26. {winget}: Number of winget packages\n 27. {opkg}: Number of opkg packages\n 28. {am-system}: Number of am-system packages\n 29. {sorcery}: Number of sorcery packages\n 30. {lpkg}: Number of lpkg packages\n 31. {lpkgbuild}: Number of lpkgbuild packages\n 32. {guix-system}: Number of guix-system packages\n 33. {guix-user}: Number of guix-user packages\n 34. {guix-home}: Number of guix-home packages\n 35. {linglong}: Number of linglong packages\n 36. {pacstall}: Number of pacstall packages\n 37. {mport}: Number of mport packages\n 38. {am-user}: Number of am-user (aka appman) packages\n 39. {pkgsrc}: Number of pkgsrc packages\n 40. {hpkg-system}: Number of hpkg-system packages\n 41. {hpkg-user}: Number of hpkg-user packages\n 42. {pisi}: Number of pisi packages\n 43. {soar}: Number of soar packages\n 44. {kiss}: Number of kiss packages\n 45. {moss}: Number of moss packages\n 46. {nix-all}: Total number of all nix packages\n 47. {flatpak-all}: Total number of all flatpak app packages\n 48. {brew-all}: Total number of all brew packages\n 49. {guix-all}: Total number of all guix packages\n 50. {hpkg-all}: Total number of all hpkg packages\n 51. {emerge-all}: Number of all combined emerge packages", + "description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge-world}: Number of @world emerge packages\n 7. {emerge-system}: Number of non-world emerge set packages\n 8. {eopkg}: Number of eopkg packages\n 9. {xbps}: Number of xbps packages\n 10. {nix-system}: Number of nix-system packages\n 11. {nix-user}: Number of nix-user packages\n 12. {nix-default}: Number of nix-default packages\n 13. {apk}: Number of apk packages\n 14. {pkg}: Number of pkg packages\n 15. {flatpak-system}: Number of flatpak-system app packages\n 16. {flatpak-user}: Number of flatpak-user app packages\n 17. {snap}: Number of snap packages\n 18. {brew}: Number of brew packages\n 19. {brew-cask}: Number of brew-cask packages\n 20. {macports}: Number of macports packages\n 21. {scoop-user}: Number of scoop-user packages\n 22. {scoop-global}: Number of scoop-global packages\n 23. {choco}: Number of choco packages\n 24. {pkgtool}: Number of pkgtool packages\n 25. {paludis}: Number of paludis packages\n 26. {winget}: Number of winget packages\n 27. {opkg}: Number of opkg packages\n 28. {am-system}: Number of am-system packages\n 29. {sorcery}: Number of sorcery packages\n 30. {lpkg}: Number of lpkg packages\n 31. {lpkgbuild}: Number of lpkgbuild packages\n 32. {guix-system}: Number of guix-system packages\n 33. {guix-user}: Number of guix-user packages\n 34. {guix-home}: Number of guix-home packages\n 35. {linglong}: Number of linglong packages\n 36. {pacstall}: Number of pacstall packages\n 37. {mport}: Number of mport packages\n 38. {am-user}: Number of am-user (aka appman) packages\n 39. {pkgsrc}: Number of pkgsrc packages\n 40. {hpkg-system}: Number of hpkg-system packages\n 41. {hpkg-user}: Number of hpkg-user packages\n 42. {pisi}: Number of pisi packages\n 43. {soar}: Number of soar packages\n 44. {kiss}: Number of kiss packages\n 45. {moss}: Number of moss packages\n 46. {nix-all}: Total number of all nix packages\n 47. {flatpak-all}: Total number of all flatpak app packages\n 48. {brew-all}: Total number of all brew packages\n 49. {guix-all}: Total number of all guix packages\n 50. {hpkg-all}: Total number of all hpkg packages\n 51. {emerge-all}: Number of all combined emerge packages", "type": "string" }, "physicaldiskFormat": {