master ohmu/fastfetch-openbsd / src / modules / physicaldisk / physicaldisk.c
ohmu  ·  2026-10-15
  1#include "common/printing.h"
  2#include "common/jsonconfig.h"
  3#include "common/temps.h"
  4#include "common/size.h"
  5#include "detection/physicaldisk/physicaldisk.h"
  6#include "modules/physicaldisk/physicaldisk.h"
  7
  8static int sortDevices(const FFPhysicalDiskResult* left, const FFPhysicalDiskResult* right) {
  9    return ffStrbufComp(&left->name, &right->name);
 10}
 11
 12static void formatKey(const FFPhysicalDiskOptions* options, FFPhysicalDiskResult* dev, uint32_t index, FFstrbuf* key) {
 13    if (options->moduleArgs.key.length == 0) {
 14        ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), dev->name.length ? dev->name.chars : dev->devPath.chars);
 15    } else {
 16        ffStrbufClear(key);
 17        FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) {
 18                                                                          FF_ARG(index, "index"),
 19                                                                          FF_ARG(dev->name, "name"),
 20                                                                          FF_ARG(dev->devPath, "dev-path"),
 21                                                                          FF_ARG(options->moduleArgs.keyIcon, "icon"),
 22                                                                          FF_ARG(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), "module-name"),
 23                                                                      }));
 24    }
 25}
 26
 27bool ffPrintPhysicalDisk(FFPhysicalDiskOptions* options) {
 28    FF_LIST_AUTO_DESTROY result = ffListCreate();
 29    const char* error = ffDetectPhysicalDisk(&result, options);
 30
 31    if (error) {
 32        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
 33        return false;
 34    }
 35
 36    ffListSort(&result, sizeof(FFPhysicalDiskResult), (const void*) sortDevices);
 37
 38    uint32_t index = 0;
 39    FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
 40    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
 41
 42    FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
 43        formatKey(options, dev, result.length == 1 ? 0 : index + 1, &key);
 44        ffStrbufClear(&buffer);
 45        ffSizeAppendNum(dev->size, &buffer);
 46
 47        const char* physicalType = dev->type & FF_PHYSICALDISK_TYPE_VIRTUAL
 48            ? "Virtual"
 49            : dev->type & FF_PHYSICALDISK_TYPE_HDD
 50            ? "HDD"
 51            : dev->type & FF_PHYSICALDISK_TYPE_SSD
 52            ? "SSD"
 53            : "";
 54        const char* removableType = dev->type & FF_PHYSICALDISK_TYPE_REMOVABLE
 55            ? "Removable"
 56            : dev->type & FF_PHYSICALDISK_TYPE_FIXED
 57            ? "Fixed"
 58            : "";
 59        const char* readOnlyType = dev->type & FF_PHYSICALDISK_TYPE_READONLY
 60            ? "Read-only"
 61            : "";
 62
 63        if (options->moduleArgs.outputFormat.length == 0) {
 64            ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
 65
 66            if (physicalType[0] || removableType[0] || readOnlyType[0]) {
 67                ffStrbufAppendS(&buffer, " [");
 68                if (physicalType[0]) {
 69                    ffStrbufAppendS(&buffer, physicalType);
 70                }
 71                if (removableType[0]) {
 72                    if (buffer.chars[buffer.length - 1] != '[') {
 73                        ffStrbufAppendS(&buffer, ", ");
 74                    }
 75                    ffStrbufAppendS(&buffer, removableType);
 76                }
 77                if (readOnlyType[0]) {
 78                    if (buffer.chars[buffer.length - 1] != '[') {
 79                        ffStrbufAppendS(&buffer, ", ");
 80                    }
 81                    ffStrbufAppendS(&buffer, readOnlyType);
 82                }
 83                ffStrbufAppendC(&buffer, ']');
 84            }
 85
 86            if (dev->temperature != FF_PHYSICALDISK_TEMP_UNSET) {
 87                if (buffer.length > 0) {
 88                    ffStrbufAppendS(&buffer, " - ");
 89                }
 90
 91                ffTempsAppendNum(dev->temperature, &buffer, options->tempConfig, &options->moduleArgs);
 92            }
 93            ffStrbufPutTo(&buffer, stdout);
 94        } else {
 95            FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate();
 96            ffTempsAppendNum(dev->temperature, &tempStr, options->tempConfig, &options->moduleArgs);
 97            if (dev->type & FF_PHYSICALDISK_TYPE_READWRITE) {
 98                readOnlyType = "Read-write";
 99            }
100            FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
101                                                                                                         FF_ARG(buffer, "size"),
102                                                                                                         FF_ARG(dev->name, "name"),
103                                                                                                         FF_ARG(dev->interconnect, "interconnect"),
104                                                                                                         FF_ARG(dev->devPath, "dev-path"),
105                                                                                                         FF_ARG(dev->serial, "serial"),
106                                                                                                         FF_ARG(physicalType, "physical-type"),
107                                                                                                         FF_ARG(removableType, "removable-type"),
108                                                                                                         FF_ARG(readOnlyType, "readonly-type"),
109                                                                                                         FF_ARG(dev->revision, "revision"),
110                                                                                                         FF_ARG(tempStr, "temperature"),
111                                                                                                     }));
112        }
113        ++index;
114    }
115
116    FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
117        ffStrbufDestroy(&dev->name);
118        ffStrbufDestroy(&dev->interconnect);
119        ffStrbufDestroy(&dev->devPath);
120        ffStrbufDestroy(&dev->serial);
121        ffStrbufDestroy(&dev->revision);
122    }
123
124    return true;
125}
126
127void ffParsePhysicalDiskJsonObject(FFPhysicalDiskOptions* options, yyjson_val* module) {
128    yyjson_val *key, *val;
129    size_t idx, max;
130    yyjson_obj_foreach (module, idx, max, key, val) {
131        if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
132            continue;
133        }
134
135        if (unsafe_yyjson_equals_str(key, "namePrefix")) {
136            ffStrbufSetJsonVal(&options->namePrefix, val);
137            continue;
138        }
139
140        if (unsafe_yyjson_equals_str(key, "hideVirtual")) {
141            if (!yyjson_is_bool(val)) {
142                ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideVirtual must be a boolean");
143            } else {
144                if (unsafe_yyjson_is_true(val)) {
145                    options->hideType |= FF_PHYSICALDISK_TYPE_VIRTUAL;
146                } else {
147                    options->hideType &= ~FF_PHYSICALDISK_TYPE_VIRTUAL;
148                }
149            }
150            continue;
151        }
152
153        if (unsafe_yyjson_equals_str(key, "hideUnused")) {
154            if (!yyjson_is_bool(val)) {
155                ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideUnused must be a boolean");
156            } else {
157                if (unsafe_yyjson_is_true(val)) {
158                    options->hideType |= FF_PHYSICALDISK_TYPE_UNUSED;
159                } else {
160                    options->hideType &= ~FF_PHYSICALDISK_TYPE_UNUSED;
161                }
162            }
163            continue;
164        }
165
166        if (ffTempsParseJsonObject(key, val, &options->temp, &options->tempConfig)) {
167            continue;
168        }
169
170        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
171    }
172}
173
174[[gnu::cold]]
175void ffGeneratePhysicalDiskJsonConfig(FFPhysicalDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
176    ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
177
178    yyjson_mut_obj_add_strbuf(doc, module, "namePrefix", &options->namePrefix);
179
180    ffTempsGenerateJsonConfig(doc, module, options->temp, options->tempConfig);
181
182    yyjson_mut_obj_add_bool(doc, module, "hideVirtual", !!(options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL));
183    yyjson_mut_obj_add_bool(doc, module, "hideUnused", !!(options->hideType & FF_PHYSICALDISK_TYPE_UNUSED));
184}
185
186bool ffGeneratePhysicalDiskJsonResult(FFPhysicalDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
187    FF_LIST_AUTO_DESTROY result = ffListCreate();
188    const char* error = ffDetectPhysicalDisk(&result, options);
189
190    if (error) {
191        yyjson_mut_obj_add_str(doc, module, "error", error);
192        return false;
193    }
194
195    yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
196    FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
197        yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
198        yyjson_mut_obj_add_strbuf(doc, obj, "name", &dev->name);
199        yyjson_mut_obj_add_strbuf(doc, obj, "devPath", &dev->devPath);
200        yyjson_mut_obj_add_strbuf(doc, obj, "interconnect", &dev->interconnect);
201
202        if (dev->type & FF_PHYSICALDISK_TYPE_VIRTUAL) {
203            yyjson_mut_obj_add_str(doc, obj, "kind", "Virtual");
204        } else if (dev->type & FF_PHYSICALDISK_TYPE_HDD) {
205            yyjson_mut_obj_add_str(doc, obj, "kind", "HDD");
206        } else if (dev->type & FF_PHYSICALDISK_TYPE_SSD) {
207            yyjson_mut_obj_add_str(doc, obj, "kind", "SSD");
208        } else {
209            yyjson_mut_obj_add_null(doc, obj, "kind");
210        }
211
212        yyjson_mut_obj_add_uint(doc, obj, "size", dev->size);
213        yyjson_mut_obj_add_strbuf(doc, obj, "serial", &dev->serial);
214
215        if (dev->type & FF_PHYSICALDISK_TYPE_REMOVABLE) {
216            yyjson_mut_obj_add_bool(doc, obj, "removable", true);
217        } else if (dev->type & FF_PHYSICALDISK_TYPE_FIXED) {
218            yyjson_mut_obj_add_bool(doc, obj, "removable", false);
219        } else {
220            yyjson_mut_obj_add_null(doc, obj, "removable");
221        }
222
223        if (dev->type & FF_PHYSICALDISK_TYPE_READONLY) {
224            yyjson_mut_obj_add_bool(doc, obj, "readOnly", true);
225        } else if (dev->type & FF_PHYSICALDISK_TYPE_READWRITE) {
226            yyjson_mut_obj_add_bool(doc, obj, "readOnly", false);
227        } else {
228            yyjson_mut_obj_add_null(doc, obj, "readOnly");
229        }
230
231        yyjson_mut_obj_add_bool(doc, obj, "unknown", !!(dev->type & FF_PHYSICALDISK_TYPE_UNUSED));
232
233        yyjson_mut_obj_add_strbuf(doc, obj, "revision", &dev->revision);
234
235        if (dev->temperature != FF_PHYSICALDISK_TEMP_UNSET) {
236            yyjson_mut_obj_add_real(doc, obj, "temperature", dev->temperature);
237        } else {
238            yyjson_mut_obj_add_null(doc, obj, "temperature");
239        }
240    }
241
242    FF_LIST_FOR_EACH (FFPhysicalDiskResult, dev, result) {
243        ffStrbufDestroy(&dev->name);
244        ffStrbufDestroy(&dev->interconnect);
245        ffStrbufDestroy(&dev->devPath);
246        ffStrbufDestroy(&dev->serial);
247        ffStrbufDestroy(&dev->revision);
248    }
249
250    return true;
251}
252
253void ffInitPhysicalDiskOptions(FFPhysicalDiskOptions* options) {
254    ffOptionInitModuleArg(&options->moduleArgs, "󰋊");
255
256    ffStrbufInit(&options->namePrefix);
257    options->temp = false;
258    options->tempConfig = (FFColorRangeConfig) { 50, 70 };
259    options->hideType = FF_PHYSICALDISK_TYPE_UNUSED;
260}
261
262void ffDestroyPhysicalDiskOptions(FFPhysicalDiskOptions* options) {
263    ffOptionDestroyModuleArg(&options->moduleArgs);
264    ffStrbufDestroy(&options->namePrefix);
265}
266
267FFModuleBaseInfo ffPhysicalDiskModuleInfo = {
268    .name = "PhysicalDisk",
269    .description = "Print physical disk information",
270    .displayName = {
271        .en = "Physical Disk",
272        .ar = "القرص الفعلي",
273        .cs = "Fyzický disk",
274        .de = "Physische Festplatte",
275        .es = "Disco físico",
276        .fi = "Fyysinen Levy",
277        .fr = "Disque physique",
278        .gl = "Disco físico",
279        .he = "דיסק פיזי",
280        .id = "Disk Fisik",
281        .it = "Disco fisico",
282        .ja = "物理ディスク",
283        .ko = "물리 디스크",
284        .pl = "Dysk fizyczny",
285        .pt = "Disco físico",
286        .ru = "Физический диск",
287        .tr = "Fiziksel Disk",
288        .uk = "Фізичний диск",
289        .vi = "Ổ đĩa vật lý",
290        .zh_CN = "物理磁盘",
291        .zh_TW = "物理磁碟",
292    },
293    .initOptions = (void*) ffInitPhysicalDiskOptions,
294    .destroyOptions = (void*) ffDestroyPhysicalDiskOptions,
295    .parseJsonObject = (void*) ffParsePhysicalDiskJsonObject,
296    .printModule = (void*) ffPrintPhysicalDisk,
297    .generateJsonResult = (void*) ffGeneratePhysicalDiskJsonResult,
298    .generateJsonConfig = (void*) ffGeneratePhysicalDiskJsonConfig,
299    .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
300        { "Device size (formatted)", "size" },
301        { "Device name *", "name" },
302        { "Device interconnect type", "interconnect" },
303        { "Device raw file path *", "dev-path" },
304        { "Serial number", "serial" },
305        { "Device kind (SSD or HDD)", "physical-type" },
306        { "Device kind (Removable or Fixed)", "removable-type" },
307        { "Device kind (Read-only or Read-write)", "readonly-type" },
308        { "Product revision", "revision" },
309        { "Device temperature (formatted)", "temperature" },
310    })),
311    .defaultOrder = 69,
312};