ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/strutil.h"
4#include "common/textModifier.h"
5#include "logo/logo.h"
6#include "modules/separator/separator.h"
7
8bool ffPrintSeparator(FFSeparatorOptions* options) {
9 ffLogoPrintLine();
10
11 if (options->outputColor.length && !instance.config.display.pipe) {
12 ffPrintColor(&options->outputColor);
13 }
14
15 if (options->times > 0) {
16 if (__builtin_expect(options->string.length == 1, 1)) {
17 ffPrintCharTimes(options->string.chars[0], options->times);
18 } else {
19 for (uint32_t i = 0; i < options->times; i++) {
20 fputs(options->string.chars, stdout);
21 }
22 }
23 } else {
24 const FFPlatform* platform = &instance.state.platform;
25
26 uint32_t titleLength = 1 // @
27 + ffUtf8StrWidth(platform->userName.chars, platform->userName.length) // user name
28 + (instance.state.titleFqdn ? platform->hostName.length : ffStrbufFirstIndexC(&platform->hostName, '.')); // host name
29
30 if (__builtin_expect(options->string.length == 1, 1)) {
31 ffPrintCharTimes(options->string.chars[0], titleLength);
32 } else {
33 uint32_t wcsLength = ffUtf8StrWidth(options->string.chars, options->string.length);
34
35 int remaining = (int) titleLength;
36 // Write the whole separator as often as it fits fully into titleLength
37 for (; remaining >= (int) wcsLength; remaining -= (int) wcsLength) {
38 ffStrbufWriteTo(&options->string, stdout);
39 }
40
41 if (remaining > 0) {
42 // Write as much of the separator as needed to fill titleLength
43 if (wcsLength != options->string.length) {
44 // Unicode chars
45 const char* ptr = options->string.chars;
46 uint32_t remainBytes = options->string.length;
47 while (remaining > 0 && remainBytes > 0 && *ptr != '\0') {
48 uint8_t charWidth = 0;
49 uint8_t bytes = ffUtf8CharLenWidth(ptr, remainBytes, &charWidth);
50 if (__builtin_expect(bytes == 0, false)) {
51 break;
52 }
53
54 remaining -= (int) charWidth;
55 ptr += bytes;
56 remainBytes -= bytes;
57 }
58 fwrite(options->string.chars, (size_t) (ptr - options->string.chars), 1, stdout);
59 } else {
60 fwrite(options->string.chars, (size_t) remaining, 1, stdout);
61 }
62 }
63 }
64 }
65
66 if (options->outputColor.length && !instance.config.display.pipe) {
67 fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
68 }
69 putchar('\n');
70
71 return true;
72}
73
74void ffParseSeparatorJsonObject(FFSeparatorOptions* options, yyjson_val* module) {
75 yyjson_val *key, *val;
76 size_t idx, max;
77 yyjson_obj_foreach (module, idx, max, key, val) {
78 if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition")) {
79 continue;
80 }
81
82 if (unsafe_yyjson_equals_str(key, "string")) {
83 ffStrbufSetJsonVal(&options->string, val);
84 continue;
85 }
86
87 if (unsafe_yyjson_equals_str(key, "outputColor")) {
88 ffOptionParseColor(yyjson_get_str(val), &options->outputColor);
89 continue;
90 }
91
92 if (unsafe_yyjson_equals_str(key, "times")) {
93 if (!ffJsonConfigParseUInt32(val, &options->times, UINT32_MAX)) {
94 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Property 'times' must be a non-negative integer no greater than 4294967295");
95 continue;
96 }
97 continue;
98 }
99
100 if (unsafe_yyjson_equals_str(key, "length")) {
101 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "The option length has been renamed to times.");
102 continue;
103 }
104
105 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
106 }
107}
108
109[[gnu::cold]]
110void ffGenerateSeparatorJsonConfig(FFSeparatorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
111 yyjson_mut_obj_add_strbuf(doc, module, "string", &options->string);
112 yyjson_mut_obj_add_strbuf(doc, module, "outputColor", &options->outputColor);
113 yyjson_mut_obj_add_uint(doc, module, "times", options->times);
114}
115
116void ffInitSeparatorOptions(FFSeparatorOptions* options) {
117 ffStrbufInitStatic(&options->string, "-");
118 ffStrbufInit(&options->outputColor);
119 options->times = 0;
120}
121
122void ffDestroySeparatorOptions(FFSeparatorOptions* options) {
123 ffStrbufDestroy(&options->string);
124 ffStrbufDestroy(&options->outputColor);
125}
126
127FFModuleBaseInfo ffSeparatorModuleInfo = {
128 .name = "Separator",
129 .description = "Print a separator line",
130 .displayName = {
131 .en = "Separator",
132 .ar = "الفاصل",
133 .cs = "Oddělovač",
134 .de = "Trennlinie",
135 .es = "Separador",
136 .fi = "Erotin",
137 .fr = "Séparateur",
138 .gl = "Separador",
139 .he = "מפריד",
140 .id = "Pemisah",
141 .it = "Separatore",
142 .ja = "セパレーター",
143 .ko = "구분선",
144 .pl = "Separator",
145 .pt = "Separador",
146 .ru = "Разделитель",
147 .tr = "Ayraç",
148 .uk = "Роздільник",
149 .vi = "Dấu phân cách",
150 .zh_CN = "分隔线",
151 .zh_TW = "分隔線",
152 },
153 .initOptions = (void*) ffInitSeparatorOptions,
154 .destroyOptions = (void*) ffDestroySeparatorOptions,
155 .parseJsonObject = (void*) ffParseSeparatorJsonObject,
156 .printModule = (void*) ffPrintSeparator,
157 .generateJsonConfig = (void*) ffGenerateSeparatorJsonConfig,
158 .defaultOrder = 2,
159};