diff --git a/CMakeLists.txt b/CMakeLists.txt index cd226a7d0f..037e9dfd9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,8 +413,16 @@ if (BUILD_TESTS) PRIVATE libfastfetch ) + add_executable(fastfetch-test-list + tests/list.c + ) + target_link_libraries(fastfetch-test-list + PRIVATE libfastfetch + ) + enable_testing() add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf) + add_test(NAME test-list COMMAND fastfetch-test-list) endif() ################## diff --git a/README.md b/README.md index 71f71e98e4..a6fb617edf 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The following libraries are used if present at runtime: * [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font. * [`libsqlite3`](https://www.sqlite.org/index.html): Needed for pkg & rpm package count. * [`librpm`](http://rpm.org/): Slower fallback for rpm package count. Needed on openSUSE. -* [`libplist`](https://github.com/libimobiledevice/libplist): Binary `plist` file parser. Needed for iTerm2 Terminal font. +* [`libplist`](https://github.com/libimobiledevice/libplist): Binary `plist` file parser ( macOS ). Needed for iTerm2 Terminal font and WM Theme. ## Support status All categories not listed here should work without needing a specific implementation. diff --git a/src/modules/wmtheme.c b/src/modules/wmtheme.c index fbaf22512d..8e422e672b 100644 --- a/src/modules/wmtheme.c +++ b/src/modules/wmtheme.c @@ -229,6 +229,87 @@ static void printOpenbox(FFinstance* instance, const FFstrbuf* dePrettyName) ffStrbufDestroy(&absolutePath); } +#ifdef FF_HAVE_LIBPLIST +#include "common/library.h" +#include "common/io.h" +#include + +static const char* quartzCompositorParsePlist(FFinstance* instance, const FFstrbuf* content, FFstrbuf* theme) { + FF_LIBRARY_LOAD(libplist, &instance->config.libplist, "dlopen libplist failed", "libplist-2.0"FF_LIBRARY_EXTENSION, 2); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_is_binary); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_from_bin); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_from_xml); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_dict_get_item); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_get_string_ptr); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_get_uint_val); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libplist, plist_free); + + plist_t root_node = NULL; + if (ffplist_is_binary(content->chars, content->length)) + ffplist_from_bin(content->chars, content->length, &root_node); + else + ffplist_from_xml(content->chars, content->length, &root_node); + + if(root_node == NULL) + return "parsing Quartz Compositor preference file failed"; + + plist_t pWmThemeColor = ffplist_dict_get_item(root_node, "AppleAccentColor"); + uint64_t wmThemeColor = 100; + if (pWmThemeColor != NULL) + ffplist_get_uint_val(pWmThemeColor, &wmThemeColor); + switch (wmThemeColor) + { + case (uint64_t)-1: ffStrbufAppendS(theme, " (Graphite)"); + case 0: ffStrbufAppendS(theme, "Red"); break; + case 1: ffStrbufAppendS(theme, "Orange"); break; + case 2: ffStrbufAppendS(theme, "Yellow"); break; + case 3: ffStrbufAppendS(theme, "Green"); break; + case 5: ffStrbufAppendS(theme, "Purple"); break; + case 6: ffStrbufAppendS(theme, "Pink"); break; + default: ffStrbufAppendS(theme, "Blue"); break; + } + + plist_t pWmTheme = ffplist_dict_get_item(root_node, "AppleInterfaceStyle"); + ffStrbufAppendF(theme, " (%s)", pWmTheme != NULL ? ffplist_get_string_ptr(pWmTheme, NULL) : "Light"); + + ffplist_free(root_node); + return NULL; +} + +static void printQuartzCompositor(FFinstance* instance) { + FFstrbuf fileName; + ffStrbufInitS(&fileName, instance->state.passwd->pw_dir); + ffStrbufAppendS(&fileName, "/Library/Preferences/.GlobalPreferences.plist"); + + FFstrbuf content; + ffStrbufInit(&content); + ffAppendFileBuffer(fileName.chars, &content); + ffStrbufDestroy(&fileName); + if(content.length == 0) + { + ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmTheme, "reading Quartz Compositor preference file failed"); + ffStrbufDestroy(&content); + return; + } + + FFstrbuf theme; + ffStrbufInit(&theme); + const char* error = quartzCompositorParsePlist(instance, &content, &theme); + ffStrbufDestroy(&content); + + if(error != NULL) + ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmTheme, "%s", error); + else + printWMTheme(instance, theme.chars); +} +#else +static void printQuartzCompositor(FFinstance* instance) +{ + FF_UNUSED(instance); + ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmTheme, "Fastfetch was compiled without libplist support"); +} +#endif + void ffPrintWMTheme(FFinstance* instance) { #ifdef __ANDROID__ @@ -261,6 +342,8 @@ void ffPrintWMTheme(FFinstance* instance) printWMThemeFromSettings(instance, "/org/mate/Marco/general/theme", "org.mate.Marco.general", NULL, "theme"); else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Openbox") == 0) printOpenbox(instance, &result->dePrettyName); + else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Quartz Compositor") == 0) + printQuartzCompositor(instance); else ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmTheme, "Unknown WM: %s", result->wmPrettyName.chars); } diff --git a/src/util/FFlist.c b/src/util/FFlist.c index 8dc563cf46..eccf96e325 100644 --- a/src/util/FFlist.c +++ b/src/util/FFlist.c @@ -46,6 +46,8 @@ uint32_t ffListFirstIndexComp(const FFlist* list, void* compElement, bool(*compF void ffListDestroy(FFlist* list) { - if(list->data != NULL) - free(list->data); + //Avoid free-after-use. These 3 assignments are cheap so don't remove them + list->capacity = list->length = 0; + free(list->data); + list->data = NULL; } diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 430fca127d..9fad01961d 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -29,6 +29,12 @@ void ffStrbufInitCopy(FFstrbuf* strbuf, const FFstrbuf* src) ffStrbufAppend(strbuf, src); } +void ffStrbufInitS(FFstrbuf* strbuf, const char* str) +{ + ffStrbufInitA(strbuf, 0); + ffStrbufAppendS(strbuf, str); +} + uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) { if(strbuf->allocated == 0) @@ -94,14 +100,7 @@ void ffStrbufAppendS(FFstrbuf* strbuf, const char* value) if(value == NULL) return; - for(uint32_t i = 0; value[i] != '\0'; i++) - { - if(i % 16 == 0) - ffStrbufEnsureFree(strbuf, 16); - strbuf->chars[strbuf->length++] = value[i]; - } - - strbuf->chars[strbuf->length] = '\0'; + ffStrbufAppendNS(strbuf, (uint32_t)strlen(value), value); } void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value) @@ -110,15 +109,8 @@ void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value) return; ffStrbufEnsureFree(strbuf, length); - - for(uint32_t i = 0; i < length; i++) - { - if(value[i] == '\0') - break; - - strbuf->chars[strbuf->length++] = value[i]; - } - + memcpy(&strbuf->chars[strbuf->length], value, length); + strbuf->length += length; strbuf->chars[strbuf->length] = '\0'; } @@ -572,5 +564,8 @@ uint16_t ffStrbufToUInt16(const FFstrbuf* strbuf, uint16_t defaultValue) void ffStrbufDestroy(FFstrbuf* strbuf) { + //Avoid free-after-use. These 3 assignments are cheap so don't remove them + strbuf->allocated = strbuf->length = 0; free(strbuf->chars); + strbuf->chars = NULL; } diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 0c0b9020bf..2242d4ec8a 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -24,6 +24,7 @@ typedef struct FFstrbuf void ffStrbufInit(FFstrbuf* strbuf); void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate); void ffStrbufInitCopy(FFstrbuf* strbuf, const FFstrbuf* src); +void ffStrbufInitS(FFstrbuf* strbuf, const char* str); void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free); diff --git a/tests/list.c b/tests/list.c new file mode 100644 index 0000000000..ebae7c8d7d --- /dev/null +++ b/tests/list.c @@ -0,0 +1,89 @@ +#include "fastfetch.h" + +#include +#include +#include + +static void testFailed(const FFlist* list, const char* message, ...) +{ + va_list args; + va_start(args, message); + fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stderr); + vfprintf(stderr, message, args); + for (uint32_t i = 0; i < list->length; ++i) + { + fprintf(stderr, "%u ", *(uint32_t*)ffListGet(list, i)); + } + fputc('\n', stderr); + fputs(FASTFETCH_TEXT_MODIFIER_RESET, stderr); + fputc('\n', stderr); + va_end(args); + exit(1); +} + +int main(int argc, char** argv) +{ + FF_UNUSED(argc, argv) + + FFlist list; + + //initA + + ffListInitA(&list, sizeof(uint32_t), 0); + + if(ffListGet(&list, 0) != NULL) + testFailed(&list, "ffListGet(&list, 0) != NULL"); + + if(list.elementSize != sizeof(int)) + testFailed(&list, "list.elementSize != sizeof(int)"); + + if(list.capacity != 0) + testFailed(&list, "list.capacity != 0"); + + if(list.length != 0) + testFailed(&list, "list.length != 0"); + + //add + for (uint32_t i = 1; i <= FF_LIST_DEFAULT_ALLOC + 1; ++i) + { + *(uint32_t*)ffListAdd(&list) = i; + + if(list.elementSize != sizeof(uint32_t)) + testFailed(&list, "list.elementSize != sizeof(uint32_t)"); + + if(list.length != i) + testFailed(&list, "list.length != i"); + + if(i <= FF_LIST_DEFAULT_ALLOC) + { + if(list.capacity != FF_LIST_DEFAULT_ALLOC) + testFailed(&list, "list.length != FF_LIST_DEFAULT_ALLOC"); + } + else + { + if(list.capacity != FF_LIST_DEFAULT_ALLOC * 2) + testFailed(&list, "list.length != FF_LIST_DEFAULT_ALLOC * 2"); + } + + if(*(uint32_t*)ffListGet(&list, 0) != 1) + testFailed(&list, "*(int*)ffListGet(&list, 0) != 1"); + + if(*(uint32_t*)ffListGet(&list, i - 1) != i) + testFailed(&list, "*(int*)ffListGet(&list, i - 1) != i"); + } + + //Destroy + ffListDestroy(&list); + + if(list.elementSize != sizeof(uint32_t)) + testFailed(&list, "list.elementSize != sizeof(uint32_t)"); + + if(list.capacity != 0) + testFailed(&list, "list.capacity != 0"); + + if(list.length != 0) + testFailed(&list, "list.length != 0"); + + //Success + puts("\033[32mAll tests passed!"FASTFETCH_TEXT_MODIFIER_RESET); +} diff --git a/tests/strbuf.c b/tests/strbuf.c index d5401af53b..a096498fb2 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -26,24 +26,58 @@ int main(int argc, char** argv) //initA - ffStrbufInitA(&strbuf, 64); + ffStrbufInitA(&strbuf, 0); - if(strbuf.allocated != 64) - testFailed(&strbuf, "strbuf.allocated != 64"); + if(strbuf.chars[0] != 0) //make sure chars[0] is accessable + testFailed(&strbuf, "strbuf.chars[0] != 0"); + + if(strbuf.allocated != 0) + testFailed(&strbuf, "strbuf.allocated != 0"); if(strbuf.length != 0) - testFailed(&strbuf, "testSrbuf.length != 0"); + testFailed(&strbuf, "strbuf.length != 0"); //appendS - ffStrbufAppendS(&strbuf, "123456789"); + ffStrbufAppendS(&strbuf, "12345"); + + if(strbuf.length != 5) + testFailed(&strbuf, "strbuf.length != 5"); + + if(strbuf.allocated < 6) + testFailed(&strbuf, "strbuf.allocated < 6"); + + if(ffStrbufCompS(&strbuf, "12345") != 0) + testFailed(&strbuf, "strbuf.data != \"12345\""); + + //appendNS + + ffStrbufAppendNS(&strbuf, 4, "67890"); if(strbuf.length != 9) testFailed(&strbuf, "strbuf.length != 9"); - if(strcmp(strbuf.chars, "123456789") != 0) + if(strbuf.allocated < 10) + testFailed(&strbuf, "strbuf.allocated < 10"); + + if(ffStrbufCompS(&strbuf, "123456789") != 0) testFailed(&strbuf, "strbuf.data != \"123456789\""); + //appendS long + + ffStrbufAppendS(&strbuf, "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + + if(strbuf.length != 109) + testFailed(&strbuf, "strbuf.length != 109"); + + if(strbuf.allocated < 110) + testFailed(&strbuf, "strbuf.allocated < 110"); + + if(strbuf.chars[strbuf.length] != 0) + testFailed(&strbuf, "strbuf.chars[strbuf.length] != 0"); + + strbuf.length = 9; + //startsWithS if(!ffStrbufStartsWithS(&strbuf, "123")) @@ -79,6 +113,18 @@ int main(int argc, char** argv) if(strcmp(strbuf.chars, "12316") != 0) testFailed(&strbuf, "strbuf.chars != \"12316\""); + //Destroy + + ffStrbufDestroy(&strbuf); + if(strbuf.allocated != 0) + testFailed(&strbuf, "strbuf.allocated != 0"); + + if(strbuf.length != 0) + testFailed(&strbuf, "strbuf.length != 0"); + + if(strbuf.chars != NULL) + testFailed(&strbuf, "strbuf.chars != NULL"); + //Success puts("\033[32mAll tests passed!"FASTFETCH_TEXT_MODIFIER_RESET); }