ohmu
·
2026-10-15
1#include "common/printing.h"
2#include "common/jsonconfig.h"
3#include "common/strutil.h"
4#include "detection/libc/libc.h"
5#include "detection/editor/editor.h"
6#include "modules/editor/editor.h"
7
8// `FFEditorResult` owns four buffers and has no destructor of its own. Both entry points below
9// have to free them on the error path too — which used to be skipped, and which became the common
10// path once `ffDetectEditor()` started reporting an `$EDITOR` that does not name an executable
11// instead of quietly succeeding.
12static void destroyEditorResult(FFEditorResult* result) {
13 ffStrbufDestroy(&result->name);
14 ffStrbufDestroy(&result->path);
15 ffStrbufDestroy(&result->exe);
16 ffStrbufDestroy(&result->version);
17}
18
19bool ffPrintEditor(FFEditorOptions* options) {
20 FFEditorResult result = {
21 .type = "Unknown",
22 .name = ffStrbufCreate(),
23 .path = ffStrbufCreate(),
24 .exe = ffStrbufCreate(),
25 .version = ffStrbufCreate(),
26 };
27 const char* error = ffDetectEditor(&result);
28
29 if (error) {
30 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
31 destroyEditorResult(&result);
32 return false;
33 }
34
35 if (options->moduleArgs.outputFormat.length == 0) {
36 ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
37 if (result.exe.length) {
38 ffStrbufWriteTo(&result.exe, stdout);
39 if (result.version.length) {
40 printf(" %s", result.version.chars);
41 }
42 } else {
43 ffStrbufWriteTo(&result.name, stdout);
44 }
45 putchar('\n');
46 } else {
47 FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
48 FF_ARG(result.type, "type"),
49 FF_ARG(result.name, "name"),
50 FF_ARG(result.exe, "exe-name"),
51 FF_ARG(result.path, "path"),
52 FF_ARG(result.version, "version"),
53 }));
54 }
55
56 destroyEditorResult(&result);
57
58 return true;
59}
60
61void ffParseEditorJsonObject(FFEditorOptions* options, yyjson_val* module) {
62 yyjson_val *key, *val;
63 size_t idx, max;
64 yyjson_obj_foreach (module, idx, max, key, val) {
65 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
66 continue;
67 }
68
69 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
70 }
71}
72
73[[gnu::cold]]
74void ffGenerateEditorJsonConfig(FFEditorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
75 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
76}
77
78bool ffGenerateEditorJsonResult([[maybe_unused]] FFEditorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
79 FFEditorResult result = {
80 .name = ffStrbufCreate(),
81 .path = ffStrbufCreate(),
82 .version = ffStrbufCreate(),
83 };
84
85 const char* error = ffDetectEditor(&result);
86
87 if (error) {
88 yyjson_mut_obj_add_str(doc, module, "error", error);
89 destroyEditorResult(&result);
90 return false;
91 }
92
93 yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result");
94 yyjson_mut_obj_add_str(doc, obj, "type", result.type);
95 yyjson_mut_obj_add_strbuf(doc, obj, "name", &result.name);
96 yyjson_mut_obj_add_strbuf(doc, obj, "path", &result.path);
97 yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result.exe);
98 yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version);
99
100 destroyEditorResult(&result);
101
102 return true;
103}
104
105void ffInitEditorOptions(FFEditorOptions* options) {
106 ffOptionInitModuleArg(&options->moduleArgs, "");
107}
108
109void ffDestroyEditorOptions(FFEditorOptions* options) {
110 ffOptionDestroyModuleArg(&options->moduleArgs);
111}
112
113FFModuleBaseInfo ffEditorModuleInfo = {
114 .name = "Editor",
115 .description = "Print information about the default editor ($VISUAL or $EDITOR)",
116 .displayName = {
117 .en = "Editor",
118 .ar = "المحرر",
119 .cs = "Editor",
120 .de = "Editor",
121 .es = "Editor",
122 .fi = "Editori",
123 .fr = "Éditeur",
124 .gl = "Editor",
125 .he = "עורך",
126 .id = "Editor",
127 .it = "Editor",
128 .ja = "エディター",
129 .ko = "편집기",
130 .pl = "Edytor",
131 .pt = "Editor",
132 .ru = "Редактор",
133 .tr = "Düzenleyici",
134 .uk = "Редактор",
135 .vi = "Trình soạn thảo",
136 .zh_CN = "编辑器",
137 .zh_TW = "編輯器",
138 },
139 .initOptions = (void*) ffInitEditorOptions,
140 .destroyOptions = (void*) ffDestroyEditorOptions,
141 .parseJsonObject = (void*) ffParseEditorJsonObject,
142 .printModule = (void*) ffPrintEditor,
143 .generateJsonResult = (void*) ffGenerateEditorJsonResult,
144 .generateJsonConfig = (void*) ffGenerateEditorJsonConfig,
145 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
146 { "Type (Visual / Editor)", "type" },
147 { "Name", "name" },
148 { "Exe name of real path", "exe-name" },
149 { "Full path of real path", "path" },
150 { "Version", "version" },
151 })),
152 .defaultOrder = 16,
153};