UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2017 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <sys/stat.h> 33 #include <errno.h> 34 35 #include "../ui/toolkit.h" 36 37 38 #include "properties.h" 39 #include <cx/string.h> 40 #include <cx/buffer.h> 41 42 #include <cx/hash_map.h> 43 #include "ucx_properties.h" 44 45 static CxMap *application_properties; 46 static CxMap *language; 47 48 #ifndef UI_COCOA 49 50 static char *locales_dir; 51 static char *pixmaps_dir; 52 53 #endif 54 55 56 char* ui_getappdir(void) { 57 if(ui_appname() == NULL) { 58 return NULL; 59 } 60 61 return ui_configfile(NULL); 62 } 63 64 #ifndef _WIN32 65 #define UI_PATH_SEPARATOR '/' 66 #define UI_ENV_HOME "HOME" 67 #else 68 #define UI_PATH_SEPARATOR '\\' 69 #define UI_ENV_HOME "USERPROFILE" 70 #endif 71 72 char* ui_configfile(char *name) { 73 const char *appname = ui_appname(); 74 if(!appname) { 75 return NULL; 76 } 77 78 CxBuffer buf; 79 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 80 81 // add base dir 82 char *homeenv = getenv(UI_ENV_HOME); 83 if(homeenv == NULL) { 84 cxBufferDestroy(&buf); 85 return NULL; 86 } 87 cxstring home = cx_str(homeenv); 88 89 cxBufferWrite(home.ptr, 1, home.length, &buf); 90 if(home.ptr[home.length-1] != UI_PATH_SEPARATOR) { 91 cxBufferPut(&buf, UI_PATH_SEPARATOR); 92 } 93 94 #ifdef UI_COCOA 95 // on OS X the app dir is $HOME/Library/Application Support/$APPNAME/ 96 cxBufferPutString(&buf, "Library/Application Support/"); 97 #elif defined(_WIN32) 98 // on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/ 99 cxBufferPutString(&buf, "AppData\\Local\\"); 100 #else 101 // app dir is $HOME/.$APPNAME/ 102 cxBufferPut(&buf, '.'); 103 #endif 104 cxBufferPutString(&buf, appname); 105 cxBufferPut(&buf, UI_PATH_SEPARATOR); 106 107 // add file name 108 if(name) { 109 cxBufferPutString(&buf, name); 110 } 111 112 cxBufferPut(&buf, 0); 113 114 return buf.space; 115 } 116 117 static int ui_mkdir(char *path) { 118 #ifdef _WIN32 119 return mkdir(path); 120 #else 121 return mkdir(path, S_IRWXU); 122 #endif 123 } 124 125 void uic_load_app_properties() { 126 application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128); 127 128 if(!ui_appname()) { 129 // applications without name cannot load app properties 130 return; 131 } 132 133 char *dir = ui_configfile(NULL); 134 if(!dir) { 135 return; 136 } 137 if(ui_mkdir(dir)) { 138 if(errno != EEXIST) { 139 fprintf(stderr, "Ui Error: Cannot create directory %s\n", dir); 140 free(dir); 141 return; 142 } 143 } 144 free(dir); 145 146 char *path = ui_configfile("application.properties"); 147 if(!path) { 148 return; 149 } 150 151 FILE *file = fopen(path, "r"); 152 if(!file) { 153 free(path); 154 return; 155 } 156 157 if(ucx_properties_load(application_properties, file)) { 158 fprintf(stderr, "Ui Error: Cannot load application properties.\n"); 159 } 160 161 fclose(file); 162 free(path); 163 } 164 165 int uic_store_app_properties() { 166 char *path = ui_configfile("application.properties"); 167 if(!path) { 168 return 1; 169 } 170 171 FILE *file = fopen(path, "w"); 172 if(!file) { 173 fprintf(stderr, "Ui Error: Cannot open properties file: %s\n", path); 174 free(path); 175 return 1; 176 } 177 178 int ret = 0; 179 if(ucx_properties_store(application_properties, file)) { 180 fprintf(stderr, "Ui Error: Cannot store application properties.\n"); 181 ret = 1; 182 } 183 184 fclose(file); 185 free(path); 186 187 return ret; 188 } 189 190 191 const char* ui_get_property(const char *name) { 192 return cxMapGet(application_properties, name); 193 } 194 195 void ui_set_property(const char *name, const char *value) { 196 cxMapPut(application_properties, name, (char*)value); 197 } 198 199 const char* ui_set_default_property(const char *name, const char *value) { 200 const char *v = cxMapGet(application_properties, name); 201 if(!v) { 202 cxMapPut(application_properties, name, (char*)value); 203 v = value; 204 } 205 return v; 206 } 207 208 int ui_properties_store(void) { 209 return uic_store_app_properties(); 210 } 211 212 213 static char* uic_concat_path(const char *base, const char *p, const char *ext) { 214 size_t baselen = strlen(base); 215 216 CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 217 if(baselen > 0) { 218 cxBufferWrite(base, 1, baselen, buf); 219 if(base[baselen - 1] != '/') { 220 cxBufferPut(buf, '/'); 221 } 222 } 223 cxBufferWrite(p, 1, strlen(p), buf); 224 if(ext) { 225 cxBufferWrite(ext, 1, strlen(ext), buf); 226 } 227 228 char *str = buf->space; 229 free(buf); 230 return str; 231 } 232 233 #ifndef UI_COCOA 234 235 void ui_locales_dir(char *path) { 236 locales_dir = path; 237 } 238 239 void ui_pixmaps_dir(char *path) { 240 pixmaps_dir = path; 241 } 242 243 char* uic_get_image_path(const char *imgfilename) { 244 if(pixmaps_dir) { 245 return uic_concat_path(pixmaps_dir, imgfilename, NULL); 246 } else { 247 return NULL; 248 } 249 } 250 251 void ui_load_lang(char *locale) { 252 if(!locale) { 253 locale = "en_EN"; 254 } 255 256 char *path = uic_concat_path(locales_dir, locale, ".properties"); 257 258 uic_load_language_file(path); 259 free(path); 260 } 261 262 void ui_load_lang_def(char *locale, char *default_locale) { 263 char tmp[6]; 264 if(!locale) { 265 char *lang = getenv("LANG"); 266 if(lang && strlen(lang) >= 5) { 267 memcpy(tmp, lang, 5); 268 tmp[5] = '\0'; 269 locale = tmp; 270 } else { 271 locale = default_locale; 272 } 273 } 274 275 char *path = uic_concat_path(locales_dir, locale, ".properties"); 276 277 if(uic_load_language_file(path)) { 278 if(default_locale) { 279 ui_load_lang_def(default_locale, NULL); 280 } else { 281 // cannot find any language file 282 fprintf(stderr, "Ui Error: Cannot load language.\n"); 283 free(path); 284 exit(-1); 285 } 286 } 287 free(path); 288 } 289 290 #endif 291 292 int uic_load_language_file(const char *path) { 293 CxMap *lang = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 256); 294 295 FILE *file = fopen(path, "r"); 296 if(!file) { 297 return 1; 298 } 299 300 if(ucx_properties_load(lang, file)) { 301 fprintf(stderr, "Ui Error: Cannot parse language file: %s.\n", path); 302 } 303 304 fclose(file); 305 306 cxMapRehash(lang); 307 308 language = lang; 309 310 return 0; 311 } 312 313 char* uistr(char *name) { 314 char *value = uistr_n(name); 315 return value ? value : "missing string"; 316 } 317 318 char* uistr_n(char *name) { 319 if(!language) { 320 return NULL; 321 } 322 return cxMapGet(language, name); 323 } 324