#include "fastfetch.h" #include "common/format.h" #include "common/parsing.h" #include "util/textModifier.h" #include "util/stringUtils.h" #include void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) { if(formatarg->type == FF_FORMAT_ARG_TYPE_INT) ffStrbufAppendF(buffer, "%i", *(int*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT) ffStrbufAppendF(buffer, "%" PRIu32, *(uint32_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT64) ffStrbufAppendF(buffer, "%" PRIu64, *(uint64_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16) ffStrbufAppendF(buffer, "%" PRIu16, *(uint16_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8) ffStrbufAppendF(buffer, "%" PRIu8, *(uint8_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_STRING) ffStrbufAppendS(buffer, (const char*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_STRBUF) ffStrbufAppend(buffer, (FFstrbuf*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_FLOAT) ffStrbufAppendF(buffer, "%f", *(float*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_DOUBLE) ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_BOOL) ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false"); else if(formatarg->type == FF_FORMAT_ARG_TYPE_LIST) { const FFlist* list = formatarg->value; for(uint32_t i = 0; i < list->length; i++) { ffStrbufAppend(buffer, ffListGet(list, i)); if(i < list->length - 1) ffStrbufAppendS(buffer, ", "); } } else if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL) { fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type); } } /** * @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 inline uint32_t getArgumentIndex(const FFstrbuf* placeholderValue) { uint32_t result = UINT32_MAX; if(placeholderValue->chars[0] != '-') sscanf(placeholderValue->chars, "%u", &result); return result == 0 ? UINT32_MAX : result; } 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 void appendEmptyPlaceholder(FFstrbuf* buffer, const char* placeholder, uint32_t* argCounter, uint32_t numArgs, const FFformatarg* arguments) { if(*argCounter >= numArgs) ffStrbufAppendS(buffer, placeholder); else ffFormatAppendFormatArg(buffer, arguments + *argCounter); (*argCounter)++; } static inline bool formatArgSet(const FFformatarg* arg) { return arg->value != NULL && ( (arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) || //Also is false for NaN (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(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 && arg->value != NULL) ); } 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; uint32_t numOpenColors = 0; 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; } // if we have an { at the end handle it as {} if(i == formatstr->length - 1) { appendEmptyPlaceholder(buffer, "{", &argCounter, numArgs, arguments); 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; } // placeholder is {} if(formatstr->chars[i] == '}') { appendEmptyPlaceholder(buffer, "{}", &argCounter, numArgs, arguments); continue; } FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate(); while(i < formatstr->length && formatstr->chars[i] != '}') ffStrbufAppendC(&placeholderValue, formatstr->chars[i++]); // test if for stop, if so break the loop if(placeholderValue.length == 1 && placeholderValue.chars[0] == '-') break; // test for end of an if, if so do nothing if(placeholderValue.length == 1 && placeholderValue.chars[0] == '?') { if(numOpenIfs == 0) appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); else --numOpenIfs; continue; } // test for end of a not if, if so do nothing if(placeholderValue.length == 1 && placeholderValue.chars[0] == '/') { if(numOpenNotIfs == 0) appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); else --numOpenNotIfs; continue; } // test for end of a color, if so do nothing if(placeholderValue.length == 1 && placeholderValue.chars[0] == '#') { if(numOpenColors == 0) appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); else { ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); --numOpenColors; } continue; } // test for if, if so evaluate it if(placeholderValue.chars[0] == '?') { ffStrbufSubstrAfter(&placeholderValue, 0); uint32_t index = getArgumentIndex(&placeholderValue); // 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 increament it again directly after continue continue; } // test for not if, if so evaluate it if(placeholderValue.chars[0] == '/') { ffStrbufSubstrAfter(&placeholderValue, 0); uint32_t index = getArgumentIndex(&placeholderValue); // 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 increament it again directly after continue continue; } //test for color, if so evaluate it if(placeholderValue.chars[0] == '#') { ++numOpenColors; ffStrbufSubstrAfter(&placeholderValue, 0); ffStrbufAppendS(buffer, "\033["); ffStrbufAppend(buffer, &placeholderValue); ffStrbufAppendC(buffer, 'm'); continue; } uint32_t index = getArgumentIndex(&placeholderValue); // test for invalid index if(index > numArgs) { appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); continue; } ffFormatAppendFormatArg(buffer, &arguments[index - 1]); } ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); }