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}