master ohmu/fastfetch-openbsd / src / common / impl / option.c
ohmu  ·  2026-10-15
  1#include "fastfetch.h"
  2#include "common/option.h"
  3#include "common/color.h"
  4#include "common/strutil.h"
  5
  6#include <limits.h>
  7
  8// Return start position of the inner key if the argument key belongs to the module specified, nullptr otherwise
  9const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName) {
 10    const char* subKey = argumentKey;
 11    if (!(subKey[0] == '-' && subKey[1] == '-')) {
 12        return nullptr;
 13    }
 14
 15    subKey += 2;
 16    uint32_t moduleNameLen = (uint32_t) strlen(moduleName);
 17    if (strncasecmp(subKey, moduleName, moduleNameLen) != 0) {
 18        return nullptr;
 19    }
 20
 21    subKey += moduleNameLen;
 22
 23    if (subKey[0] == '\0') {
 24        return subKey;
 25    }
 26
 27    if (subKey[0] != '-') {
 28        return nullptr;
 29    }
 30
 31    subKey += 1;
 32
 33    return subKey;
 34}
 35
 36void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer) {
 37    if (value == nullptr) {
 38        fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
 39        exit(477);
 40    }
 41
 42    ffStrbufSetS(buffer, value);
 43}
 44
 45uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value) {
 46    if (value == nullptr) {
 47        fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
 48        exit(480);
 49    }
 50
 51    char* end;
 52    unsigned long num = strtoul(value, &end, 10);
 53    if (value[0] == '-' || *end != '\0' || num > UINT32_MAX) {
 54        fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
 55        exit(479);
 56    }
 57
 58    return (uint32_t) num;
 59}
 60
 61int32_t ffOptionParseInt32(const char* argumentKey, const char* value) {
 62    if (value == nullptr) {
 63        fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
 64        exit(480);
 65    }
 66
 67    char* end;
 68    long num = strtol(value, &end, 10);
 69    if (*end != '\0' || num < INT32_MIN || num > INT32_MAX) {
 70        fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
 71        exit(479);
 72    }
 73
 74    return (int32_t) num;
 75}
 76
 77int ffOptionParseEnum(const char* argumentKey, const char* requestedKey, FFKeyValuePair pairs[]) {
 78    if (requestedKey == nullptr) {
 79        fprintf(stderr, "Error: usage: %s <value>\n", argumentKey);
 80        exit(476);
 81    }
 82
 83    for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
 84        if (ffStrEqualsIgnCase(requestedKey, pPair->key)) {
 85            return pPair->value;
 86        }
 87    }
 88
 89    fprintf(stderr, "Error: unknown %s value: %s\n", argumentKey, requestedKey);
 90    exit(478);
 91}
 92
 93bool ffOptionParseBoolean(const char* str) {
 94    return (
 95        !ffStrSet(str) ||
 96        ffStrEqualsIgnCase(str, "true") ||
 97        ffStrEqualsIgnCase(str, "yes") ||
 98        ffStrEqualsIgnCase(str, "on") ||
 99        ffStrEqualsIgnCase(str, "1"));
100}
101
102void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) {
103    if (!value || value[0] == '\0') {
104        return;
105    }
106
107    // If value is already an ANSI escape code, use it
108    if (value[0] == '\e' && value[1] == '[') {
109        ffStrbufAppendS(buffer, value + 2);
110        ffStrbufTrimRight(buffer, 'm');
111        return;
112    }
113
114    ffStrbufEnsureFree(buffer, 63);
115
116    while (*value != '\0') {
117#define FF_APPEND_COLOR_CODE_COND(prefix, code)   \
118    if (ffStrStartsWithIgnCase(value, #prefix)) { \
119        ffStrbufAppendS(buffer, code);            \
120        value += strlen(#prefix);                 \
121        continue;                                 \
122    }
123#define FF_APPEND_COLOR_PROP_COND(prefix, prop, honorBrightColor)                                       \
124    if (ffStrStartsWithIgnCase(value, #prefix)) {                                                       \
125        if (honorBrightColor && instance.config.display.brightColor) ffStrbufAppendS(buffer, "1;");     \
126        if (instance.config.display.prop.length) ffStrbufAppend(buffer, &instance.config.display.prop); \
127        else ffStrbufAppendS(buffer, FF_COLOR_FG_DEFAULT);                                              \
128        value += strlen(#prefix);                                                                       \
129        continue;                                                                                       \
130    }
131
132        if (ffCharIsEnglishAlphabet(value[0])) {
133            // clang-format off
134            FF_APPEND_COLOR_CODE_COND(reset_, FF_COLOR_MODE_RESET)
135            else FF_APPEND_COLOR_CODE_COND(bold_, FF_COLOR_MODE_BOLD)
136            else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD)
137            else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM)
138            else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC)
139            else FF_APPEND_COLOR_CODE_COND(underline_, FF_COLOR_MODE_UNDERLINE)
140            else FF_APPEND_COLOR_CODE_COND(blink_, FF_COLOR_MODE_BLINK)
141            else FF_APPEND_COLOR_CODE_COND(inverse_, FF_COLOR_MODE_INVERSE)
142            else FF_APPEND_COLOR_CODE_COND(hidden_, FF_COLOR_MODE_HIDDEN)
143            else FF_APPEND_COLOR_CODE_COND(strike_, FF_COLOR_MODE_STRIKETHROUGH)
144            else FF_APPEND_COLOR_CODE_COND(black, FF_COLOR_FG_BLACK)
145            else FF_APPEND_COLOR_CODE_COND(red, FF_COLOR_FG_RED)
146            else FF_APPEND_COLOR_CODE_COND(green, FF_COLOR_FG_GREEN)
147            else FF_APPEND_COLOR_CODE_COND(yellow, FF_COLOR_FG_YELLOW)
148            else FF_APPEND_COLOR_CODE_COND(blue, FF_COLOR_FG_BLUE)
149            else FF_APPEND_COLOR_CODE_COND(magenta, FF_COLOR_FG_MAGENTA)
150            else FF_APPEND_COLOR_CODE_COND(cyan, FF_COLOR_FG_CYAN)
151            else FF_APPEND_COLOR_CODE_COND(white, FF_COLOR_FG_WHITE)
152            else FF_APPEND_COLOR_CODE_COND(default, FF_COLOR_FG_DEFAULT)
153            else FF_APPEND_COLOR_CODE_COND(light_black, FF_COLOR_FG_LIGHT_BLACK)
154            else FF_APPEND_COLOR_CODE_COND(light_red, FF_COLOR_FG_LIGHT_RED)
155            else FF_APPEND_COLOR_CODE_COND(light_green, FF_COLOR_FG_LIGHT_GREEN)
156            else FF_APPEND_COLOR_CODE_COND(light_yellow, FF_COLOR_FG_LIGHT_YELLOW)
157            else FF_APPEND_COLOR_CODE_COND(light_blue, FF_COLOR_FG_LIGHT_BLUE)
158            else FF_APPEND_COLOR_CODE_COND(light_magenta, FF_COLOR_FG_LIGHT_MAGENTA)
159            else FF_APPEND_COLOR_CODE_COND(light_cyan, FF_COLOR_FG_LIGHT_CYAN)
160            else FF_APPEND_COLOR_CODE_COND(light_white, FF_COLOR_FG_LIGHT_WHITE)
161            else FF_APPEND_COLOR_PROP_COND(keys, colorKeys, true)
162            else FF_APPEND_COLOR_PROP_COND(title, colorTitle, true)
163            else FF_APPEND_COLOR_PROP_COND(output, colorOutput, false)
164            else FF_APPEND_COLOR_PROP_COND(separator, colorSeparator, false)
165            else {
166                fprintf(stderr, "Error: invalid color code found: %s\n", value);
167                exit(479);
168            }
169            // clang-format on
170        } else if (value[0] == '@') {
171            // Xterm 256 color
172            ++value;
173            char* pend = nullptr;
174            uint32_t color = (uint32_t) strtoul(value, &pend, 10);
175            if (pend == value || color > 255) {
176                fprintf(stderr, "Error: invalid 256 color code found: %s\n", value);
177                exit(479);
178            }
179
180            ffStrbufAppendS(buffer, FF_COLOR_FG_256);
181            ffStrbufAppendUInt(buffer, color);
182            value = pend;
183            continue;
184        } else if (value[0] == '#') {
185            // RGB color
186            ++value;
187            char* pend = nullptr;
188            uint32_t rgb = (uint32_t) strtoul(value, &pend, 16);
189            if (pend == value) {
190                fprintf(stderr, "Error: invalid RGB color code found: %s\n", value);
191                exit(479);
192            }
193            if (pend - value > 6) {
194                fprintf(stderr, "Error: RGB color code too long: %s\n", value);
195                exit(479);
196            } else if (pend - value == 3) {
197                rgb = ((rgb & 0xF00) >> 8) * 0x110000 +
198                    ((rgb & 0x0F0) >> 4) * 0x001100 +
199                    ((rgb & 0x00F) >> 0) * 0x000011;
200            } else if (pend - value != 6) {
201                fprintf(stderr, "Error: invalid RGB color code length: %s\n", value);
202                exit(479);
203            }
204
205            uint32_t r = rgb >> 16, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF;
206            ffStrbufAppendF(buffer, FF_COLOR_FG_RGB "%u;%u;%u", r, g, b);
207            value = pend;
208            continue;
209        }
210        ffStrbufAppendC(buffer, *value);
211        ++value;
212
213#undef FF_APPEND_COLOR_CODE_COND
214#undef FF_APPEND_COLOR_PROP_COND
215    }
216}