mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-11-09 06:30:10 +01:00
Global: fixes a lot bugs found by AI
This commit is contained in:
@@ -85,11 +85,29 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
|
||||
|
||||
switch (moduleType[0]) {
|
||||
#if !FF_MODULE_DISABLE_CPUUSAGE
|
||||
#if !FF_MODULE_DISABLE_CPUUSAGE || !FF_MODULE_DISABLE_COMMAND
|
||||
case 'C':
|
||||
case 'c':
|
||||
#endif
|
||||
#if !FF_MODULE_DISABLE_CPUUSAGE
|
||||
FF_IF_MODULE_MATCH(ffCPUUsageModuleInfo.name)
|
||||
ffPrepareCPUUsage(); // The rate is derived from the CPU's own counters; no options are involved
|
||||
#endif
|
||||
|
||||
#if !FF_MODULE_DISABLE_COMMAND
|
||||
FF_IF_MODULE_MATCH(ffCommandModuleInfo.name) {
|
||||
// `command` runs its shell commands in parallel: `ffPrepareCommand` spawns them here,
|
||||
// in the prepare pass, and `ffDetectCommand` collects them in the print pass. The
|
||||
// command line itself can only be read from the JSON config, and this code path does
|
||||
// not consult it, so nothing would ever be queued and the module would report an
|
||||
// internal error marker. Refuse it up front instead of letting it get that far.
|
||||
fputs("Error: module `command` is not supported with `--structure`\n", stderr);
|
||||
fputs(" Its options can only be read from the JSON config, which `--structure` bypasses.\n", stderr);
|
||||
fputs(" Add it to the config instead, e.g. `{ \"modules\": [ { \"type\": \"command\", \"text\": \"uname -r\" } ] }`\n", stderr);
|
||||
exit(481);
|
||||
}
|
||||
#endif
|
||||
#if !FF_MODULE_DISABLE_CPUUSAGE || !FF_MODULE_DISABLE_COMMAND
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -329,6 +329,10 @@ FFNativeFD ffGetNullFD(void) {
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffIsTerminal(int fd) {
|
||||
return isatty(fd) != 0;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName) {
|
||||
return unlink(fileName) == 0;
|
||||
}
|
||||
|
||||
@@ -500,6 +500,17 @@ FFNativeFD ffGetNullFD(void) {
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffIsTerminal(int fd) {
|
||||
intptr_t handle = _get_osfhandle(fd);
|
||||
if (handle == -1 || handle == -2) {
|
||||
// -1: an invalid fd; -2: a valid fd with no file behind it (`_get_osfhandle`'s way of
|
||||
// saying FF_INVALID_FD). Neither is a terminal.
|
||||
return false;
|
||||
}
|
||||
DWORD mode;
|
||||
return GetConsoleMode((HANDLE) handle, &mode) != 0;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName) {
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG len;
|
||||
|
||||
@@ -60,6 +60,22 @@ void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* m
|
||||
}
|
||||
}
|
||||
|
||||
bool ffJsonConfigParseUInt32(yyjson_val* val, uint32_t* result, uint32_t max) {
|
||||
if (!yyjson_is_int(val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// `yyjson_get_sint()` is exact for every value that fits into `int64_t`, and is negative for the
|
||||
// `uint64_t` values above `INT64_MAX`, which no caller accepts anyway.
|
||||
int64_t num = unsafe_yyjson_get_sint(val);
|
||||
if (num < 0 || (uint64_t) num > max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = (uint32_t) num;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]) {
|
||||
if (yyjson_is_int(val)) {
|
||||
int intVal = yyjson_get_int(val);
|
||||
@@ -311,6 +327,16 @@ static const char* printJsonConfig(FFdata* data, bool prepare) {
|
||||
if (!unsafe_yyjson_is_bool(previousSucceeded)) {
|
||||
return "Property 'succeeded' in 'condition' must be a boolean";
|
||||
}
|
||||
if (prepare) {
|
||||
// Whether this module is printed at all depends on the result of the previous
|
||||
// one, which is only known in the print pass. Preparing it here would leave a
|
||||
// result behind that no print pass ever consumes, and since the modules are
|
||||
// paired by position (see `ffPrepareCommand` / `ffDetectCommand`), that
|
||||
// would silently shift every later module onto the wrong result.
|
||||
// Skip it, and let the module report the missing preparation if it turns out
|
||||
// to be printed after all.
|
||||
continue;
|
||||
}
|
||||
if (succeeded != unsafe_yyjson_get_bool(previousSucceeded)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "common/textModifier.h"
|
||||
#include "logo/logo.h"
|
||||
|
||||
void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType) {
|
||||
void ffPrintLogoAndKey(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType) {
|
||||
ffLogoPrintLine();
|
||||
|
||||
// This is used by --set-keyless, in this case we want neither the module name nor the separator
|
||||
@@ -40,7 +40,7 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
|
||||
fputs(moduleName, stdout);
|
||||
|
||||
if (moduleIndex > 0) {
|
||||
printf(" %hhu", moduleIndex);
|
||||
printf(" %u", moduleIndex);
|
||||
}
|
||||
} else {
|
||||
FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
|
||||
@@ -82,7 +82,7 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
|
||||
}
|
||||
}
|
||||
|
||||
bool ffPrintFormat(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
bool ffPrintFormat(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
bool success;
|
||||
if (__builtin_expect(moduleArgs != nullptr, 1)) {
|
||||
@@ -102,7 +102,7 @@ bool ffPrintFormat(const char* moduleName, uint8_t moduleIndex, const FFModuleAr
|
||||
return success;
|
||||
}
|
||||
|
||||
void ffPrintError(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...) {
|
||||
void ffPrintError(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...) {
|
||||
if (!instance.config.display.showErrors) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -286,6 +286,13 @@ static inline void wrapClosedir(HANDLE* pdir) {
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void);
|
||||
// Whether a descriptor is attached to a real terminal, i.e. whether a human is looking at it.
|
||||
//
|
||||
// Do not use `isatty()` for this on Windows. It is `_isatty()`, which answers "is this a character
|
||||
// device" rather than "is this a console", and `NUL` -- as well as MSYS2's `/dev/null` -- is a
|
||||
// character device. A redirected run therefore looks interactive, which is how
|
||||
// `fastfetch --gen-config <path> > /dev/null` ends up on the interactive path and blocks forever.
|
||||
bool ffIsTerminal(int fd);
|
||||
// Returns whether the file was removed; callers that only want it gone discard that, so not `nodiscard`
|
||||
[[gnu::nonnull(1)]] bool ffRemoveFile(const char* fileName);
|
||||
// Modification time of a file, in milliseconds since the Unix epoch, or 0 if it can not be read.
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
// `val` may be null (yyjson predicates tolerate it); `result` and `pairs` may not.
|
||||
// Returns an error string, null on success, so it is `nodiscard`.
|
||||
[[gnu::nonnull(2, 3), nodiscard]] const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]);
|
||||
// Reads an integer in `[0, max]` into `result`. Returns false for anything else.
|
||||
//
|
||||
// `yyjson_get_uint()` accepts the signed number subtype as well and reinterprets its bits, so the
|
||||
// bare `(uint32_t) yyjson_get_uint(val)` it replaces turned `-1` into `UINT32_MAX` — 4.3 billion
|
||||
// characters for `separator.times`, a ~50 day sleep for `waitTime`.
|
||||
[[gnu::nonnull(1, 2), nodiscard]] bool ffJsonConfigParseUInt32(yyjson_val* val, uint32_t* result, uint32_t max);
|
||||
|
||||
// The three helpers below are routinely called as statements, to add a key to a document, so their
|
||||
// result is not `nodiscard`.
|
||||
|
||||
@@ -14,14 +14,14 @@ typedef enum FFPrintType: uint8_t {
|
||||
// Both `moduleName` and `moduleArgs` are optional and explicitly handled: a null `moduleName` is what
|
||||
// `--set-keyless` relies on, and a null `moduleArgs` (used by ffPrintError callers) skips the custom
|
||||
// key entirely. So neither takes `nonnull`.
|
||||
void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType);
|
||||
void ffPrintLogoAndKey(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType);
|
||||
// Same for `moduleArgs`; `numArgs` / `arguments` are only read when `moduleArgs` is non-null, so
|
||||
// `arguments` is not `nonnull(6)` either. At least one caller discards the result, so not `nodiscard`.
|
||||
bool ffPrintFormat(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments);
|
||||
bool ffPrintFormat(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments);
|
||||
#define FF_PRINT_FORMAT_CHECKED(moduleName, moduleIndex, moduleArgs, printType, arguments) \
|
||||
ffPrintFormat((moduleName), (moduleIndex), (moduleArgs), (printType), (sizeof(arguments) / sizeof(*arguments)), (arguments));
|
||||
// `moduleName` / `moduleArgs` are forwarded to ffPrintLogoAndKey, which accepts null for both;
|
||||
// only `message` is dereferenced here (by `vprintf`).
|
||||
[[gnu::format(printf, 5, 6), gnu::nonnull(5)]] void ffPrintError(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...);
|
||||
[[gnu::format(printf, 5, 6), gnu::nonnull(5)]] void ffPrintError(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...);
|
||||
[[gnu::nonnull(1)]] void ffPrintColor(const FFstrbuf* colorValue);
|
||||
void ffPrintCharTimes(char c, uint32_t times);
|
||||
|
||||
Reference in New Issue
Block a user