master ohmu/fastfetch-openbsd / src / options / general.c
ohmu  ·  2026-10-15
  1#include "fastfetch.h"
  2#include "common/jsonconfig.h"
  3#include "common/processing.h"
  4#include "common/strutil.h"
  5#include "common/lua.h"
  6#include "options/general.h"
  7
  8#include <unistd.h>
  9
 10const char* ffOptionsParseGeneralJsonConfig(FFOptionsGeneral* options, yyjson_val* root, yyjson_val** pkey) {
 11    yyjson_val* object = yyjson_obj_get(root, "general");
 12    if (!object) {
 13        return nullptr;
 14    }
 15    if (!yyjson_is_obj(object)) {
 16        return "Property 'general' must be an object";
 17    }
 18
 19    yyjson_val *key, *val;
 20    size_t idx, max;
 21    yyjson_obj_foreach (object, idx, max, key, val) {
 22        *pkey = key;
 23        if (unsafe_yyjson_equals_str(key, "thread")) {
 24            options->multithreading = yyjson_get_bool(val);
 25        } else if (unsafe_yyjson_equals_str(key, "processingTimeout")) {
 26            options->processingTimeout = (int32_t) yyjson_get_int(val);
 27        } else if (unsafe_yyjson_equals_str(key, "preRun")) {
 28            return "general.preRun is removed due to security concerns.";
 29        } else if (unsafe_yyjson_equals_str(key, "detectVersion")) {
 30            options->detectVersion = yyjson_get_bool(val);
 31        } else if (unsafe_yyjson_equals_str(key, "playerName")) {
 32            ffStrbufSetJsonVal(&options->playerName, val);
 33        } else if (unsafe_yyjson_equals_str(key, "preload")) {
 34            if (!yyjson_is_obj(val)) {
 35                return "Property 'general.preload' must be an object";
 36            }
 37
 38            yyjson_val* script = yyjson_obj_get(val, "lua");
 39            if (script) {
 40                if (!yyjson_is_str(script)) {
 41                    return "Property 'general.preload.lua' must be a string";
 42                }
 43
 44#if FF_HAVE_LUA
 45                const char* error = ffLuaLoadState();
 46                if (error) {
 47                    return error;
 48                }
 49
 50                lua_State* L = luaData.L;
 51                lua_settop(L, 0);
 52                if (luaL_loadbuffer(L, unsafe_yyjson_get_str(script), unsafe_yyjson_get_len(script), "@general.preload.lua") != LUA_OK ||
 53                    lua_pcall(L, 0, 0, 0) != LUA_OK) {
 54                    // The position Lua puts in front of a message is kept here: this is printed once, so naming the
 55                    // line of the preload script that raised it is useful. `lua_tolstring` only converts strings and
 56                    // numbers, so an error value of any other type leaves nothing to report.
 57                    return lua_tolstring(L, -1, nullptr) ?: "the script raised an error that is not a string";
 58                }
 59                lua_settop(L, 0);
 60
 61#else
 62                return "Property 'general.preload.lua' requires a build with Lua support";
 63#endif
 64            }
 65        }
 66#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
 67        else if (unsafe_yyjson_equals_str(key, "dsForceDrm")) {
 68            if (yyjson_is_str(val)) {
 69                int value;
 70                const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
 71                                                                           { "sysfs-only", FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY },
 72                                                                           { "false", FF_DS_FORCE_DRM_TYPE_FALSE },
 73                                                                           { "true", FF_DS_FORCE_DRM_TYPE_TRUE },
 74                                                                           {},
 75                                                                       });
 76                if (error) {
 77                    return "Invalid enum value of `dsForceDrm`";
 78                } else {
 79                    options->dsForceDrm = (FFDsForceDrmType) value;
 80                }
 81            } else {
 82                options->dsForceDrm = yyjson_get_bool(val) ? FF_DS_FORCE_DRM_TYPE_TRUE : FF_DS_FORCE_DRM_TYPE_FALSE;
 83            }
 84        }
 85#endif
 86        else {
 87            return "Unknown general property";
 88        }
 89    }
 90
 91    return nullptr;
 92}
 93
 94bool ffOptionsParseGeneralCommandLine(FFOptionsGeneral* options, const char* key, const char* value) {
 95    if (ffStrEqualsIgnCase(key, "--thread") || ffStrEqualsIgnCase(key, "--multithreading")) {
 96        options->multithreading = ffOptionParseBoolean(value);
 97    } else if (ffStrEqualsIgnCase(key, "--processing-timeout")) {
 98        options->processingTimeout = ffOptionParseInt32(key, value);
 99    } else if (ffStrEqualsIgnCase(key, "--detect-version")) {
100        options->detectVersion = ffOptionParseBoolean(value);
101    } else if (ffStrEqualsIgnCase(key, "--player-name")) {
102        ffOptionParseString(key, value, &options->playerName);
103    }
104#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
105    else if (ffStrEqualsIgnCase(key, "--ds-force-drm")) {
106        if (ffOptionParseBoolean(value)) {
107            options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_TRUE;
108        } else if (ffStrEqualsIgnCase(value, "sysfs-only")) {
109            options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY;
110        } else {
111            options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_FALSE;
112        }
113    }
114#endif
115    else {
116        return false;
117    }
118
119    return true;
120}
121
122void ffOptionsInitGeneral(FFOptionsGeneral* options) {
123    options->processingTimeout = 5000;
124    options->multithreading = true;
125    options->detectVersion = true;
126    ffStrbufInit(&options->playerName);
127
128#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
129    options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_FALSE;
130#endif
131}
132
133void ffOptionsDestroyGeneral(FFOptionsGeneral* options) {
134    ffStrbufDestroy(&options->playerName);
135}
136
137[[gnu::cold]]
138void ffOptionsGenerateGeneralJsonConfig(FFdata* data, FFOptionsGeneral* options) {
139    yyjson_mut_doc* doc = data->resultDoc;
140    yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, doc->root, "general");
141
142    yyjson_mut_obj_add_bool(doc, obj, "thread", options->multithreading);
143
144    yyjson_mut_obj_add_int(doc, obj, "processingTimeout", options->processingTimeout);
145
146    yyjson_mut_obj_add_bool(doc, obj, "detectVersion", options->detectVersion);
147
148    yyjson_mut_obj_add_strbuf(doc, obj, "playerName", &options->playerName);
149
150#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
151
152    switch (options->dsForceDrm) {
153        case FF_DS_FORCE_DRM_TYPE_FALSE:
154            yyjson_mut_obj_add_bool(doc, obj, "dsForceDrm", false);
155            break;
156        case FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY:
157            yyjson_mut_obj_add_str(doc, obj, "dsForceDrm", "sysfs-only");
158            break;
159        case FF_DS_FORCE_DRM_TYPE_TRUE:
160            yyjson_mut_obj_add_bool(doc, obj, "dsForceDrm", true);
161            break;
162    }
163#endif
164}