From d387800bda545dedbd6e70da13677ca10f6e3fbd Mon Sep 17 00:00:00 2001 From: Linus Dierheimer Date: Sun, 4 Sep 2022 12:43:30 +0200 Subject: [PATCH] Separate host detection and printing logic --- CMakeLists.txt | 22 +++- src/common/settings.c | 5 +- src/common/settings.h | 2 +- src/detection/host/host.c | 25 ++++ src/detection/host/host.h | 31 +++++ src/detection/host/host_android.c | 45 +++++++ src/detection/host/host_linux.c | 118 ++++++++++++++++++ src/logo/builtin.c | 2 +- src/modules/gpu.c | 8 +- src/modules/host.c | 197 ++++-------------------------- src/util/FFstrbuf.c | 13 ++ src/util/FFstrbuf.h | 3 + tests/strbuf.c | 22 ++++ 13 files changed, 312 insertions(+), 181 deletions(-) create mode 100644 src/detection/host/host.c create mode 100644 src/detection/host/host.h create mode 100644 src/detection/host/host_android.c create mode 100644 src/detection/host/host_linux.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fbe94e2ba..6bb5bdb41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,7 @@ configure_file(src/fastfetch_config.h.in fastfetch_config.h) # libfastfetch target # ####################### -add_library(libfastfetch OBJECT +set(LIBFASTFETCH_SRC src/util/FFstrbuf.c src/util/FFlist.c src/util/FFvaluestore.c @@ -163,6 +163,7 @@ add_library(libfastfetch OBJECT src/detection/datetime.c src/detection/temps.c src/detection/title.c + src/detection/host/host.c src/detection/displayserver/displayServer.c src/detection/displayserver/wayland.c src/detection/displayserver/xcb.c @@ -209,6 +210,25 @@ add_library(libfastfetch OBJECT src/modules/opencl.c ) +if(APPLE) + list(APPEND LIBFASTFETCH_SRC + ) +elseif(ANDROID) + list(APPEND LIBFASTFETCH_SRC + "src/detection/host/host_android.c" + ) +elseif(UNIX) + list(APPEND LIBFASTFETCH_SRC + "src/detection/host/host_linux.c" + ) +else() + message(FATAL_ERROR "Unsupported platform") +endif() + +add_library(libfastfetch OBJECT + ${LIBFASTFETCH_SRC} +) + if(ENABLE_LIBPCI) pkg_check_modules(LIBPCI libpci) if(LIBPCI_FOUND) diff --git a/src/common/settings.c b/src/common/settings.c index 36addafea..8f44aaf7d 100644 --- a/src/common/settings.c +++ b/src/common/settings.c @@ -352,10 +352,9 @@ int ffSettingsGetSQLite3Int(const FFinstance* instance, const char* dbPath, cons #ifdef __ANDROID__ #include -bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result) { - uint32_t originalLength = result->length; +void ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result) { ffStrbufEnsureFree(result, PROP_VALUE_MAX); result->length += (uint32_t) __system_property_get(propName, result->chars + result->length); - return result->length > originalLength; + result->chars[result->length] = '\0'; } #endif diff --git a/src/common/settings.h b/src/common/settings.h index 8980020af..8f487eabb 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -33,7 +33,7 @@ FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, con int ffSettingsGetSQLite3Int(const FFinstance* instance, const char* dbPath, const char* query); #ifdef __ANDROID__ -bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result); +void ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result); #endif #endif diff --git a/src/detection/host/host.c b/src/detection/host/host.c new file mode 100644 index 000000000..cc8038d8a --- /dev/null +++ b/src/detection/host/host.c @@ -0,0 +1,25 @@ +#include "host.h" + +#include "pthread.h" + +void ffDetectHostImpl(FFHostResult* host); + +const FFHostResult* ffDetectHost() +{ + static FFHostResult host; + + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static bool init = false; + pthread_mutex_lock(&mutex); + if(init) + { + pthread_mutex_unlock(&mutex); + return &host; + } + init = true; + + ffDetectHostImpl(&host); + + pthread_mutex_unlock(&mutex); + return &host; +} diff --git a/src/detection/host/host.h b/src/detection/host/host.h new file mode 100644 index 000000000..4352cf766 --- /dev/null +++ b/src/detection/host/host.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef FF_INCLUDED_DETECTION_HOST_host +#define FF_INCLUDED_DETECTION_HOST_host + +#include "fastfetch.h" + +#define FF_HOST_PRODUCT_NAME_WSL "Windows Subsystem for Linux" + +typedef struct FFHostResult +{ + FFstrbuf productFamily; + FFstrbuf productName; + FFstrbuf productVersion; + FFstrbuf productSku; + FFstrbuf biosDate; + FFstrbuf biosRelease; + FFstrbuf biosVendor; + FFstrbuf biosVersion; + FFstrbuf boardName; + FFstrbuf boardVendor; + FFstrbuf boardVersion; + FFstrbuf chassisType; + FFstrbuf chassisVendor; + FFstrbuf chassisVersion; + FFstrbuf sysVendor; +} FFHostResult; + +const FFHostResult* ffDetectHost(); + +#endif diff --git a/src/detection/host/host_android.c b/src/detection/host/host_android.c new file mode 100644 index 000000000..c18a55bd9 --- /dev/null +++ b/src/detection/host/host_android.c @@ -0,0 +1,45 @@ +#include "host.h" + +#include + +void ffDetectHostImpl(FFHostResult* host) +{ + //Family + + ffStrbufInit(&host->productFamily); + ffSettingsGetAndroidProperty("ro.product.device", &host->productFamily); + + //Name + + ffStrbufInit(&host->productName); + + ffSettingsGetAndroidProperty("ro.product.brand", &host->productName); + if(host->productName.length > 0){ + host->productName.chars[0] = (char) toupper(host->productName.chars[0]); + ffStrbufAppendC(&host->productName, ' '); + } + + ffSettingsGetAndroidProperty("ro.product.model", &host->productName); + + ffStrbufTrimRight(&host->productName, ' '); + + //Sys vendor + + ffStrbufInit(&host->sysVendor); + ffSettingsGetAndroidProperty("ro.product.manufacturer", &host->sysVendor); + + //Not implemented + + ffStrbufInitA(&host->productVersion, 0); + ffStrbufInitA(&host->productSku, 0); + ffStrbufInitA(&host->biosDate, 0); + ffStrbufInitA(&host->biosRelease, 0); + ffStrbufInitA(&host->biosVendor, 0); + ffStrbufInitA(&host->biosVersion, 0); + ffStrbufInitA(&host->boardName, 0); + ffStrbufInitA(&host->boardVendor, 0); + ffStrbufInitA(&host->boardVersion, 0); + ffStrbufInitA(&host->chassisType, 0); + ffStrbufInitA(&host->chassisVendor, 0); + ffStrbufInitA(&host->chassisVersion, 0); +} diff --git a/src/detection/host/host_linux.c b/src/detection/host/host_linux.c new file mode 100644 index 000000000..80e048ae7 --- /dev/null +++ b/src/detection/host/host_linux.c @@ -0,0 +1,118 @@ +#include "host.h" +#include "common/io.h" + +#include + +static bool hostValueSet(FFstrbuf* value) +{ + return + value->length > 0 && + ffStrbufStartsWithIgnCaseS(value, "To be filled") != true && + ffStrbufStartsWithIgnCaseS(value, "To be set") != true && + ffStrbufStartsWithIgnCaseS(value, "OEM") != true && + ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true && + ffStrbufIgnCaseCompS(value, "None") != 0 && + ffStrbufIgnCaseCompS(value, "System Product") != 0 && + ffStrbufIgnCaseCompS(value, "System Product Name") != 0 && + ffStrbufIgnCaseCompS(value, "System Product Version") != 0 && + ffStrbufIgnCaseCompS(value, "System Name") != 0 && + ffStrbufIgnCaseCompS(value, "System Version") != 0 && + ffStrbufIgnCaseCompS(value, "Default string") != 0 && + ffStrbufIgnCaseCompS(value, "Undefined") != 0 && + ffStrbufIgnCaseCompS(value, "Not Specified") != 0 && + ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 && + ffStrbufIgnCaseCompS(value, "INVALID") != 0 && + ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 && + ffStrbufIgnCaseCompS(value, "All Series") != 0 + ; +} + +static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) +{ + ffReadFileBuffer(devicesPath, buffer); + if(hostValueSet(buffer)) + return; + + ffReadFileBuffer(classPath, buffer); + if(hostValueSet(buffer)) + return; + + ffStrbufClear(buffer); +} + +static void getHostProductName(FFstrbuf* name) +{ + getHostValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", name); + if(name->length > 0) + return; + + ffReadFileBuffer("/sys/firmware/devicetree/base/model", name); + if(hostValueSet(name)) + return; + + //does a clear before the read + ffReadFileBuffer("/tmp/sysinfo/model", name); + if(hostValueSet(name)) + return; + + ffStrbufClear(name); +} + +void ffDetectHostImpl(FFHostResult* host) +{ + ffStrbufInit(&host->productFamily); + getHostValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->productFamily); + + ffStrbufInit(&host->productName); + getHostProductName(&host->productName); + + ffStrbufInit(&host->productVersion); + getHostValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->productVersion); + + ffStrbufInit(&host->productSku); + getHostValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->productSku); + + ffStrbufInit(&host->biosDate); + getHostValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &host->biosDate); + + ffStrbufInit(&host->biosRelease); + getHostValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &host->biosRelease); + + ffStrbufInit(&host->biosVendor); + getHostValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &host->biosVendor); + + ffStrbufInit(&host->biosVersion); + getHostValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &host->biosVersion); + + ffStrbufInit(&host->boardName); + getHostValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &host->boardName); + + ffStrbufInit(&host->boardVendor); + getHostValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &host->boardVendor); + + ffStrbufInit(&host->boardVersion); + getHostValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &host->boardVersion); + + ffStrbufInit(&host->chassisType); + getHostValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &host->chassisType); + + ffStrbufInit(&host->chassisVendor); + getHostValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &host->chassisVendor); + + ffStrbufInit(&host->chassisVersion); + getHostValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &host->chassisVersion); + + ffStrbufInit(&host->sysVendor); + getHostValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->sysVendor); + + //KVM/Qemu virtual machine + if(ffStrbufStartsWithS(&host->productName, "Standard PC")) + ffStrbufPrependS(&host->productName, "KVM/QEMU "); + + //On WSL, the real host can't be detected. Instead use WSL as host. + if(host->productFamily.length == 0 && host->productName.length == 0 && ( + getenv("WSLENV") != NULL || + getenv("WSL_DISTRO") != NULL || + getenv("WSL_INTEROP") != NULL + )) ffStrbufAppendS(&host->productName, FF_HOST_PRODUCT_NAME_WSL); +} diff --git a/src/logo/builtin.c b/src/logo/builtin.c index baf45a76a..8879374b6 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -37,7 +37,7 @@ const FFlogo* ffLogoBuiltinGetUnknown() static const FFlogo* getLogoNone() { FF_LOGO_INIT - FF_LOGO_NAMES("none", "empty", "") + FF_LOGO_NAMES("none", "empty") FF_LOGO_LINES("") FF_LOGO_COLORS("") FF_LOGO_RETURN diff --git a/src/modules/gpu.c b/src/modules/gpu.c index a71f8fa0a..a6e087bf1 100644 --- a/src/modules/gpu.c +++ b/src/modules/gpu.c @@ -5,6 +5,7 @@ #include "common/properties.h" #include "detection/temps.h" #include "detection/vulkan.h" +#include "detection/host/host.h" #include @@ -266,11 +267,8 @@ void ffPrintGPU(FFinstance* instance) if(ffPrintFromCache(instance, FF_GPU_MODULE_NAME, &instance->config.gpu, FF_GPU_NUM_FORMAT_ARGS)) return; - if( - getenv("WSLENV") != NULL || - getenv("WSL_DISTRO") != NULL || - getenv("WSL_INTEROP") != NULL - ) { + if(ffStrbufCompS(&ffDetectHost()->productName, FF_HOST_PRODUCT_NAME_WSL) == 0) + { ffPrintError(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpu, "WSL doesn't expose senseful GPU names"); return; } diff --git a/src/modules/host.c b/src/modules/host.c index 2be78d988..d40bb54b4 100644 --- a/src/modules/host.c +++ b/src/modules/host.c @@ -1,196 +1,53 @@ #include "fastfetch.h" #include "common/printing.h" #include "common/caching.h" - -#include +#include "detection/host/host.h" #define FF_HOST_MODULE_NAME "Host" #define FF_HOST_NUM_FORMAT_ARGS 15 -#ifndef __ANDROID__ - #include "common/io.h" -#else - #include -#endif - -#ifndef __ANDROID__ -static bool hostValueSet(FFstrbuf* value) -{ - ffStrbufTrimRight(value, '\n'); - ffStrbufTrim(value, ' '); - - return - value->length > 0 && - ffStrbufStartsWithIgnCaseS(value, "To be filled") != true && - ffStrbufStartsWithIgnCaseS(value, "To be set") != true && - ffStrbufStartsWithIgnCaseS(value, "OEM") != true && - ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true && - ffStrbufIgnCaseCompS(value, "None") != 0 && - ffStrbufIgnCaseCompS(value, "System Product") != 0 && - ffStrbufIgnCaseCompS(value, "System Product Name") != 0 && - ffStrbufIgnCaseCompS(value, "System Product Version") != 0 && - ffStrbufIgnCaseCompS(value, "System Name") != 0 && - ffStrbufIgnCaseCompS(value, "System Version") != 0 && - ffStrbufIgnCaseCompS(value, "Default string") != 0 && - ffStrbufIgnCaseCompS(value, "Undefined") != 0 && - ffStrbufIgnCaseCompS(value, "Not Specified") != 0 && - ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 && - ffStrbufIgnCaseCompS(value, "INVALID") != 0 && - ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 && - ffStrbufIgnCaseCompS(value, "All Series") != 0 - ; -} - -static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) -{ - ffReadFileBuffer(devicesPath, buffer); - - if(buffer->length == 0) - ffReadFileBuffer(classPath, buffer); -} -#endif - void ffPrintHost(FFinstance* instance) { if(ffPrintFromCache(instance, FF_HOST_MODULE_NAME, &instance->config.host, FF_HOST_NUM_FORMAT_ARGS)) return; - FFstrbuf product_family; - ffStrbufInit(&product_family); - #ifndef __ANDROID__ - getHostValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &product_family); - if(!hostValueSet(&product_family)) - ffStrbufClear(&product_family); - #else - ffSettingsGetAndroidProperty("ro.product.device", &product_family); - #endif + const FFHostResult* host = ffDetectHost(); - FFstrbuf product_name; - ffStrbufInit(&product_name); - #ifndef __ANDROID__ - getHostValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &product_name); - - if(product_name.length == 0) - ffReadFileBuffer("/sys/firmware/devicetree/base/model", &product_name); - - if(product_name.length == 0) - ffReadFileBuffer("/tmp/sysinfo/model", &product_name); - - if(ffStrbufStartsWithS(&product_name, "Standard PC")) - { - FFstrbuf copy; - ffStrbufInitCopy(©, &product_name); - ffStrbufSetS(&product_name, "KVM/QEMU "); - ffStrbufAppend(&product_name, ©); - ffStrbufDestroy(©); - } - - if(!hostValueSet(&product_name)) - ffStrbufClear(&product_name); - - //On WSL, the real host can't be detected. Instead use WSL as host. - if(product_name.length == 0 && product_family.length == 0 && ( - getenv("WSLENV") != NULL || - getenv("WSL_DISTRO") != NULL || - getenv("WSL_INTEROP") != NULL - )) ffStrbufAppendS(&product_name, "Windows Subsystem for Linux"); - #else - ffSettingsGetAndroidProperty("ro.product.brand", &product_name); - if(product_name.length > 0){ - product_name.chars[0] = (char) toupper(product_name.chars[0]); - ffStrbufAppendC(&product_name, ' '); - } - - if(!ffSettingsGetAndroidProperty("ro.product.model", &product_name)) - ffSettingsGetAndroidProperty("ro.product.name", &product_name); - - ffStrbufTrimRight(&product_name, ' '); - #endif - - if(product_family.length == 0 && product_name.length == 0) + if(host->productFamily.length == 0 && host->productName.length == 0) { - ffStrbufDestroy(&product_family); - ffStrbufDestroy(&product_name); ffPrintError(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host, "neither product_family nor product_name is set by O.E.M."); return; } - #ifdef __ANDROID__ - #define FF_HOST_DATA(name) \ - FFstrbuf name; \ - ffStrbufInitA(&name, 0); - #else - #define FF_HOST_DATA(name) \ - FFstrbuf name; \ - ffStrbufInit(&name); \ - getHostValue("/sys/devices/virtual/dmi/id/"#name, "/sys/class/dmi/id/"#name, &name); \ - if(!hostValueSet(&name)) \ - ffStrbufClear(&name); - #endif + FFstrbuf output; + ffStrbufInit(&output); - FF_HOST_DATA(product_version) - FF_HOST_DATA(product_sku) - FF_HOST_DATA(bios_date) - FF_HOST_DATA(bios_release) - FF_HOST_DATA(bios_vendor) - FF_HOST_DATA(bios_version) - FF_HOST_DATA(board_name) - FF_HOST_DATA(board_vendor) - FF_HOST_DATA(board_version) - FF_HOST_DATA(chassis_type) - FF_HOST_DATA(chassis_vendor) - FF_HOST_DATA(chassis_version) - FF_HOST_DATA(sys_vendor) - - FFstrbuf host; - ffStrbufInit(&host); - - if(product_name.length > 0) - ffStrbufAppend(&host, &product_name); + if(host->productName.length > 0) + ffStrbufAppend(&output, &host->productName); else - ffStrbufAppend(&host, &product_family); + ffStrbufAppend(&output, &host->productFamily); - if(product_version.length > 0) + if(host->productVersion.length > 0) { - ffStrbufAppendC(&host, ' '); - ffStrbufAppend(&host, &product_version); + ffStrbufAppendC(&output, ' '); + ffStrbufAppend(&output, &host->productVersion); } - ffPrintAndWriteToCache(instance, FF_HOST_MODULE_NAME, &instance->config.host, &host, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &product_family}, - {FF_FORMAT_ARG_TYPE_STRBUF, &product_name}, - {FF_FORMAT_ARG_TYPE_STRBUF, &product_version}, - {FF_FORMAT_ARG_TYPE_STRBUF, &product_sku}, - {FF_FORMAT_ARG_TYPE_STRBUF, &bios_date}, - {FF_FORMAT_ARG_TYPE_STRBUF, &bios_release}, - {FF_FORMAT_ARG_TYPE_STRBUF, &bios_vendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &bios_version}, - {FF_FORMAT_ARG_TYPE_STRBUF, &board_name}, - {FF_FORMAT_ARG_TYPE_STRBUF, &board_vendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &board_version}, - {FF_FORMAT_ARG_TYPE_STRBUF, &chassis_type}, - {FF_FORMAT_ARG_TYPE_STRBUF, &chassis_vendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &chassis_version}, - {FF_FORMAT_ARG_TYPE_STRBUF, &sys_vendor} + ffPrintAndWriteToCache(instance, FF_HOST_MODULE_NAME, &instance->config.host, &output, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productFamily}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->productSku}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosDate}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosRelease}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisType}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host->sysVendor} }); - - ffStrbufDestroy(&product_family); - ffStrbufDestroy(&product_name); - - //On Android, all of them get initialized without heap, so no need to destroy them. - #ifndef __ANDROID__ - ffStrbufDestroy(&product_version); - ffStrbufDestroy(&product_sku); - ffStrbufDestroy(&bios_date); - ffStrbufDestroy(&bios_release); - ffStrbufDestroy(&bios_vendor); - ffStrbufDestroy(&bios_version); - ffStrbufDestroy(&board_name); - ffStrbufDestroy(&board_vendor); - ffStrbufDestroy(&board_version); - ffStrbufDestroy(&chassis_type); - ffStrbufDestroy(&chassis_vendor); - ffStrbufDestroy(&chassis_version); - ffStrbufDestroy(&sys_vendor); - #endif } diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 32ac8aea9..adca0dc11 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -188,6 +188,19 @@ void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...) va_end(arguments); } +void ffStrbufPrependS(FFstrbuf* strbuf, const char* value) +{ + ffStrbufPrependNS(strbuf, (uint32_t) strlen(value), value); +} + +void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value) +{ + ffStrbufEnsureFree(strbuf, length); + memmove(strbuf->chars + length, strbuf->chars, strbuf->length + 1); // + 1 for the null byte + memcpy(strbuf->chars, value, length); + strbuf->length += length; +} + void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value) { ffStrbufClear(strbuf); diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 0d3febc0d..5b7a251c5 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -40,6 +40,9 @@ void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transfor void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments); +void ffStrbufPrependS(FFstrbuf* strbuf, const char* value); +void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value); + void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value); void ffStrbufSetS(FFstrbuf* strbuf, const char* value); diff --git a/tests/strbuf.c b/tests/strbuf.c index cae266501..d5401af53 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -23,6 +23,9 @@ int main(int argc, char** argv) FF_UNUSED(argc, argv) FFstrbuf strbuf; + + //initA + ffStrbufInitA(&strbuf, 64); if(strbuf.allocated != 64) @@ -31,6 +34,8 @@ int main(int argc, char** argv) if(strbuf.length != 0) testFailed(&strbuf, "testSrbuf.length != 0"); + //appendS + ffStrbufAppendS(&strbuf, "123456789"); if(strbuf.length != 9) @@ -39,9 +44,13 @@ int main(int argc, char** argv) if(strcmp(strbuf.chars, "123456789") != 0) testFailed(&strbuf, "strbuf.data != \"123456789\""); + //startsWithS + if(!ffStrbufStartsWithS(&strbuf, "123")) testFailed(&strbuf, "!ffStrbufStartsWithS(&strbuf, \"123\")"); + //removeS + ffStrbufRemoveS(&strbuf, "78"); if(strbuf.length != 7) @@ -50,6 +59,8 @@ int main(int argc, char** argv) if(strcmp(strbuf.chars, "1234569") != 0) testFailed(&strbuf, "strcmp(strbuf.chars, \"123469\") != 0"); + //removeStrings + ffStrbufRemoveStrings(&strbuf, 3, "23", "45", "9"); if(strbuf.length != 2) @@ -58,5 +69,16 @@ int main(int argc, char** argv) if(strcmp(strbuf.chars, "16") != 0) testFailed(&strbuf, "strbuf.chars != \"126\""); + //PrependS + + ffStrbufPrependS(&strbuf, "123"); + + if(strbuf.length != 5) + testFailed(&strbuf, "strbuf.length != 5"); + + if(strcmp(strbuf.chars, "12316") != 0) + testFailed(&strbuf, "strbuf.chars != \"12316\""); + + //Success puts("\033[32mAll tests passed!"FASTFETCH_TEXT_MODIFIER_RESET); }