--- a/ui/common/properties.c Sun Dec 07 20:00:33 2025 +0100 +++ b/ui/common/properties.c Sat Dec 13 15:58:58 2025 +0100 @@ -44,7 +44,7 @@ #include <cx/buffer.h> #include <cx/hash_map.h> -#include "ucx_properties.h" +#include <cx/properties.h> static CxMap *application_properties; static CxMap *language; @@ -54,8 +54,27 @@ static char *locales_dir; static char *pixmaps_dir; +static UiBool use_xdg_config_home = TRUE; + #endif +static UiBool load_on_startup = TRUE; +static void *properties_data = NULL; +static size_t properties_data_length = 0; + +void ui_load_properties_file_on_startup(UiBool enable) { + load_on_startup = enable; +} + +void ui_set_properties_data(const char *str, size_t len) { + if(str && len > 0) { + properties_data = malloc(len); + memcpy(properties_data, str, len); + } else { + properties_data = NULL; + properties_data_length = 0; + } +} char* ui_getappdir(void) { if(ui_appname() == NULL) { @@ -73,6 +92,8 @@ #define UI_ENV_HOME "USERPROFILE" #endif +#define UI_XDG_CONFIG_HOME_VAR "XDG_CONFIG_HOME" + char* ui_configfile(const char *name) { const char *appname = ui_appname(); if(!appname) { @@ -102,8 +123,23 @@ // on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/ cxBufferPutString(&buf, "AppData\\Local\\"); #else - // app dir is $HOME/.$APPNAME/ - cxBufferPut(&buf, '.'); + if(use_xdg_config_home) { + // app dir is $HOME/.config/$APPNAME/ + char *xdghome = getenv(UI_XDG_CONFIG_HOME_VAR); + size_t xdghomelen = xdghome ? strlen(xdghome) : 0; + if(xdghome && xdghomelen > 0) { + cxBufferSeek(&buf, 0, SEEK_SET); + cxBufferPutString(&buf, xdghome); + if(xdghome[xdghomelen-1] != '/') { + cxBufferPut(&buf, '/'); + } + } else { + cxBufferPutString(&buf, ".config/"); + } + } else { + cxBufferPut(&buf, '.'); + } + #endif cxBufferPutString(&buf, appname); cxBufferPut(&buf, UI_PATH_SEPARATOR); @@ -126,10 +162,53 @@ #endif } +static int load_properties(FILE *file, CxMap *map) { + CxProperties prop; + cxPropertiesInitDefault(&prop); + char buf[8192]; + size_t r; + CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR; + while((r = fread(buf, 1, 8192, file)) > 0) { + cxPropertiesFilln(&prop, buf, r); + cxstring key; + cxstring value; + while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) { + cxMapPut(map, key, cx_strdup(value).ptr); + } + if(status > CX_PROPERTIES_OK) { + break; + } + } + return status <= CX_PROPERTIES_NO_DATA ? 0 : 1; +} + void uic_load_app_properties() { application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128); application_properties->collection.simple_destructor = free; + if(!load_on_startup) { + if(properties_data) { + CxProperties prop; + cxPropertiesInitDefault(&prop); + + CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR; + cxPropertiesFilln(&prop, properties_data, properties_data_length); + + cxstring key; + cxstring value; + while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) { + cxMapPut(application_properties, key, cx_strdup(value).ptr); + } + + if(status > CX_PROPERTIES_NO_DATA) { + fprintf(stderr, "Error: cannot load properties: %d\n", (int)status); + } else { + cxMapRehash(application_properties); + } + } + return; + } + if(!ui_appname()) { // applications without name cannot load app properties return; @@ -139,9 +218,37 @@ if(!dir) { return; } + size_t len = strlen(dir); + if(len < 2) { + return; + } if(ui_mkdir(dir)) { + if(errno == ENOENT) { + char *parent = strdup(dir); + for(int i=len-2;i>=0;i--) { + if(parent[i] == '/') { + parent[i] = 0; + break; + } + } + // try creating the parent + int err = ui_mkdir(parent); + if(err) { + fprintf(stderr, "Error: Cannot create directory %s: %s\n", parent, strerror(errno)); + free(parent); + free(dir); + return; + } + free(parent); + + // try dir again + if(!ui_mkdir(dir)) { + errno = EEXIST; // success + } + } + if(errno != EEXIST) { - fprintf(stderr, "Ui Error: Cannot create directory %s\n", dir); + fprintf(stderr, "Error: Cannot create directory %s: %s\n", dir, strerror(errno)); free(dir); return; } @@ -159,8 +266,8 @@ return; } - if(ucx_properties_load(application_properties, file)) { - fprintf(stderr, "Ui Error: Cannot load application properties.\n"); + if(load_properties(file, application_properties)) { + fprintf(stderr, "Error: Cannot load application properties.\n"); } fclose(file); @@ -181,11 +288,13 @@ } int ret = 0; - if(ucx_properties_store(application_properties, file)) { - fprintf(stderr, "Ui Error: Cannot store application properties.\n"); - ret = 1; + CxMapIterator i = cxMapIterator(application_properties); + cx_foreach(CxMapEntry *, entry, i) { + fprintf(file, "%.*s = %s\n", (int)entry->key->len, (char*)entry->key->data, (char*)entry->value); } + cxMapRehash(application_properties); + fclose(file); free(path); @@ -313,8 +422,8 @@ return 1; } - if(ucx_properties_load(lang, file)) { - fprintf(stderr, "Ui Error: Cannot parse language file: %s.\n", path); + if(load_properties(file, lang)) { + fprintf(stderr, "Error: Cannot parse language file: %s.\n", path); } fclose(file);