2021-04-09 14:07:05 +02:00
|
|
|
#include "fastfetch.h"
|
2026-01-06 09:48:38 +08:00
|
|
|
#include "common/format.h"
|
|
|
|
|
#include "common/parsing.h"
|
|
|
|
|
#include "common/textModifier.h"
|
2026-05-28 15:58:08 +08:00
|
|
|
#include "common/strutil.h"
|
2026-05-18 01:42:13 +08:00
|
|
|
#include "common/library.h"
|
2021-04-09 14:07:05 +02:00
|
|
|
|
2022-10-13 18:12:54 +08:00
|
|
|
#include <inttypes.h>
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) {
|
|
|
|
|
switch (formatarg->type) {
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_INT:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendSInt(buffer, *(int32_t*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_UINT:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendUInt(buffer, *(uint32_t*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_UINT64:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendUInt(buffer, *(uint64_t*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_UINT16:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendUInt(buffer, *(uint16_t*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_UINT8:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendUInt(buffer, *(uint8_t*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_STRING:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendS(buffer, (const char*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_STRBUF:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppend(buffer, (const FFstrbuf*) formatarg->value);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_FLOAT:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendDouble(buffer, *(float*) formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_DOUBLE:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendDouble(buffer, *(double*) formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-12 15:07:19 +08:00
|
|
|
case FF_ARG_TYPE_BOOL:
|
2026-03-29 09:28:49 +08:00
|
|
|
ffStrbufAppendS(buffer, *(bool*) formatarg->value ? "true" : "false");
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2026-03-29 09:28:49 +08:00
|
|
|
case FF_ARG_TYPE_LIST: {
|
2024-10-25 14:48:11 +08:00
|
|
|
const FFlist* list = (const FFlist*) formatarg->value;
|
2026-03-29 09:28:49 +08:00
|
|
|
for (uint32_t i = 0; i < list->length; i++) {
|
2024-10-25 14:48:11 +08:00
|
|
|
ffStrbufAppend(buffer, FF_LIST_GET(FFstrbuf, *list, i));
|
2026-03-29 09:28:49 +08:00
|
|
|
if (i < list->length - 1) {
|
2024-09-03 15:18:17 +08:00
|
|
|
ffStrbufAppendS(buffer, ", ");
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-09-03 15:18:17 +08:00
|
|
|
}
|
|
|
|
|
break;
|
2021-05-23 19:40:34 +02:00
|
|
|
}
|
2026-03-29 09:28:49 +08:00
|
|
|
case FF_ARG_TYPE_BUFFER: {
|
2026-03-24 16:09:25 +08:00
|
|
|
// Placeholder for binary data, just print the size for now
|
|
|
|
|
const FFArgBuffer* argBuffer = (const FFArgBuffer*) formatarg->value;
|
|
|
|
|
ffStrbufAppendF(buffer, "buffer(%u bytes)", argBuffer->length);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-09-03 15:18:17 +08:00
|
|
|
default:
|
2026-03-29 09:28:49 +08:00
|
|
|
if (formatarg->type != FF_ARG_TYPE_NULL) {
|
2024-09-03 15:18:17 +08:00
|
|
|
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-09-03 15:18:17 +08:00
|
|
|
break;
|
2021-04-16 17:46:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-31 15:08:52 +05:30
|
|
|
// strtol() to an int32_t. Values that don't fit are clamped instead of wrapping around,
|
|
|
|
|
// so that e.g. 4294967297 can't turn into 1.
|
|
|
|
|
static int32_t strToInt32(const char* str, char** pEnd) {
|
|
|
|
|
long long value = strtoll(str, pEnd, 10);
|
|
|
|
|
return value > INT32_MAX ? INT32_MAX : value < INT32_MIN ? INT32_MIN : (int32_t) value;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-01 14:54:27 +01:00
|
|
|
/**
|
|
|
|
|
* @brief parses a string to a uint32_t
|
|
|
|
|
*
|
|
|
|
|
* If the string can't be parsed, or is < 1, uint32_t max is returned.
|
|
|
|
|
*
|
|
|
|
|
* @param placeholderValue the string to parse
|
|
|
|
|
* @return uint32_t the parsed value
|
|
|
|
|
*/
|
2026-03-29 09:28:49 +08:00
|
|
|
static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs, const FFformatarg* arguments) {
|
2024-06-26 16:14:34 +08:00
|
|
|
char firstChar = placeholderValue[0];
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '\0') {
|
2024-06-26 16:14:34 +08:00
|
|
|
return 0; // use arg counter
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar >= '0' && firstChar <= '9') {
|
2026-07-10 18:08:04 +08:00
|
|
|
char* pEnd = nullptr;
|
2026-10-31 15:08:52 +05:30
|
|
|
unsigned long result = strtoul(placeholderValue, &pEnd, 10);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (result > numArgs) {
|
2024-05-27 16:41:37 +08:00
|
|
|
return UINT32_MAX;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
if (*pEnd != '\0') {
|
2024-06-26 16:14:34 +08:00
|
|
|
return UINT32_MAX;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-10-31 15:08:52 +05:30
|
|
|
return (uint32_t) result;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (ffCharIsEnglishAlphabet(firstChar)) {
|
|
|
|
|
for (uint32_t i = 0; i < numArgs; ++i) {
|
2024-05-27 16:41:37 +08:00
|
|
|
const FFformatarg* arg = &arguments[i];
|
2026-03-29 09:28:49 +08:00
|
|
|
if (arg->name && ffStrEqualsIgnCase(placeholderValue, arg->name)) {
|
2024-05-27 16:41:37 +08:00
|
|
|
return i + 1;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-05-27 16:41:37 +08:00
|
|
|
}
|
|
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
return UINT32_MAX;
|
2021-04-24 21:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength) {
|
2021-04-24 21:42:48 +02:00
|
|
|
ffStrbufAppendS(buffer, start);
|
|
|
|
|
ffStrbufAppend(buffer, placeholderValue);
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (index < formatStringLength) {
|
2021-04-24 21:42:48 +02:00
|
|
|
ffStrbufAppendC(buffer, '}');
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static inline bool formatArgSet(const FFformatarg* arg) {
|
2026-08-04 19:18:23 +08:00
|
|
|
// clang-format off
|
|
|
|
|
return arg->value != nullptr && (
|
|
|
|
|
(arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) ||
|
|
|
|
|
(arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0)
|
|
|
|
|
);
|
|
|
|
|
// clang-format on
|
2021-04-24 21:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
[[maybe_unused]] static inline void normalizeArgName(FFstrbuf* dst, const char* src) {
|
2026-05-20 18:28:24 +08:00
|
|
|
ffStrbufClear(dst);
|
|
|
|
|
bool flag = false;
|
|
|
|
|
for (const char* p = src; *p; ++p) {
|
|
|
|
|
if (*p == '-') {
|
|
|
|
|
flag = true;
|
|
|
|
|
} else if (flag) {
|
|
|
|
|
ffStrbufAppendC(dst, (char) toupper((unsigned char) *p));
|
|
|
|
|
flag = false;
|
|
|
|
|
} else {
|
|
|
|
|
ffStrbufAppendC(dst, *p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 09:48:13 +08:00
|
|
|
#if FF_HAVE_LUA
|
2026-06-01 16:00:28 +08:00
|
|
|
#include "common/lua.h"
|
2026-05-17 09:48:13 +08:00
|
|
|
|
2026-05-21 09:25:41 +08:00
|
|
|
static void appendLuaError(FFstrbuf* buffer, const char* prefix, lua_State* L) {
|
2026-07-10 18:08:04 +08:00
|
|
|
const char* err = lua_tolstring(L, -1, nullptr);
|
2026-05-21 09:25:41 +08:00
|
|
|
if (err) {
|
2026-09-24 15:32:15 +08:00
|
|
|
// Drop the position Lua puts in front of an error and keep the message only. Both places
|
|
|
|
|
// that build one -- `luaG_addinfo` and `luaL_where` -- format it as `%s:%d: %s`, so a
|
|
|
|
|
// position is a colon, a line number, a colon and a space:
|
|
|
|
|
// general.preload.lua:1: <name> expected near '=' -> <name> expected near '='
|
|
|
|
|
// [string ""]:1: x -> x
|
|
|
|
|
// Not every error has one, and cutting at the first colon mangled those. `luaL_loadfilex`
|
|
|
|
|
// reports `cannot open <path>: <strerror>`, where that colon is the one after the path, so
|
|
|
|
|
// the path -- the only part naming the problem -- was dropped and `No such file or
|
|
|
|
|
// directory` was left on its own. A colon followed by a line number does not identify a
|
|
|
|
|
// position either, because a path can contain one: `cannot open /tmp/x:12:y/nope.lua: ...`
|
|
|
|
|
// has to survive whole. The trailing space is what separates the two, and a real position
|
|
|
|
|
// always has it.
|
|
|
|
|
for (const char* colon = strchr(err, ':'); colon; colon = strchr(colon + 1, ':')) {
|
|
|
|
|
const char* line = colon + 1;
|
|
|
|
|
while (*line >= '0' && *line <= '9') {
|
|
|
|
|
++line;
|
|
|
|
|
}
|
|
|
|
|
if (line > colon + 1 && line[0] == ':' && line[1] == ' ') {
|
|
|
|
|
err = line + 2;
|
|
|
|
|
break;
|
2026-05-21 09:25:41 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ffStrbufAppendF(buffer, "%s: %s", prefix, err ? err : "unknown");
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 09:26:52 +08:00
|
|
|
static bool parseLuaString(FFstrbuf* buffer, const char* script, uint32_t scriptLen, uint32_t numArgs, const FFformatarg* arguments) {
|
2026-06-01 16:00:28 +08:00
|
|
|
const char* err = ffLuaLoadState();
|
2026-05-17 09:48:13 +08:00
|
|
|
if (err) {
|
|
|
|
|
ffStrbufAppendF(buffer, "Lua init error: %s", err);
|
2026-05-22 09:26:52 +08:00
|
|
|
return false;
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-20 18:28:24 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY argNameBuf = ffStrbufCreate();
|
2026-05-22 09:26:52 +08:00
|
|
|
bool ret = false;
|
2026-05-20 18:28:24 +08:00
|
|
|
|
2026-05-17 09:48:13 +08:00
|
|
|
lua_State* L = luaData.L;
|
|
|
|
|
// Clear stack and load chunk
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_settop(L, 0);
|
|
|
|
|
if (luaL_loadbuffer(L, script, scriptLen, "") != LUA_OK) {
|
2026-05-21 09:25:41 +08:00
|
|
|
appendLuaError(buffer, "Lua load error", L);
|
2026-05-17 09:48:13 +08:00
|
|
|
} else {
|
|
|
|
|
// Build args table for name lookup only.
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_createtable(L, 0, 0);
|
2026-05-17 09:48:13 +08:00
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < numArgs; ++i) {
|
|
|
|
|
const FFformatarg* arg = &arguments[i];
|
2026-05-20 18:28:24 +08:00
|
|
|
switch (arg->type) {
|
|
|
|
|
case FF_ARG_TYPE_INT:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushinteger(L, (lua_Integer) * (int32_t*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_UINT:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushinteger(L, (lua_Integer) * (uint32_t*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_UINT64:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushinteger(L, (lua_Integer) * (uint64_t*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_UINT16:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushinteger(L, (lua_Integer) * (uint16_t*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_UINT8:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushinteger(L, (lua_Integer) * (uint8_t*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_FLOAT:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushnumber(L, (lua_Number) * (float*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_DOUBLE:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushnumber(L, (lua_Number) * (double*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_BOOL:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushboolean(L, *(bool*) arg->value);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_STRING:
|
2026-09-23 22:29:39 +08:00
|
|
|
if (!arg->value) {
|
|
|
|
|
lua_pushlstring(L, "", 0);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushlstring(L, (const char*) arg->value, strlen((const char*) arg->value));
|
|
|
|
|
}
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
case FF_ARG_TYPE_STRBUF: {
|
|
|
|
|
const FFstrbuf* sb = (const FFstrbuf*) arg->value;
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushlstring(L, sb->chars, sb->length);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case FF_ARG_TYPE_LIST: {
|
|
|
|
|
const FFlist* list = (const FFlist*) arg->value;
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_createtable(L, 0, 0);
|
2026-05-20 18:28:24 +08:00
|
|
|
for (uint32_t li = 0; li < list->length; ++li) {
|
|
|
|
|
const FFstrbuf* item = FF_LIST_GET(FFstrbuf, *list, li);
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushlstring(L, item->chars, item->length);
|
|
|
|
|
lua_seti(L, -2, (lua_Integer) (li + 1));
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
2026-05-20 18:28:24 +08:00
|
|
|
default:
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_pushnil(L);
|
2026-05-20 18:28:24 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (arg->name && arg->name[0]) {
|
|
|
|
|
normalizeArgName(&argNameBuf, arg->name);
|
|
|
|
|
} else {
|
|
|
|
|
ffStrbufSetF(&argNameBuf, "arg%" PRIu32, i + 1);
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_setfield(L, -2, argNameBuf.chars);
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-01 16:00:28 +08:00
|
|
|
if (lua_pcall(L, 1, LUA_MULTRET, 0) != LUA_OK) {
|
2026-05-21 09:25:41 +08:00
|
|
|
appendLuaError(buffer, "Lua runtime error", L);
|
2026-05-17 09:48:13 +08:00
|
|
|
} else {
|
2026-06-01 16:00:28 +08:00
|
|
|
int nresults = lua_gettop(L);
|
2026-05-22 09:26:52 +08:00
|
|
|
if (nresults == 0) {
|
|
|
|
|
ffStrbufAppendS(buffer, "Lua result error: no result");
|
|
|
|
|
} else {
|
2026-05-17 09:48:13 +08:00
|
|
|
// Convert first result to string
|
2026-07-10 18:08:04 +08:00
|
|
|
const char* res = lua_tolstring(L, 1, nullptr);
|
2026-05-17 09:48:13 +08:00
|
|
|
if (res) {
|
|
|
|
|
ffStrbufAppendS(buffer, res);
|
|
|
|
|
} else {
|
2026-07-10 18:08:04 +08:00
|
|
|
luaL_tolstring(L, 1, nullptr);
|
|
|
|
|
const char* sval = lua_tolstring(L, -1, nullptr);
|
2026-05-17 09:48:13 +08:00
|
|
|
if (sval) {
|
|
|
|
|
ffStrbufAppendS(buffer, sval);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-22 09:26:52 +08:00
|
|
|
ret = true;
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-01 16:00:28 +08:00
|
|
|
lua_settop(L, 0);
|
2026-05-22 09:26:52 +08:00
|
|
|
return ret;
|
2026-05-17 09:48:13 +08:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-09-21 13:16:43 +08:00
|
|
|
// Returns the length of the ANSI CSI sequence at `str`, or 0 if there is none.
|
|
|
|
|
// A CSI sequence is `ESC [` + parameter bytes (0x30-0x3F) + intermediate bytes (0x20-0x2F) + final byte (0x40-0x7E)
|
|
|
|
|
static uint32_t ansiEscapeLength(const char* str) {
|
|
|
|
|
if (str[0] != '\e' || str[1] != '[') {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* p = str + 2;
|
|
|
|
|
while (*p >= '0' && *p <= '?') {
|
|
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
while (*p >= ' ' && *p <= '/') {
|
|
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
if (*p < '@' || *p > '~') {
|
|
|
|
|
return 0; // not a complete CSI sequence
|
|
|
|
|
}
|
|
|
|
|
return (uint32_t) (p - str) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 15:55:10 +08:00
|
|
|
static bool skipAnsiEscape(FFstrbuf* in, FFstrbuf* out, FFstrbuf* trailingEscape) {
|
2026-09-21 13:16:43 +08:00
|
|
|
// Skip all ANSI escape codes at the start of the string, so that they don't count
|
|
|
|
|
// towards the truncation / padding length. A value may carry more than one of them,
|
|
|
|
|
// e.g. `\e[31m\e[1m`.
|
|
|
|
|
uint32_t prefixLen = 0;
|
|
|
|
|
while (in->length - prefixLen >= 2) {
|
|
|
|
|
uint32_t len = ansiEscapeLength(in->chars + prefixLen);
|
|
|
|
|
if (len == 0) {
|
|
|
|
|
break;
|
2026-06-02 15:55:10 +08:00
|
|
|
}
|
2026-09-21 13:16:43 +08:00
|
|
|
prefixLen += len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prefixLen == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffStrbufAppendNS(out, prefixLen, in->chars);
|
|
|
|
|
ffStrbufSubstrAfter(in, prefixLen - 1);
|
|
|
|
|
|
|
|
|
|
if (trailingEscape) {
|
|
|
|
|
// likely have one or more `CSI m` resets at the end of the string
|
|
|
|
|
uint32_t end = in->length;
|
|
|
|
|
while (end >= 3) {
|
|
|
|
|
uint32_t start = end - 1;
|
|
|
|
|
while (start > 0 && in->chars[start] != '\e') {
|
|
|
|
|
--start;
|
2026-06-02 15:55:10 +08:00
|
|
|
}
|
2026-09-21 13:16:43 +08:00
|
|
|
uint32_t len = ansiEscapeLength(in->chars + start);
|
|
|
|
|
if (len == 0 || start + len != end) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
end = start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (end < in->length) {
|
|
|
|
|
ffStrbufSetNS(trailingEscape, in->length - end, in->chars + end);
|
|
|
|
|
ffStrbufSubstrBefore(in, end);
|
2026-06-02 15:55:10 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-09-21 13:16:43 +08:00
|
|
|
return true;
|
2026-06-02 15:55:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-10-31 12:16:04 +05:30
|
|
|
// Returns the index of the `}` of the `{<marker>}` that closes the conditional we are in, or the
|
|
|
|
|
// length of the format string if there is none. `i` is the index of the `}` that ends the opening
|
|
|
|
|
// placeholder. The part to skip may hold conditionals of its own, which have to be passed over.
|
|
|
|
|
// Like in `parseFormatString`, only a conditional with a valid argument opens a block.
|
|
|
|
|
static uint32_t findConditionalEnd(const FFstrbuf* formatstr, uint32_t i, char marker, uint32_t numArgs, const FFformatarg* arguments) {
|
|
|
|
|
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
|
|
|
|
uint32_t depth = 0;
|
|
|
|
|
|
|
|
|
|
for (++i; i < formatstr->length; ++i) {
|
|
|
|
|
if (formatstr->chars[i] != '{') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++i;
|
|
|
|
|
if (i >= formatstr->length) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// double {{ evaluates to a single { and doesn't start a placeholder
|
|
|
|
|
if (formatstr->chars[i] == '{') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t iEnd = ffStrbufNextIndexC(formatstr, i, '}');
|
|
|
|
|
if (formatstr->chars[i] == marker) {
|
|
|
|
|
if (iEnd - i == 1) {
|
|
|
|
|
if (depth == 0) {
|
|
|
|
|
return iEnd;
|
|
|
|
|
}
|
|
|
|
|
--depth;
|
|
|
|
|
} else {
|
|
|
|
|
ffStrbufClear(&placeholderValue);
|
|
|
|
|
ffStrbufAppendNS(&placeholderValue, iEnd - i - 1, &formatstr->chars[i + 1]);
|
|
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
|
|
|
|
if (index >= 1 && index <= numArgs) {
|
|
|
|
|
++depth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i = iEnd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return formatstr->length;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 10:07:28 +08:00
|
|
|
static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
2021-04-24 21:42:48 +02:00
|
|
|
uint32_t argCounter = 0;
|
|
|
|
|
|
|
|
|
|
uint32_t numOpenIfs = 0;
|
|
|
|
|
uint32_t numOpenNotIfs = 0;
|
2021-04-09 14:07:05 +02:00
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
for (uint32_t i = 0; i < formatstr->length; ++i) {
|
2021-04-24 21:42:48 +02:00
|
|
|
// if we don't have a placeholder start just copy the chars over to output buffer
|
2026-03-29 09:28:49 +08:00
|
|
|
if (formatstr->chars[i] != '{') {
|
2021-04-09 14:07:05 +02:00
|
|
|
ffStrbufAppendC(buffer, formatstr->chars[i]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 21:42:48 +02:00
|
|
|
// jump to next char, the start of the placeholder value
|
2021-04-09 14:07:05 +02:00
|
|
|
++i;
|
|
|
|
|
|
2026-05-20 18:40:20 +08:00
|
|
|
// unmatched trailing '{'
|
|
|
|
|
if (i >= formatstr->length) {
|
|
|
|
|
ffStrbufAppendC(buffer, '{');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 21:42:48 +02:00
|
|
|
// double {{ elvaluates to a single { and doesn't count as start
|
2026-03-29 09:28:49 +08:00
|
|
|
if (formatstr->chars[i] == '{') {
|
2021-04-09 14:07:05 +02:00
|
|
|
ffStrbufAppendC(buffer, '{');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
ffStrbufClear(&placeholderValue);
|
2021-04-24 21:42:48 +02:00
|
|
|
|
|
|
|
|
{
|
2024-05-27 16:41:37 +08:00
|
|
|
uint32_t iEnd = ffStrbufNextIndexC(formatstr, i, '}');
|
|
|
|
|
ffStrbufAppendNS(&placeholderValue, iEnd - i, &formatstr->chars[i]);
|
|
|
|
|
i = iEnd;
|
2021-04-24 21:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
char firstChar = placeholderValue.chars[0];
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (placeholderValue.length == 1) {
|
2024-05-27 16:41:37 +08:00
|
|
|
// test if for stop, if so break the loop
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '-') {
|
2024-05-27 16:41:37 +08:00
|
|
|
break;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
// test for end of an if, if so do nothing
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '?') {
|
|
|
|
|
if (numOpenIfs == 0) {
|
2024-05-27 16:41:37 +08:00
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2024-05-27 16:41:37 +08:00
|
|
|
--numOpenIfs;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// test for end of a not if, if so do nothing
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '/') {
|
|
|
|
|
if (numOpenNotIfs == 0) {
|
2024-05-27 16:41:37 +08:00
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2024-05-27 16:41:37 +08:00
|
|
|
--numOpenNotIfs;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-05-27 16:41:37 +08:00
|
|
|
|
|
|
|
|
continue;
|
2021-04-25 09:13:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-27 16:41:37 +08:00
|
|
|
// test for end of a color, if so do nothing
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '#') {
|
|
|
|
|
if (!instance.config.display.pipe) {
|
2024-05-27 16:41:37 +08:00
|
|
|
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-05-27 16:41:37 +08:00
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-04-25 09:13:18 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-24 21:42:48 +02:00
|
|
|
// test for if, if so evaluate it
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '?') {
|
2021-04-24 21:42:48 +02:00
|
|
|
ffStrbufSubstrAfter(&placeholderValue, 0);
|
|
|
|
|
|
2024-06-26 16:14:34 +08:00
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
2021-04-24 21:42:48 +02:00
|
|
|
|
|
|
|
|
// testing for an invalid index
|
2026-03-29 09:28:49 +08:00
|
|
|
if (index > numArgs || index < 1) {
|
2021-04-24 21:42:48 +02:00
|
|
|
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// continue normally if an format arg is set and the value is > 0
|
2026-03-29 09:28:49 +08:00
|
|
|
if (formatArgSet(&arguments[index - 1])) {
|
2021-04-24 21:42:48 +02:00
|
|
|
++numOpenIfs;
|
2021-04-09 14:07:05 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 21:42:48 +02:00
|
|
|
// fastforward to the end of the if without printing the in between
|
2026-10-31 12:16:04 +05:30
|
|
|
i = findConditionalEnd(formatstr, i, '?', numArgs, arguments);
|
2021-04-24 21:42:48 +02:00
|
|
|
continue;
|
2021-04-09 14:07:05 +02:00
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
|
|
|
|
|
// test for not if, if so evaluate it
|
2026-03-29 09:28:49 +08:00
|
|
|
if (firstChar == '/') {
|
2021-04-24 21:42:48 +02:00
|
|
|
ffStrbufSubstrAfter(&placeholderValue, 0);
|
|
|
|
|
|
2024-06-26 16:14:34 +08:00
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
2021-04-24 21:42:48 +02:00
|
|
|
|
|
|
|
|
// testing for an invalid index
|
2026-03-29 09:28:49 +08:00
|
|
|
if (index > numArgs || index < 1) {
|
2021-04-24 21:42:48 +02:00
|
|
|
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
|
2021-04-09 14:07:05 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// continue normally if an format arg is not set or the value is 0
|
|
|
|
|
if (!formatArgSet(&arguments[index - 1])) {
|
2021-04-24 21:42:48 +02:00
|
|
|
++numOpenNotIfs;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fastforward to the end of the if without printing the in between
|
2026-10-31 12:16:04 +05:30
|
|
|
i = findConditionalEnd(formatstr, i, '/', numArgs, arguments);
|
2021-04-24 21:42:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// test for color, if so evaluate it
|
|
|
|
|
if (firstChar == '#') {
|
|
|
|
|
if (!instance.config.display.pipe) {
|
2024-05-28 10:56:01 +08:00
|
|
|
ffStrbufAppendS(buffer, "\e[");
|
2024-05-28 15:38:45 +08:00
|
|
|
ffOptionParseColorNoClear(placeholderValue.chars + 1, buffer);
|
2024-05-28 10:56:01 +08:00
|
|
|
ffStrbufAppendC(buffer, 'm');
|
|
|
|
|
}
|
2021-04-25 09:13:18 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// test for constant or env var, if so evaluate it
|
|
|
|
|
if (firstChar == '$') {
|
2026-07-10 18:08:04 +08:00
|
|
|
char* pend = nullptr;
|
2026-10-31 15:08:52 +05:30
|
|
|
int32_t indexSigned = strToInt32(placeholderValue.chars + 1, &pend);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (pend == placeholderValue.chars + 1) {
|
2025-02-08 22:11:41 +08:00
|
|
|
// treat placeholder as an environment variable
|
|
|
|
|
char* envValue = getenv(placeholderValue.chars + 1);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (envValue) {
|
2025-02-08 22:11:41 +08:00
|
|
|
ffStrbufAppendS(buffer, envValue);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2025-02-08 22:11:41 +08:00
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-02-08 22:11:41 +08:00
|
|
|
// treat placeholder as a constant
|
|
|
|
|
uint32_t index = (uint32_t) (indexSigned < 0 ? (int32_t) instance.config.display.constants.length + indexSigned : indexSigned - 1);
|
2024-07-15 11:00:15 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (*pend != '\0' || instance.config.display.constants.length <= index) {
|
2025-02-08 22:11:41 +08:00
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2025-02-08 22:11:41 +08:00
|
|
|
FFstrbuf* item = FF_LIST_GET(FFstrbuf, instance.config.display.constants, index);
|
|
|
|
|
ffStrbufAppend(buffer, item);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-15 11:00:15 +08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-11 11:07:00 +08:00
|
|
|
char* pSep = placeholderValue.chars;
|
|
|
|
|
char cSep = '\0';
|
2026-06-02 16:03:04 +08:00
|
|
|
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '|' && *pSep != '~') {
|
2024-09-11 11:07:00 +08:00
|
|
|
++pSep;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
if (*pSep) {
|
2024-09-11 11:07:00 +08:00
|
|
|
cSep = *pSep;
|
|
|
|
|
*pSep = '\0';
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-07-10 18:08:04 +08:00
|
|
|
pSep = nullptr;
|
2024-06-26 16:14:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
2021-04-24 21:42:48 +02:00
|
|
|
|
|
|
|
|
// test for invalid index
|
2026-03-29 09:28:49 +08:00
|
|
|
if (index == 0) {
|
2024-06-26 16:14:34 +08:00
|
|
|
index = ++argCounter;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-26 16:14:34 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (index > numArgs) {
|
|
|
|
|
if (pSep) {
|
|
|
|
|
*pSep = cSep;
|
|
|
|
|
}
|
2021-04-24 21:42:48 +02:00
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
|
|
|
continue;
|
2021-04-09 14:07:05 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (!cSep) {
|
2024-09-11 11:07:00 +08:00
|
|
|
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (cSep == '~') {
|
2024-09-11 11:07:00 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
|
|
|
|
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
2026-06-02 15:55:10 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY trailingEscape = ffStrbufCreate();
|
|
|
|
|
skipAnsiEscape(&tempString, buffer, &trailingEscape);
|
2024-06-26 16:14:34 +08:00
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
char* pEnd = nullptr;
|
2026-10-31 15:08:52 +05:30
|
|
|
int32_t start = strToInt32(pSep + 1, &pEnd);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (start < 0) {
|
2024-09-11 11:07:00 +08:00
|
|
|
start = (int32_t) tempString.length + start;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
if (start >= 0 && (uint32_t) start < tempString.length) {
|
|
|
|
|
if (*pEnd == '\0') {
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (*pEnd == ',') {
|
2026-10-31 15:08:52 +05:30
|
|
|
int32_t end = strToInt32(pEnd + 1, &pEnd);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (!*pEnd) {
|
|
|
|
|
if (end < 0) {
|
2024-09-11 11:07:00 +08:00
|
|
|
end = (int32_t) tempString.length + end;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-10-26 05:34:50 +00:00
|
|
|
if (end >= 0 && (uint32_t) end > tempString.length) {
|
2024-09-11 11:07:00 +08:00
|
|
|
end = (int32_t) tempString.length;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
if (end > start) {
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppendNS(buffer, (uint32_t) (end - start), &tempString.chars[start]);
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-09-11 11:07:00 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 15:55:10 +08:00
|
|
|
if (trailingEscape.length > 0) {
|
|
|
|
|
ffStrbufAppend(buffer, &trailingEscape);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (*pEnd) {
|
2024-09-11 11:07:00 +08:00
|
|
|
*pSep = cSep;
|
|
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-07-10 18:08:04 +08:00
|
|
|
char* pEnd = nullptr;
|
2026-10-31 15:08:52 +05:30
|
|
|
int32_t truncLength = strToInt32(pSep + 1, &pEnd);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (*pEnd != '\0') {
|
2024-09-11 11:07:00 +08:00
|
|
|
*pSep = cSep;
|
|
|
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ellipsis = false;
|
2026-03-29 09:28:49 +08:00
|
|
|
if (truncLength < 0) {
|
2024-09-11 11:07:00 +08:00
|
|
|
ellipsis = true;
|
2026-10-31 15:08:52 +05:30
|
|
|
truncLength = truncLength == INT32_MIN ? INT32_MAX : -truncLength;
|
2024-09-11 11:07:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
|
|
|
|
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
2026-06-02 15:55:10 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY trailingEscape = ffStrbufCreate();
|
|
|
|
|
skipAnsiEscape(&tempString, buffer, &trailingEscape);
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (tempString.length == (uint32_t) truncLength) {
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppend(buffer, &tempString);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (tempString.length > (uint32_t) truncLength) {
|
|
|
|
|
if (cSep == ':') {
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufSubstrBefore(&tempString, (uint32_t) truncLength);
|
|
|
|
|
ffStrbufTrimRightSpace(&tempString);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
|
|
|
|
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis ? truncLength : truncLength - 1));
|
2024-09-02 15:54:22 +08:00
|
|
|
}
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppend(buffer, &tempString);
|
2024-09-02 15:54:22 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ellipsis) {
|
2024-09-02 15:54:22 +08:00
|
|
|
ffStrbufAppendS(buffer, "…");
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
} else if (cSep == ':') {
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppend(buffer, &tempString);
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-06-02 16:03:04 +08:00
|
|
|
if (cSep == '<') { // left align
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppend(buffer, &tempString);
|
|
|
|
|
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
2026-06-02 16:03:04 +08:00
|
|
|
} else if (cSep == '>') { // right align
|
2024-09-11 11:07:00 +08:00
|
|
|
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
|
|
|
|
ffStrbufAppend(buffer, &tempString);
|
2026-06-02 16:03:04 +08:00
|
|
|
} else if (cSep == '|') { // center align
|
|
|
|
|
uint32_t padding = ((uint32_t) truncLength - tempString.length) / 2;
|
|
|
|
|
ffStrbufAppendNC(buffer, padding, ' ');
|
|
|
|
|
ffStrbufAppend(buffer, &tempString);
|
|
|
|
|
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length - padding, ' ');
|
2024-09-02 15:54:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-02 15:55:10 +08:00
|
|
|
|
|
|
|
|
if (trailingEscape.length > 0) {
|
|
|
|
|
ffStrbufAppend(buffer, &trailingEscape);
|
|
|
|
|
}
|
2024-06-26 16:14:34 +08:00
|
|
|
}
|
2021-04-09 14:07:05 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (!instance.config.display.pipe) {
|
2024-05-28 10:56:01 +08:00
|
|
|
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-05-22 09:26:52 +08:00
|
|
|
|
|
|
|
|
return true;
|
2021-04-09 14:07:05 +02:00
|
|
|
}
|
2026-05-22 10:07:28 +08:00
|
|
|
|
|
|
|
|
bool ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
|
|
|
|
#if FF_HAVE_LUA
|
|
|
|
|
if (ffStrbufStartsWithS(formatstr, "lua:")) {
|
|
|
|
|
// If outputFormat starts with "lua:", treat the rest as a Lua script
|
|
|
|
|
return parseLuaString(buffer, formatstr->chars + 4, formatstr->length - 4, numArgs, arguments);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return parseFormatString(buffer, formatstr, numArgs, arguments);
|
|
|
|
|
}
|