Global: fixes a lot bugs found by AI

This commit is contained in:
Carter Li
2026-09-22 14:49:43 +08:00
committed by 李通洲
parent fe49a9624e
commit c26bace3c2
91 changed files with 563 additions and 282 deletions
+19 -1
View File
@@ -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
+4
View File
@@ -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;
}
+11
View File
@@ -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;
+26
View File
@@ -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;
}
+4 -4
View File
@@ -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;
}
+7
View File
@@ -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.
+6
View File
@@ -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`.
+3 -3
View File
@@ -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);