diff --git a/src/common/format.c b/src/common/format.c index 4957527e4..44193acc9 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -17,87 +17,229 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) 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); - ffStrbufDestroy(buffer); exit(806); } } +static inline bool placeholderValueIsForError(const FFstrbuf* placeholderValue) +{ + return + ffStrbufIgnCaseCompS(placeholderValue, "e") == 0 || + ffStrbufIgnCaseCompS(placeholderValue, "error") == 0 || + ffStrbufIgnCaseCompS(placeholderValue, "0") == 0 + ; +} + +static inline uint32_t getArgumentIndex(const FFstrbuf* placeholderValue) +{ + uint32_t result = UINT32_MAX; + + if(placeholderValue->chars[0] != '-') + sscanf(placeholderValue->chars, "%u", &result); + + return 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)++]); +} + +static inline bool formatArgSet(const FFformatarg* arg) +{ + return arg->value != NULL && ( + (arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 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 && *(const char*)arg->value != '\0') || + (arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) || + (arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) + ); +} + void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments) { - uint32_t argCounter = 1; //First arg is 1 in fomat string + uint32_t argCounter = 0; + + uint32_t numOpenIfs = 0; + uint32_t numOpenNotIfs = 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) { - ffStrbufAppendC(buffer, '{'); - continue; //This will always stop the loop + 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; } - uint32_t argIndex; - + // placeholder is {} if(formatstr->chars[i] == '}') { - if(argCounter > numArgs) - { - ffStrbufAppendS(buffer, "{}"); - continue; - } - - argIndex = argCounter++; + appendEmptyPlaceholder(buffer, "{}", &argCounter, numArgs, arguments); + continue; } - else + + FFstrbuf placeholderValue; + ffStrbufInit(&placeholderValue); + + while(formatstr->chars[i] != '}' && i < formatstr->length) + ffStrbufAppendC(&placeholderValue, formatstr->chars[i++]); + + // test for error, if so print it + if(placeholderValueIsForError(&placeholderValue)) { + ffStrbufAppend(buffer, error); + ffStrbufDestroy(&placeholderValue); + continue; + } + + // test if for stop, if so break the loop + if(placeholderValue.length == 1 && placeholderValue.chars[0] == '-') { - FFstrbuf argnumstr; - ffStrbufInit(&argnumstr); - - while(formatstr->chars[i] != '}' && i < formatstr->length) - ffStrbufAppendC(&argnumstr, formatstr->chars[i++]); - - if( - ffStrbufIgnCaseCompS(&argnumstr, "e") == 0 || - ffStrbufIgnCaseCompS(&argnumstr, "error") == 0 || - ffStrbufIgnCaseCompS(&argnumstr, "0") == 0 - ) { - ffStrbufAppend(buffer, error); - ffStrbufDestroy(&argnumstr); - continue; - } - - //Test if argnumstr is valid - if( - argnumstr.length == 0 || - ffStrbufGetC(&argnumstr, 0) == '-' || - sscanf(argnumstr.chars, "%u", &argIndex) != 1 || - argIndex > numArgs - ) { - //Not valid - ffStrbufAppendC(buffer, '{'); - ffStrbufAppend(buffer, &argnumstr); - if(formatstr->chars[i] == '}') // We dont have a closing { when ending because whole format string is over - ffStrbufAppendC(buffer, '}'); - ffStrbufDestroy(&argnumstr); - continue; - } - - ffStrbufDestroy(&argnumstr); + ffStrbufDestroy(&placeholderValue); + break; } - ffFormatAppendFormatArg(buffer, &arguments[argIndex - 1]); + // 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; + + ffStrbufDestroy(&placeholderValue); + 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; + + ffStrbufDestroy(&placeholderValue); + continue; + } + + // test for if, if so evaluate it + if(placeholderValue.chars[0] == '?') + { + ffStrbufSubstrAfter(&placeholderValue, 0); + + // continue if an error is set + if(placeholderValueIsForError(&placeholderValue) && error != NULL && error->length > 0) + { + ++numOpenIfs; + ffStrbufDestroy(&placeholderValue); + continue; + } + + uint32_t index = getArgumentIndex(&placeholderValue); + + // testing for an invalid index + if(index > numArgs) + { + appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length); + ffStrbufDestroy(&placeholderValue); + continue; + } + + // continue normally if an format arg is set and the value is > 0 + if(formatArgSet(&arguments[index - 1])) + { + ++numOpenIfs; + ffStrbufDestroy(&placeholderValue); + continue; + } + + // fastforward to the end of the if without printing the in between + i = ffStrbufFirstIndexAfterS(formatstr, i, "{?}") + 2; // 2 is the length of "{?}" -1 because the loop will increament it again directly after continue + ffStrbufDestroy(&placeholderValue); + continue; + } + + // test for not if, if so evaluate it + if(placeholderValue.chars[0] == '/') + { + ffStrbufSubstrAfter(&placeholderValue, 0); + + //continue if an error os not set + if(placeholderValueIsForError(&placeholderValue) && (error == NULL || error->length == 0)) + { + ++numOpenNotIfs; + ffStrbufDestroy(&placeholderValue); + continue; + } + + uint32_t index = getArgumentIndex(&placeholderValue); + + // testing for an invalid index + if(index > numArgs) + { + appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length); + ffStrbufDestroy(&placeholderValue); + continue; + } + + //continue normally if an format arg is not set or the value is 0 + if(!formatArgSet(&arguments[index - 1])) + { + ++numOpenNotIfs; + ffStrbufDestroy(&placeholderValue); + continue; + } + + // fastforward to the end of the if without printing the in between + i = ffStrbufFirstIndexAfterS(formatstr, i, "{/}") + 2; // 2 is the length of "{/}" -1 because the loop will increament it again directly after continue + ffStrbufDestroy(&placeholderValue); + continue; + } + + uint32_t index = getArgumentIndex(&placeholderValue); + + // test for invalid index + if(index > numArgs) + { + appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); + ffStrbufDestroy(&placeholderValue); + continue; + } + + ffFormatAppendFormatArg(buffer, &arguments[index - 1]); + + ffStrbufDestroy(&placeholderValue); } ffStrbufTrimRight(buffer, ' '); diff --git a/src/common/io.c b/src/common/io.c index 21f34ef03..35af5f7f2 100644 --- a/src/common/io.c +++ b/src/common/io.c @@ -55,15 +55,12 @@ void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIn FF_STRBUF_CREATE(error); ffStrbufAppendVF(&error, message, arguments); - FFformatarg nullArgs[numFormatArgs]; - for(uint32_t i = 0; i < numFormatArgs; i++) - { - nullArgs[i].type = FF_FORMAT_ARG_TYPE_NULL; - nullArgs[i].value = NULL; - } + // calloc sets all to 0 and FF_FORMAT_ARG_TYPE_NULL also has value 0 so we don't need to explictly set it + FFformatarg* nullArgs = calloc(numFormatArgs, sizeof(FFformatarg)); ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, &error, numFormatArgs, nullArgs); + free(nullArgs); ffStrbufDestroy(&error); } @@ -195,7 +192,7 @@ static bool printCachedFormat(FFinstance* instance, const char* moduleName, cons uint8_t moduleCounter = 1; - FFformatarg arguments[numArgs]; + FFformatarg* arguments = calloc(numArgs, sizeof(FFformatarg)); uint32_t argumentCounter = 0; uint32_t lastIndex = 0; @@ -218,6 +215,7 @@ static bool printCachedFormat(FFinstance* instance, const char* moduleName, cons lastIndex = nullByteIndex + 1; } + free(arguments); ffStrbufDestroy(&content); return moduleCounter > 1; diff --git a/src/fastfetch.c b/src/fastfetch.c index 5c373d81c..1d87b03bf 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -154,11 +154,25 @@ static inline void printCommandHelpFormat() "\n" "To make formatting easier, a double open curly brace (\"{{\") will be printed as a single open curly brace and not counted as the beginn of a placeholder.\n" "If a value index is missformatted or wants a non existing value, it will be printed as is, with the curly braces around it.\n" - "If the last placeholder isn't closed, it will be printed as is, with the open curly braces at the start.\n" + "If the last placeholder isn't closed, it will be treated like it was at the end of the format string.\n" + "\n" + "To only print something if a variable is set use \"{?} ... {?}\".\n" + "For example to only print a second value if it is set use \"{?2} Second value: {2}{?}\".\n" + "If a \"{?}\" is found without an opener, it is printed as is.\n" + "\n" + "To only print something if a variable is not set, do the same as with if, just replace every '?' with a '!'.\n" + "For example to print a fallback for a second value if it is not set use \"{?2}{2}{?}{/2}Second value fallback{/}\".\n" + "\n" + "There is a special variable set if an error occured during detection, you can access it with \"{e}\", \"{error}\" or \"{0}\".\n" + "You can use ifs and not ifs with it like with an index, for example use \"{?e}some text{?}\" to print an text if an error occurred.\n" + "\n" + "To stop formating at any point in the format string, use \"{-}\".\n" + "For example to print an error instead of the normal output if it occured, prefix the format string with \"{?e}{e}{-}{?}...\".\n" "\n" "If an format string evaluates to an empty value, the whole line in the output will be discarded.\n" "You can therefore use --host-format \" \" to disable host output.\n" "Note that --host-format \"\" would evaluate as not set, and therefore use the built in host format\n" + "This can be used to print nothing if an error occured: prefix the format string with \"{?e}{-}{?}...\".\n" "\n" "Format string is also the way to go to set a fixed value, just use one without placeholders.\n" "For example when running in headless mode you could use \"--resolution-format \"Preferred\"." diff --git a/src/fastfetch.h b/src/fastfetch.h index 937ac4206..eeb6882b0 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -164,7 +164,7 @@ typedef struct FFinstance typedef enum FFformatargtype { - FF_FORMAT_ARG_TYPE_NULL, + FF_FORMAT_ARG_TYPE_NULL = 0, FF_FORMAT_ARG_TYPE_UINT, FF_FORMAT_ARG_TYPE_UINT8, FF_FORMAT_ARG_TYPE_INT, diff --git a/src/util/FFlist.h b/src/util/FFlist.h index cc50a9bbe..73f85efe2 100644 --- a/src/util/FFlist.h +++ b/src/util/FFlist.h @@ -9,7 +9,7 @@ typedef struct FFlist { - void* data; + char* data; uint32_t elementSize; uint32_t length; uint32_t capacity; diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 3c1e416c2..c8da1ef2c 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -142,6 +142,8 @@ void ffStrbufSetVF(FFstrbuf* strbuf, const char* format, va_list arguments) void ffStrbufAppend(FFstrbuf* strbuf, const FFstrbuf* value) { + if(value == NULL) + return; ffStrbufAppendNS(strbuf, value->length, value->chars); } @@ -338,11 +340,13 @@ void ffStrbufRemoveStringsA(FFstrbuf* strbuf, uint32_t numStrings, const char* s void ffStrbufRemoveStringsV(FFstrbuf* strbuf, uint32_t numStrings, va_list arguments) { - const char* strings[numStrings]; + const char** strings = calloc(numStrings, sizeof(const char*)); for(uint32_t i = 0; i < numStrings; i++) strings[i] = va_arg(arguments, const char*); ffStrbufRemoveStringsA(strbuf, numStrings, strings); + + free(strings); } void ffStrbufRemoveStrings(FFstrbuf* strbuf, uint32_t numStrings, ...) @@ -353,7 +357,7 @@ void ffStrbufRemoveStrings(FFstrbuf* strbuf, uint32_t numStrings, ...) va_end(argp); } -uint32_t ffStrbufFirstIndexAfterC(FFstrbuf* strbuf, uint32_t start, const char c) +uint32_t ffStrbufFirstIndexAfterC(const FFstrbuf* strbuf, uint32_t start, const char c) { for(uint32_t i = start; i < strbuf->length; i++) { @@ -363,12 +367,36 @@ uint32_t ffStrbufFirstIndexAfterC(FFstrbuf* strbuf, uint32_t start, const char c return strbuf->length; } -uint32_t ffStrbufFirstIndexC(FFstrbuf* strbuf, const char c) +uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, const char c) { return ffStrbufFirstIndexAfterC(strbuf, 0, c); } -uint32_t ffStrbufLastIndexC(FFstrbuf* strbuf, const char c) +uint32_t ffStrbufFirstIndexAfterS(const FFstrbuf* strbuf, uint32_t start, const char* str) +{ + for(uint32_t i = start + 1; i < strbuf->length; i++) + { + bool found = true; + + for(uint32_t k = 0; str[k] != '\0'; k++) + { + if(i + k == strbuf->length) + return strbuf->length; + + if(strbuf->chars[i + k] != str[k]) + { + found = false; + break; + } + } + + if(found) + return i; + } + return strbuf->length; +} + +uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, const char c) { //We need to loop one higher than the actual index, because uint32_t is guranteed to be >= 0, so this statement would always be true for(uint32_t i = strbuf->length; i > 0; i--) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index ba38d1ab1..23bceec88 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -72,9 +72,10 @@ void ffStrbufRemoveStringsA(FFstrbuf* strbuf, uint32_t numStrings, const char* s void ffStrbufRemoveStringsV(FFstrbuf* strbuf, uint32_t numStrings, va_list arguments); void ffStrbufRemoveStrings(FFstrbuf* strbuf, uint32_t numStrings, ...); -uint32_t ffStrbufFirstIndexAfterC(FFstrbuf* strbuf, uint32_t start, const char c); -uint32_t ffStrbufFirstIndexC(FFstrbuf* strbuf, const char c); -uint32_t ffStrbufLastIndexC(FFstrbuf* strbuf, const char c); +uint32_t ffStrbufFirstIndexAfterC(const FFstrbuf* strbuf, uint32_t start, const char c); +uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, const char c); +uint32_t ffStrbufFirstIndexAfterS(const FFstrbuf* strbuf, uint32_t start, const char* str); +uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, const char c); void ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index); void ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, const char c);