#include "fastfetch.h" #include "common/jsonconfig.h" #include "util/stringUtils.h" #include "options/display.h" #include const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_val* root) { yyjson_val* object = yyjson_obj_get(root, "display"); if (!object) return NULL; if (!yyjson_is_obj(object)) return "Property 'display' must be an object"; yyjson_val *key_, *val; size_t idx, max; yyjson_obj_foreach(object, idx, max, key_, val) { const char* key = yyjson_get_str(key_); if (ffStrEqualsIgnCase(key, "stat")) { if ((options->stat = yyjson_get_bool(val))) options->showErrors = true; } else if (ffStrEqualsIgnCase(key, "pipe")) options->pipe = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "showErrors")) options->showErrors = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "disableLinewrap")) options->disableLinewrap = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "hideCursor")) options->hideCursor = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "separator")) ffStrbufSetS(&options->keyValueSeparator, yyjson_get_str(val)); else if (ffStrEqualsIgnCase(key, "color")) { if (yyjson_is_str(val)) { ffOptionParseColor(yyjson_get_str(val), &options->colorKeys); ffStrbufSet(&options->colorTitle, &options->colorKeys); } else if (yyjson_is_obj(val)) { const char* colorKeys = yyjson_get_str(yyjson_obj_get(val, "keys")); if (colorKeys) ffOptionParseColor(colorKeys, &options->colorKeys); const char* colorTitle = yyjson_get_str(yyjson_obj_get(val, "title")); if (colorTitle) ffOptionParseColor(colorTitle, &options->colorTitle); } else return "display.color must be either a string or an object"; } else if (ffStrEqualsIgnCase(key, "brightColor")) options->brightColor = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "binaryPrefix")) { int value; const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) { { "iec", FF_BINARY_PREFIX_TYPE_IEC }, { "si", FF_BINARY_PREFIX_TYPE_SI }, { "jedec", FF_BINARY_PREFIX_TYPE_JEDEC }, {}, }); if (error) return error; options->binaryPrefixType = (FFBinaryPrefixType) value; } else if (ffStrEqualsIgnCase(key, "size")) { if (!yyjson_is_obj(val)) return "display.size must be an object"; yyjson_val* maxPrefix = yyjson_obj_get(val, "maxPrefix"); if (maxPrefix) { int value; const char* error = ffJsonConfigParseEnum(maxPrefix, &value, (FFKeyValuePair[]) { { "B", 0 }, { "kB", 1 }, { "MB", 2 }, { "GB", 3 }, { "TB", 4 }, { "PB", 5 }, { "EB", 6 }, { "ZB", 7 }, { "YB", 8 }, {} }); if (error) return error; options->sizeMaxPrefix = (uint8_t) value; } yyjson_val* ndigits = yyjson_obj_get(val, "ndigits"); if (ndigits) options->percentNdigits = (uint8_t) yyjson_get_uint(ndigits); } else if (ffStrEqualsIgnCase(key, "temperatureUnit")) { int value; const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) { { "CELSIUS", FF_TEMPERATURE_UNIT_CELSIUS }, { "C", FF_TEMPERATURE_UNIT_CELSIUS }, { "FAHRENHEIT", FF_TEMPERATURE_UNIT_FAHRENHEIT }, { "F", FF_TEMPERATURE_UNIT_FAHRENHEIT }, { "KELVIN", FF_TEMPERATURE_UNIT_KELVIN }, { "K", FF_TEMPERATURE_UNIT_KELVIN }, {}, }); if (error) return error; options->temperatureUnit = (FFTemperatureUnit) value; } else if (ffStrEqualsIgnCase(key, "percent")) { if (!yyjson_is_obj(val)) return "display.percent must be an object"; yyjson_val* type = yyjson_obj_get(val, "type"); if (type) options->percentType = (uint8_t) yyjson_get_uint(type); yyjson_val* ndigits = yyjson_obj_get(val, "ndigits"); if (ndigits) options->percentNdigits = (uint8_t) yyjson_get_uint(ndigits); } else if (ffStrEqualsIgnCase(key, "bar")) { if (yyjson_is_obj(val)) { const char* charElapsed = yyjson_get_str(yyjson_obj_get(val, "charElapsed")); if (charElapsed) ffStrbufSetS(&options->barCharElapsed, charElapsed); const char* charTotal = yyjson_get_str(yyjson_obj_get(val, "charTotal")); if (charTotal) ffStrbufSetS(&options->barCharTotal, charTotal); yyjson_val* border = yyjson_obj_get(val, "border"); if (border) options->barBorder = yyjson_get_bool(border); yyjson_val* width = yyjson_obj_get(val, "width"); if (width) options->barWidth = (uint8_t) yyjson_get_uint(width); } else return "display.bar must be an object"; } else if (ffStrEqualsIgnCase(key, "noBuffer")) options->noBuffer = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "keyWidth")) options->keyWidth = (uint32_t) yyjson_get_uint(val); else if (ffStrStartsWithIgnCase(key, "percent")) return "Property `display.percentX` has been changed to `display.percent.x`"; else if (ffStrStartsWithIgnCase(key, "size")) return "Property `display.sizeX` has been changed to `display.size.x`"; else return "Unknown display property"; } return NULL; } static inline void optionCheckString(const char* key, const char* value, FFstrbuf* buffer) { if(value == NULL) { fprintf(stderr, "Error: usage: %s \n", key); exit(477); } ffStrbufEnsureFree(buffer, 63); //This is not needed, as ffStrbufSetS will resize capacity if needed, but giving a higher start should improve performance } bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key, const char* value) { if(ffStrEqualsIgnCase(key, "--stat")) { if((options->stat = ffOptionParseBoolean(value))) options->showErrors = true; } else if(ffStrEqualsIgnCase(key, "--pipe")) options->pipe = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--show-errors")) options->showErrors = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--disable-linewrap")) options->disableLinewrap = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--hide-cursor")) options->hideCursor = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--separator")) ffOptionParseString(key, value, &options->keyValueSeparator); else if(ffStrEqualsIgnCase(key, "--color")) { optionCheckString(key, value, &options->colorKeys); ffOptionParseColor(value, &options->colorKeys); ffStrbufSet(&options->colorTitle, &options->colorKeys); } else if(ffStrStartsWithIgnCase(key, "--color-")) { const char* subkey = key + strlen("--color-"); if(ffStrEqualsIgnCase(subkey, "keys")) { optionCheckString(key, value, &options->colorKeys); ffOptionParseColor(value, &options->colorKeys); } else if(ffStrEqualsIgnCase(subkey, "title")) { optionCheckString(key, value, &options->colorTitle); ffOptionParseColor(value, &options->colorTitle); } else return false; } else if(ffStrEqualsIgnCase(key, "--key-width")) options->keyWidth = ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(key, "--bright-color")) options->brightColor = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--binary-prefix")) { options->binaryPrefixType = (FFBinaryPrefixType) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { { "iec", FF_BINARY_PREFIX_TYPE_IEC }, { "si", FF_BINARY_PREFIX_TYPE_SI }, { "jedec", FF_BINARY_PREFIX_TYPE_JEDEC }, {} }); } else if(ffStrEqualsIgnCase(key, "--size-ndigits")) options->sizeNdigits = (uint8_t) ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(key, "--size-max-prefix")) { options->sizeMaxPrefix = (uint8_t) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { { "B", 0 }, { "kB", 1 }, { "MB", 2 }, { "GB", 3 }, { "TB", 4 }, { "PB", 5 }, { "EB", 6 }, { "ZB", 7 }, { "YB", 8 }, {} }); } else if(ffStrEqualsIgnCase(key, "--temperature-unit")) { options->temperatureUnit = (FFTemperatureUnit) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { { "CELSIUS", FF_TEMPERATURE_UNIT_CELSIUS }, { "C", FF_TEMPERATURE_UNIT_CELSIUS }, { "FAHRENHEIT", FF_TEMPERATURE_UNIT_FAHRENHEIT }, { "F", FF_TEMPERATURE_UNIT_FAHRENHEIT }, { "KELVIN", FF_TEMPERATURE_UNIT_KELVIN }, { "K", FF_TEMPERATURE_UNIT_KELVIN }, {}, }); } else if(ffStrEqualsIgnCase(key, "--percent-type")) options->percentType = (uint8_t) ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(key, "--percent-ndigits")) options->percentNdigits = (uint8_t) ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(key, "--no-buffer")) options->noBuffer = ffOptionParseBoolean(value); else if(ffStrStartsWithIgnCase(key, "--bar-")) { const char* subkey = key + strlen("--bar-"); if(ffStrEqualsIgnCase(subkey, "char-elapsed")) ffOptionParseString(key, value, &options->barCharElapsed); else if(ffStrEqualsIgnCase(subkey, "char-total")) ffOptionParseString(key, value, &options->barCharTotal); else if(ffStrEqualsIgnCase(subkey, "width")) options->barWidth = (uint8_t) ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(subkey, "border")) options->barBorder = ffOptionParseBoolean(value); else return false; } else return false; return true; } void ffOptionsInitDisplay(FFOptionsDisplay* options) { ffStrbufInit(&options->colorKeys); ffStrbufInit(&options->colorTitle); options->brightColor = true; ffStrbufInitStatic(&options->keyValueSeparator, ": "); options->showErrors = false; options->pipe = !isatty(STDOUT_FILENO); #ifdef NDEBUG options->disableLinewrap = !options->pipe; options->hideCursor = !options->pipe; #else options->disableLinewrap = false; options->hideCursor = false; #endif options->binaryPrefixType = FF_BINARY_PREFIX_TYPE_IEC; options->sizeNdigits = 2; options->sizeMaxPrefix = UINT8_MAX; options->temperatureUnit = FF_TEMPERATURE_UNIT_CELSIUS; options->stat = false; options->noBuffer = false; options->keyWidth = 0; ffStrbufInitStatic(&options->barCharElapsed, "■"); ffStrbufInitStatic(&options->barCharTotal, "-"); options->barWidth = 10; options->barBorder = true; options->percentType = 9; options->percentNdigits = 0; } void ffOptionsDestroyDisplay(FFOptionsDisplay* options) { ffStrbufDestroy(&options->colorKeys); ffStrbufDestroy(&options->colorTitle); ffStrbufDestroy(&options->keyValueSeparator); ffStrbufDestroy(&options->barCharElapsed); ffStrbufDestroy(&options->barCharTotal); } void ffOptionsGenerateDisplayJsonConfig(FFOptionsDisplay* options, yyjson_mut_doc* doc) { __attribute__((__cleanup__(ffOptionsDestroyDisplay))) FFOptionsDisplay defaultOptions; ffOptionsInitDisplay(&defaultOptions); yyjson_mut_val* obj = yyjson_mut_obj(doc); if (options->stat != defaultOptions.stat) yyjson_mut_obj_add_bool(doc, obj, "stat", options->stat); if (options->pipe != defaultOptions.pipe) yyjson_mut_obj_add_bool(doc, obj, "pipe", options->pipe); if (options->showErrors != defaultOptions.showErrors) yyjson_mut_obj_add_bool(doc, obj, "showErrors", options->showErrors); if (options->disableLinewrap != defaultOptions.disableLinewrap) yyjson_mut_obj_add_bool(doc, obj, "disableLinewrap", options->disableLinewrap); if (options->hideCursor != defaultOptions.hideCursor) yyjson_mut_obj_add_bool(doc, obj, "hideCursor", options->hideCursor); if (!ffStrbufEqual(&options->keyValueSeparator, &defaultOptions.keyValueSeparator)) yyjson_mut_obj_add_strbuf(doc, obj, "separator", &options->keyValueSeparator); { yyjson_mut_val* color = yyjson_mut_obj(doc); if (!ffStrbufEqual(&options->colorKeys, &defaultOptions.colorKeys)) yyjson_mut_obj_add_strbuf(doc, color, "keys", &options->colorKeys); if (!ffStrbufEqual(&options->colorTitle, &defaultOptions.colorTitle)) yyjson_mut_obj_add_strbuf(doc, color, "title", &options->colorTitle); if (yyjson_mut_obj_size(color) > 0) { if (yyjson_mut_obj_size(color) == 2 && ffStrbufEqual(&options->colorKeys, &options->colorTitle)) yyjson_mut_obj_add_strbuf(doc, obj, "color", &options->colorKeys); else yyjson_mut_obj_add_val(doc, obj, "color", color); } } if (options->brightColor != defaultOptions.brightColor) yyjson_mut_obj_add_bool(doc, obj, "brightColor", options->brightColor); if (options->binaryPrefixType != defaultOptions.binaryPrefixType) { switch (options->binaryPrefixType) { case FF_BINARY_PREFIX_TYPE_IEC: yyjson_mut_obj_add_str(doc, obj, "binaryPrefix", "iec"); break; case FF_BINARY_PREFIX_TYPE_SI: yyjson_mut_obj_add_str(doc, obj, "binaryPrefix", "si"); break; case FF_BINARY_PREFIX_TYPE_JEDEC: yyjson_mut_obj_add_str(doc, obj, "binaryPrefix", "jedec"); break; } } { yyjson_mut_val* size = yyjson_mut_obj(doc); if (options->sizeNdigits != defaultOptions.sizeNdigits) yyjson_mut_obj_add_uint(doc, size, "ndigits", options->sizeNdigits); if (options->sizeMaxPrefix != defaultOptions.sizeMaxPrefix && options->sizeMaxPrefix <= 8) { yyjson_mut_obj_add_str(doc, size, "maxPrefix", ((const char* []) { "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", })[options->sizeMaxPrefix]); } if (yyjson_mut_obj_size(size) > 0) yyjson_mut_obj_add_val(doc, obj, "size", size); } if (options->temperatureUnit != defaultOptions.temperatureUnit) { switch (options->temperatureUnit) { case FF_TEMPERATURE_UNIT_CELSIUS: yyjson_mut_obj_add_str(doc, obj, "temperatureUnit", "C"); break; case FF_TEMPERATURE_UNIT_FAHRENHEIT: yyjson_mut_obj_add_str(doc, obj, "temperatureUnit", "F"); break; case FF_TEMPERATURE_UNIT_KELVIN: yyjson_mut_obj_add_str(doc, obj, "temperatureUnit", "K"); break; } } { yyjson_mut_val* percent = yyjson_mut_obj(doc); if (options->percentType != defaultOptions.percentType) yyjson_mut_obj_add_uint(doc, percent, "type", options->percentType); if (options->percentNdigits != defaultOptions.percentNdigits) yyjson_mut_obj_add_uint(doc, percent, "ndigits", options->percentNdigits); if (yyjson_mut_obj_size(percent) > 0) yyjson_mut_obj_add_val(doc, obj, "percent", percent); } { yyjson_mut_val* bar = yyjson_mut_obj(doc); if (!ffStrbufEqual(&options->barCharElapsed, &defaultOptions.barCharElapsed)) yyjson_mut_obj_add_strbuf(doc, bar, "charElapsed", &options->barCharElapsed); if (!ffStrbufEqual(&options->barCharTotal, &defaultOptions.barCharTotal)) yyjson_mut_obj_add_strbuf(doc, bar, "charTotal", &options->barCharTotal); if (options->barBorder != defaultOptions.barBorder) yyjson_mut_obj_add_bool(doc, bar, "border", options->barBorder); if (options->barWidth != defaultOptions.barWidth) yyjson_mut_obj_add_uint(doc, bar, "width", options->barWidth); if (yyjson_mut_obj_size(bar) > 0) yyjson_mut_obj_add_val(doc, obj, "bar", bar); } if (options->noBuffer != defaultOptions.noBuffer) yyjson_mut_obj_add_bool(doc, obj, "noBuffer", options->noBuffer); if (options->keyWidth != defaultOptions.keyWidth) yyjson_mut_obj_add_uint(doc, obj, "keyWidth", options->keyWidth); if (yyjson_mut_obj_size(obj) > 0) yyjson_mut_obj_add_val(doc, doc->root, "display", obj); }