master ohmu/fastfetch-openbsd / src / modules / title / title.c
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};