ui/common/properties.c

changeset 115
e57ca2747782
parent 110
c00e968d018b
--- 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);

mercurial