Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5f16c18
Packages (emerge): differentiate `@world` set and `@system` set from …
hydrospherics Mar 17, 2026
a673849
Schema: update `json_schema.json` to accomodate the emerge set differ…
hydrospherics Mar 17, 2026
5146f6f
Fix: apply suggestions by Copilot
hydrospherics Mar 17, 2026
3eae576
fix: indentation from tab to space
hydrospherics Mar 17, 2026
9058dfb
chore: rm dupes
hydrospherics Mar 17, 2026
b464d24
fix: harden `emerge` parsing and align struct layout
hydrospherics Mar 17, 2026
2f45a5e
Fix: lower depth num to 16
hydrospherics Mar 17, 2026
df33aba
Refactor: rename emerge to emerge-all
hydrospherics Mar 17, 2026
2d12955
Fix: improve portage parent parsing and add the header I forgot to pu…
hydrospherics Mar 17, 2026
66c702d
Doc: update json_schema to replace emerge with emerge-all
hydrospherics Mar 17, 2026
f2f008e
Doc: update json_schema (again)
hydrospherics Mar 17, 2026
d48251b
Refactor: obliterate old `@system` recursive parsing
hydrospherics Mar 17, 2026
9d5078c
Refactor: combine `emerge` package fields and rename labels
hydrospherics Mar 17, 2026
48d522b
Remove 'emerge-all' count from JSON object
CarterLi Mar 18, 2026
7686130
Potential fix for pull request finding
CarterLi Mar 18, 2026
06ad1e1
Replace FF_PRINT_PACKAGE_NAME with FF_PRINT_PACKAGE_ALL
CarterLi Mar 18, 2026
5c70986
Fix reading world file and count lines correctly
CarterLi Mar 18, 2026
f859070
Chore: merge new changes from `dev` branch
hydrospherics Mar 18, 2026
aa3a141
Doc: correct `emerge-system` desc
hydrospherics Mar 18, 2026
d593cf3
Chore: merge new commits from `dev`
hydrospherics Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/json_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 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",
"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": {
Expand Down
7 changes: 5 additions & 2 deletions src/detection/packages/packages.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ typedef struct FFPackagesResult
uint32_t brewCask;
uint32_t choco;
uint32_t dpkg;
uint32_t emerge;
uint32_t emergeDeps;
uint32_t emergeSys;
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
uint32_t emergeWorld;
uint32_t eopkg;
uint32_t flatpakSystem;
uint32_t flatpakUser;
Expand Down Expand Up @@ -48,7 +50,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
Comment thread
hydrospherics marked this conversation as resolved.
uint32_t emerge;
Comment thread
hydrospherics marked this conversation as resolved.
Outdated

FFstrbuf pacmanBranch;
} FFPackagesResult;
Expand Down
170 changes: 169 additions & 1 deletion src/detection/packages/packages_linux.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <ctype.h>
Comment thread
hydrospherics marked this conversation as resolved.
#include "packages.h"
#include "common/io.h"
#include "common/parsing.h"
Expand Down Expand Up @@ -451,12 +452,179 @@ static uint32_t getPacmanPackages(FFstrbuf* baseDir)
return getNumElements(baseDir, dbPath.chars, true);
}

static uint32_t getProfSysPackages(FFstrbuf* profileDir, uint32_t depth)
{
if (depth > 64)
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
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;

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);
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
}

char resolved[PATH_MAX] = {0};
if (realpath(parentPath.chars, resolved))
{
FF_STRBUF_AUTO_DESTROY resolvedBuf = ffStrbufCreateS(resolved);
count += getProfSysPackages(&resolvedBuf, depth + 1);
}
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
}

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;

/* 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);
}
Comment thread
hydrospherics marked this conversation as resolved.
Outdated

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;
}
}
ffStrbufSubstrBefore(baseDir, baseDirLen);
Comment thread
hydrospherics marked this conversation as resolved.
}

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->emerge += total;
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
}
Comment thread
hydrospherics marked this conversation as resolved.

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);
Expand Down
25 changes: 22 additions & 3 deletions src/modules/packages/packages.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ bool ffPrintPackages(FFPackagesOptions* options)
return false;
}

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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -159,7 +169,10 @@ 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"),
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
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"),
Expand Down Expand Up @@ -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)
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
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)
Expand Down Expand Up @@ -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"},
Comment thread
hydrospherics marked this conversation as resolved.
Outdated
{"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"},
Expand Down
Loading