ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/textModifier.h"
4#include "modules/title/title.h"
5
6static void appendText(FFstrbuf* output, const FFstrbuf* text, const FFstrbuf* color) {
7 if (!instance.config.display.pipe) {
8 if (instance.config.display.brightColor) {
9 ffStrbufAppendS(output, FASTFETCH_TEXT_MODIFIER_BOLT);
10 }
11 if (color->length > 0) {
12 ffStrbufAppendF(output, "\e[%sm", color->chars);
13 } else if (instance.config.display.colorTitle.length > 0) {
14 ffStrbufAppendF(output, "\e[%sm", instance.config.display.colorTitle.chars);
15 }
16 }
17
18 ffStrbufAppend(output, text);
19
20 if (!instance.config.display.pipe) {
21 ffStrbufAppendS(output, FASTFETCH_TEXT_MODIFIER_RESET);
22 }
23}
24
25bool ffPrintTitle(FFTitleOptions* options) {
26 FF_STRBUF_AUTO_DESTROY userNameColored = ffStrbufCreate();
27 appendText(&userNameColored, &instance.state.platform.userName, &options->colorUser);
28
29 FF_STRBUF_AUTO_DESTROY hostName = ffStrbufCreateCopy(&instance.state.platform.hostName);
30 if (!options->fqdn) {
31 ffStrbufSubstrBeforeFirstC(&hostName, '.');
32 }
33 instance.state.titleFqdn = options->fqdn;
34
35 FF_STRBUF_AUTO_DESTROY hostNameColored = ffStrbufCreate();
36 appendText(&hostNameColored, &hostName, &options->colorHost);
37
38 FF_STRBUF_AUTO_DESTROY atColored = ffStrbufCreate();
39 if (!instance.config.display.pipe && options->colorAt.length > 0) {
40 ffStrbufAppendF(&atColored, "\e[%sm", options->colorAt.chars);
41 ffStrbufAppendC(&atColored, '@');
42 ffStrbufAppendS(&atColored, FASTFETCH_TEXT_MODIFIER_RESET);
43 } else {
44 ffStrbufAppendC(&atColored, '@');
45 }
46
47 if (options->moduleArgs.outputFormat.length == 0) {
48 ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Title), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
49
50 ffStrbufWriteTo(&userNameColored, stdout);
51 ffStrbufWriteTo(&atColored, stdout);
52 ffStrbufPutTo(&hostNameColored, stdout);
53 } else {
54 FF_STRBUF_AUTO_DESTROY cwdTilde = ffStrbufCreate();
55 if (
56#if _WIN32
57 ffStrbufStartsWithIgnCase
58#else
59 ffStrbufStartsWith
60#endif
61 (&instance.state.platform.cwd, &instance.state.platform.homeDir)) {
62 ffStrbufAppendS(&cwdTilde, "~/");
63 ffStrbufAppendNS(&cwdTilde, instance.state.platform.cwd.length - instance.state.platform.homeDir.length, &instance.state.platform.cwd.chars[instance.state.platform.homeDir.length]);
64 } else {
65 ffStrbufSet(&cwdTilde, &instance.state.platform.cwd);
66 }
67 if (cwdTilde.length > 1) {
68 ffStrbufTrimRight(&cwdTilde, '/');
69 }
70
71 FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Title), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
72 FF_ARG(instance.state.platform.userName, "user-name"),
73 FF_ARG(hostName, "host-name"),
74 FF_ARG(instance.state.platform.homeDir, "home-dir"),
75 FF_ARG(instance.state.platform.exePath, "exe-path"),
76 FF_ARG(instance.state.platform.userShell, "user-shell"),
77 FF_ARG(userNameColored, "user-name-colored"),
78 FF_ARG(atColored, "at-symbol-colored"),
79 FF_ARG(hostNameColored, "host-name-colored"),
80 FF_ARG(instance.state.platform.fullUserName, "full-user-name"),
81#ifndef _WIN32
82 FF_ARG(instance.state.platform.uid, "user-id"),
83#else
84 FF_ARG(instance.state.platform.sid, "user-id"),
85#endif
86 FF_ARG(instance.state.platform.pid, "pid"),
87 FF_ARG(cwdTilde, "cwd"),
88 }));
89 }
90
91 return true;
92}
93
94void ffParseTitleJsonObject(FFTitleOptions* options, yyjson_val* module) {
95 yyjson_val *key, *val;
96 size_t idx, max;
97 yyjson_obj_foreach (module, idx, max, key, val) {
98 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
99 continue;
100 }
101
102 if (unsafe_yyjson_equals_str(key, "fqdn")) {
103 options->fqdn = yyjson_get_bool(val);
104 continue;
105 }
106
107 if (unsafe_yyjson_equals_str(key, "color")) {
108 if (!yyjson_is_obj(val)) {
109 continue;
110 }
111
112 yyjson_val* color = yyjson_obj_get(val, "user");
113 if (color) {
114 ffOptionParseColor(yyjson_get_str(color), &options->colorUser);
115 }
116 color = yyjson_obj_get(val, "at");
117 if (color) {
118 ffOptionParseColor(yyjson_get_str(color), &options->colorAt);
119 }
120 color = yyjson_obj_get(val, "host");
121 if (color) {
122 ffOptionParseColor(yyjson_get_str(color), &options->colorHost);
123 }
124 continue;
125 }
126
127 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Title), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
128 }
129}
130
131[[gnu::cold]]
132void ffGenerateTitleJsonConfig(FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
133 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
134
135 yyjson_mut_obj_add_bool(doc, module, "fqdn", options->fqdn);
136
137 yyjson_mut_val* color = yyjson_mut_obj_add_obj(doc, module, "color");
138 yyjson_mut_obj_add_strbuf(doc, color, "user", &options->colorUser);
139 yyjson_mut_obj_add_strbuf(doc, color, "at", &options->colorAt);
140 yyjson_mut_obj_add_strbuf(doc, color, "host", &options->colorHost);
141}
142
143bool ffGenerateTitleJsonResult([[maybe_unused]] FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
144 yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result");
145#ifdef _WIN32
146 yyjson_mut_obj_add_strbuf(doc, obj, "userId", &instance.state.platform.sid);
147#else
148 yyjson_mut_obj_add_uint(doc, obj, "userId", instance.state.platform.uid);
149#endif
150 yyjson_mut_obj_add_strbuf(doc, obj, "userName", &instance.state.platform.userName);
151 yyjson_mut_obj_add_strbuf(doc, obj, "fullUserName", &instance.state.platform.fullUserName);
152 yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &instance.state.platform.hostName);
153 yyjson_mut_obj_add_strbuf(doc, obj, "homeDir", &instance.state.platform.homeDir);
154 yyjson_mut_obj_add_strbuf(doc, obj, "exePath", &instance.state.platform.exePath);
155 yyjson_mut_obj_add_strbuf(doc, obj, "userShell", &instance.state.platform.userShell);
156 yyjson_mut_obj_add_uint(doc, obj, "pid", instance.state.platform.pid);
157 yyjson_mut_obj_add_strbuf(doc, obj, "cwd", &instance.state.platform.cwd);
158
159 return true;
160}
161
162void ffInitTitleOptions(FFTitleOptions* options) {
163 ffOptionInitModuleArg(&options->moduleArgs, "");
164 ffStrbufSetStatic(&options->moduleArgs.key, " ");
165
166 options->fqdn = false;
167 ffStrbufInit(&options->colorUser);
168 ffStrbufInit(&options->colorAt);
169 ffStrbufInit(&options->colorHost);
170}
171
172void ffDestroyTitleOptions(FFTitleOptions* options) {
173 ffOptionDestroyModuleArg(&options->moduleArgs);
174 ffStrbufDestroy(&options->colorUser);
175 ffStrbufDestroy(&options->colorAt);
176 ffStrbufDestroy(&options->colorHost);
177}
178
179FFModuleBaseInfo ffTitleModuleInfo = {
180 .name = "Title",
181 .description = "Print the title, including your username and hostname",
182 .displayName = {
183 .en = "Title",
184 .ar = "العنوان",
185 .cs = "Titulek",
186 .de = "Titel",
187 .es = "Título",
188 .fi = "Otsikko",
189 .fr = "Titre",
190 .gl = "Título",
191 .he = "כותרת",
192 .id = "Judul",
193 .it = "Titolo",
194 .ja = "タイトル",
195 .ko = "제목",
196 .pl = "Tytuł",
197 .pt = "Título",
198 .ru = "Заголовок",
199 .tr = "Başlık",
200 .uk = "Заголовок",
201 .vi = "Tiêu đề",
202 .zh_CN = "标题",
203 .zh_TW = "標題",
204 },
205 .initOptions = (void*) ffInitTitleOptions,
206 .destroyOptions = (void*) ffDestroyTitleOptions,
207 .parseJsonObject = (void*) ffParseTitleJsonObject,
208 .printModule = (void*) ffPrintTitle,
209 .generateJsonResult = (void*) ffGenerateTitleJsonResult,
210 .generateJsonConfig = (void*) ffGenerateTitleJsonConfig,
211 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
212 { "User name", "user-name" },
213 { "Host name", "host-name" },
214 { "Home directory", "home-dir" },
215 { "Executable path of current process", "exe-path" },
216 { "User's default shell", "user-shell" },
217 { "User name (colored)", "user-name-colored" },
218 { "@ symbol (colored)", "at-symbol-colored" },
219 { "Host name (colored)", "host-name-colored" },
220 { "Full user name", "full-user-name" },
221 { "UID (*nix) / SID (Windows)", "user-id" },
222 { "PID of current process", "pid" },
223 { "CWD with home dir replaced by `~`", "cwd" },
224 })),
225 .defaultOrder = 1,
226};