diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/packages/packages_nix.c | |
Add the files
Diffstat (limited to 'src/detection/packages/packages_nix.c')
| -rw-r--r-- | src/detection/packages/packages_nix.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/detection/packages/packages_nix.c b/src/detection/packages/packages_nix.c new file mode 100644 index 0000000..d1cc264 --- /dev/null +++ b/src/detection/packages/packages_nix.c @@ -0,0 +1,149 @@ +#include "packages.h" +#include "common/io.h" +#include "common/processing.h" +#include "common/strutil.h" + +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: + if (ffCharIsDigit(c)) { + state = DIGIT; + } + break; + case DIGIT: + if (ffCharIsDigit(c)) { + continue; + } + if (c == '.') { + state = DOT; + } else { + state = START; + } + break; + case DOT: + if (ffCharIsDigit(c)) { + 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; + } + + FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreate(); + 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) { + FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreateCopy(hash); + ffStrbufAppendF(&cacheContent, "\n%u", count); + return ffWriteFileBuffer(cacheDir->chars, &cacheContent); +} + +static uint32_t getNixPackagesImpl(char* path) { + // Nix detection is kinda slow, so we only do it if the dir exists + if (!ffPathExists(path, FF_PATHTYPE_DIRECTORY)) { + return 0; + } + + FF_STRBUF_AUTO_DESTROY cacheDir = ffStrbufCreateCopy(&instance.state.platform.cacheDir); + 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 }); + + 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: + // https://github.com/fastfetch-cli/fastfetch/issues/195#issuecomment-1191748222 + + FF_STRBUF_AUTO_DESTROY output = ffStrbufCreateA(1024); + + ffProcessAppendStdOut(&output, (char* const[]) { "nix-store", "--query", "--requisites", path, NULL }); + + 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; + } + + writeNixCache(&cacheDir, &hash, count); + return count; +} + +uint32_t ffPackagesGetNix(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; +} |