ohmu
·
2026-10-15
1#include "detection/command/command.h"
2#include "common/processing.h"
3#include "common/FFstrbuf.h"
4
5typedef struct FFCommandResultBundle {
6 FFProcessHandle handle;
7 const char* error;
8} FFCommandResultBundle;
9
10// FIFO, non-thread-safe list of running commands
11static FFlist commandQueue;
12
13static const char* spawnProcess(FFCommandOptions* options, FFProcessHandle* handle) {
14 if (options->text.length == 0) {
15 return "No command text specified";
16 }
17
18#if _WIN32
19 if (ffStrbufEqualS(&options->shell, "cmd.exe") && ffStrbufEqualS(&options->param, "/d/c")) {
20 ffStrbufAppendS(&options->text, " & exit %^errorlevel%"); // Necessary to propagate 9009 from CMD
21 }
22#endif
23
24 return ffProcessSpawn(options->param.length ? (char* const[]) {
25 options->shell.chars,
26 options->param.chars,
27 options->text.chars,
28 nullptr }
29 : (char* const[]) { options->shell.chars, options->text.chars, nullptr },
30 options->useOutput,
31 FF_PROCESS_INHERIT_STDIN,
32 handle);
33}
34
35bool ffPrepareCommand(FFCommandOptions* options) {
36 if (!options->parallel) {
37 return false;
38 }
39
40 FFCommandResultBundle* bundle = FF_LIST_ADD(FFCommandResultBundle, commandQueue);
41 bundle->error = spawnProcess(options, &bundle->handle);
42
43 return true;
44}
45
46const char* ffDetectCommand(FFCommandOptions* options, FFstrbuf* result) {
47 FFCommandResultBundle bundle = {};
48 if (!options->parallel) {
49 bundle.error = spawnProcess(options, &bundle.handle);
50 } else if (!FF_LIST_SHIFT(commandQueue, &bundle)) {
51 // The module was printed without having been prepared first, so there is no process to collect.
52 // That is the expected outcome for a module gated on `condition.succeeded`: whether it is
53 // printed at all is only known in the print pass, so the prepare pass skips it (see
54 // `printJsonConfig`). In every other case the queue and the printed modules must line up.
55 return "Module was not prepared, so it cannot run in parallel. Modules gated on "
56 "`condition.succeeded` are never prepared, which is the expected cause; "
57 "anything else is a bug in fastfetch, please report it";
58 }
59
60 if (bundle.error) {
61 return bundle.error;
62 }
63
64 bundle.error = ffProcessReadOutput(&bundle.handle, result);
65 if (bundle.error) {
66 return bundle.error;
67 }
68
69 ffStrbufTrimRightSpace(result);
70 return nullptr;
71}