mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
414 lines
14 KiB
C
414 lines
14 KiB
C
#include "fastfetch.h"
|
|
#include "common/format.h"
|
|
#include "common/parsing.h"
|
|
#include "common/textModifier.h"
|
|
#include "common/stringUtils.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
|
{
|
|
switch(formatarg->type)
|
|
{
|
|
case FF_FORMAT_ARG_TYPE_INT:
|
|
ffStrbufAppendSInt(buffer, *(int32_t*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_UINT:
|
|
ffStrbufAppendUInt(buffer, *(uint32_t*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_UINT64:
|
|
ffStrbufAppendUInt(buffer, *(uint64_t*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_UINT16:
|
|
ffStrbufAppendUInt(buffer, *(uint16_t*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_UINT8:
|
|
ffStrbufAppendUInt(buffer, *(uint8_t*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_STRING:
|
|
ffStrbufAppendS(buffer, (const char*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_STRBUF:
|
|
ffStrbufAppend(buffer, (const FFstrbuf*)formatarg->value);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_FLOAT:
|
|
ffStrbufAppendDouble(buffer, *(float*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_DOUBLE:
|
|
ffStrbufAppendDouble(buffer, *(double*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_BOOL:
|
|
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
|
|
break;
|
|
case FF_FORMAT_ARG_TYPE_LIST:
|
|
{
|
|
const FFlist* list = (const FFlist*) formatarg->value;
|
|
for(uint32_t i = 0; i < list->length; i++)
|
|
{
|
|
ffStrbufAppend(buffer, FF_LIST_GET(FFstrbuf, *list, i));
|
|
if(i < list->length - 1)
|
|
ffStrbufAppendS(buffer, ", ");
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL)
|
|
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs, const FFformatarg* arguments)
|
|
{
|
|
char firstChar = placeholderValue[0];
|
|
if (firstChar == '\0')
|
|
return 0; // use arg counter
|
|
|
|
if (firstChar >= '0' && firstChar <= '9')
|
|
{
|
|
char* pEnd = NULL;
|
|
uint32_t result = (uint32_t) strtoul(placeholderValue, &pEnd, 10);
|
|
if (result > numArgs)
|
|
return UINT32_MAX;
|
|
if (*pEnd != '\0')
|
|
return UINT32_MAX;
|
|
return result;
|
|
}
|
|
else if (ffCharIsEnglishAlphabet(firstChar))
|
|
{
|
|
for (uint32_t i = 0; i < numArgs; ++i)
|
|
{
|
|
const FFformatarg* arg = &arguments[i];
|
|
if (arg->name && strcasecmp(placeholderValue, arg->name) == 0)
|
|
return i + 1;
|
|
}
|
|
}
|
|
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength)
|
|
{
|
|
ffStrbufAppendS(buffer, start);
|
|
ffStrbufAppend(buffer, placeholderValue);
|
|
|
|
if(index < formatStringLength)
|
|
ffStrbufAppendC(buffer, '}');
|
|
}
|
|
|
|
static inline bool formatArgSet(const FFformatarg* arg)
|
|
{
|
|
return arg->value != NULL && (
|
|
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_BOOL && *(bool*)arg->value) ||
|
|
(arg->type == FF_FORMAT_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
|
|
);
|
|
}
|
|
|
|
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments)
|
|
{
|
|
uint32_t argCounter = 0;
|
|
|
|
uint32_t numOpenIfs = 0;
|
|
uint32_t numOpenNotIfs = 0;
|
|
|
|
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
|
|
|
for(uint32_t i = 0; i < formatstr->length; ++i)
|
|
{
|
|
// if we don't have a placeholder start just copy the chars over to output buffer
|
|
if(formatstr->chars[i] != '{')
|
|
{
|
|
ffStrbufAppendC(buffer, formatstr->chars[i]);
|
|
continue;
|
|
}
|
|
|
|
// jump to next char, the start of the placeholder value
|
|
++i;
|
|
|
|
// double {{ elvaluates to a single { and doesn't count as start
|
|
if(formatstr->chars[i] == '{')
|
|
{
|
|
ffStrbufAppendC(buffer, '{');
|
|
continue;
|
|
}
|
|
|
|
ffStrbufClear(&placeholderValue);
|
|
|
|
{
|
|
uint32_t iEnd = ffStrbufNextIndexC(formatstr, i, '}');
|
|
ffStrbufAppendNS(&placeholderValue, iEnd - i, &formatstr->chars[i]);
|
|
i = iEnd;
|
|
}
|
|
|
|
char firstChar = placeholderValue.chars[0];
|
|
|
|
if (placeholderValue.length == 1)
|
|
{
|
|
// test if for stop, if so break the loop
|
|
if (firstChar == '-')
|
|
break;
|
|
|
|
// test for end of an if, if so do nothing
|
|
if (firstChar == '?')
|
|
{
|
|
if(numOpenIfs == 0)
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
else
|
|
--numOpenIfs;
|
|
|
|
continue;
|
|
}
|
|
|
|
// test for end of a not if, if so do nothing
|
|
if (firstChar == '/')
|
|
{
|
|
if(numOpenNotIfs == 0)
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
else
|
|
--numOpenNotIfs;
|
|
|
|
continue;
|
|
}
|
|
|
|
// test for end of a color, if so do nothing
|
|
if (firstChar == '#')
|
|
{
|
|
if (!instance.config.display.pipe)
|
|
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// test for if, if so evaluate it
|
|
if (firstChar == '?')
|
|
{
|
|
ffStrbufSubstrAfter(&placeholderValue, 0);
|
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
|
|
|
// testing for an invalid index
|
|
if (index > numArgs)
|
|
{
|
|
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
|
|
continue;
|
|
}
|
|
|
|
// continue normally if an format arg is set and the value is > 0
|
|
if (formatArgSet(&arguments[index - 1]))
|
|
{
|
|
++numOpenIfs;
|
|
continue;
|
|
}
|
|
|
|
// fastforward to the end of the if without printing the in between
|
|
i = ffStrbufNextIndexS(formatstr, i, "{?}") + 2; // 2 is the length of "{?}" - 1 because the loop will increment it again directly after continue
|
|
continue;
|
|
}
|
|
|
|
// test for not if, if so evaluate it
|
|
if (firstChar == '/')
|
|
{
|
|
ffStrbufSubstrAfter(&placeholderValue, 0);
|
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
|
|
|
// testing for an invalid index
|
|
if (index > numArgs)
|
|
{
|
|
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
|
|
continue;
|
|
}
|
|
|
|
//continue normally if an format arg is not set or the value is 0
|
|
if (!formatArgSet(&arguments[index - 1]))
|
|
{
|
|
++numOpenNotIfs;
|
|
continue;
|
|
}
|
|
|
|
// fastforward to the end of the if without printing the in between
|
|
i = ffStrbufNextIndexS(formatstr, i, "{/}") + 2; // 2 is the length of "{/}" - 1 because the loop will increment it again directly after continue
|
|
continue;
|
|
}
|
|
|
|
//test for color, if so evaluate it
|
|
if (firstChar == '#')
|
|
{
|
|
if (!instance.config.display.pipe)
|
|
{
|
|
ffStrbufAppendS(buffer, "\e[");
|
|
ffOptionParseColorNoClear(placeholderValue.chars + 1, buffer);
|
|
ffStrbufAppendC(buffer, 'm');
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//test for constant or env var, if so evaluate it
|
|
if (firstChar == '$')
|
|
{
|
|
char* pend = NULL;
|
|
int32_t indexSigned = (int32_t) strtol(placeholderValue.chars + 1, &pend, 10);
|
|
if (pend == placeholderValue.chars + 1)
|
|
{
|
|
// treat placeholder as an environment variable
|
|
char* envValue = getenv(placeholderValue.chars + 1);
|
|
if (envValue)
|
|
ffStrbufAppendS(buffer, envValue);
|
|
else
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
}
|
|
else
|
|
{
|
|
// treat placeholder as a constant
|
|
uint32_t index = (uint32_t) (indexSigned < 0 ? (int32_t) instance.config.display.constants.length + indexSigned : indexSigned - 1);
|
|
|
|
if (*pend != '\0' || instance.config.display.constants.length <= index)
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
else
|
|
{
|
|
FFstrbuf* item = FF_LIST_GET(FFstrbuf, instance.config.display.constants, index);
|
|
ffStrbufAppend(buffer, item);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
char* pSep = placeholderValue.chars;
|
|
char cSep = '\0';
|
|
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~')
|
|
++pSep;
|
|
if (*pSep)
|
|
{
|
|
cSep = *pSep;
|
|
*pSep = '\0';
|
|
}
|
|
else
|
|
{
|
|
pSep = NULL;
|
|
}
|
|
|
|
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
|
|
|
// test for invalid index
|
|
if (index == 0)
|
|
index = ++argCounter;
|
|
|
|
if (index > numArgs)
|
|
{
|
|
if (pSep) *pSep = cSep;
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
continue;
|
|
}
|
|
|
|
if (!cSep)
|
|
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
|
|
else if (cSep == '~')
|
|
{
|
|
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
|
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
|
|
|
char* pEnd = NULL;
|
|
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
|
if (start < 0)
|
|
start = (int32_t) tempString.length + start;
|
|
if (start >= 0 && (uint32_t) start < tempString.length)
|
|
{
|
|
if (*pEnd == '\0')
|
|
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
|
else if (*pEnd == ',')
|
|
{
|
|
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
|
|
if (!*pEnd)
|
|
{
|
|
if (end < 0)
|
|
end = (int32_t) tempString.length + end;
|
|
if ((uint32_t) end > tempString.length)
|
|
end = (int32_t) tempString.length;
|
|
if (end > start)
|
|
ffStrbufAppendNS(buffer, (uint32_t) (end - start), &tempString.chars[start]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*pEnd)
|
|
{
|
|
*pSep = cSep;
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char* pEnd = NULL;
|
|
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
|
if (*pEnd != '\0')
|
|
{
|
|
*pSep = cSep;
|
|
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
|
continue;
|
|
}
|
|
|
|
bool ellipsis = false;
|
|
if (truncLength < 0)
|
|
{
|
|
ellipsis = true;
|
|
truncLength = -truncLength;
|
|
}
|
|
|
|
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
|
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
|
if (tempString.length == (uint32_t) truncLength)
|
|
ffStrbufAppend(buffer, &tempString);
|
|
else if (tempString.length > (uint32_t) truncLength)
|
|
{
|
|
if (cSep == ':')
|
|
{
|
|
ffStrbufSubstrBefore(&tempString, (uint32_t) truncLength);
|
|
ffStrbufTrimRightSpace(&tempString);
|
|
}
|
|
else
|
|
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
|
|
ffStrbufAppend(buffer, &tempString);
|
|
|
|
if (ellipsis)
|
|
ffStrbufAppendS(buffer, "…");
|
|
}
|
|
else if (cSep == ':')
|
|
ffStrbufAppend(buffer, &tempString);
|
|
else
|
|
{
|
|
if (cSep == '<')
|
|
{
|
|
ffStrbufAppend(buffer, &tempString);
|
|
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
|
}
|
|
else
|
|
{
|
|
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
|
ffStrbufAppend(buffer, &tempString);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!instance.config.display.pipe)
|
|
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
|
}
|