libxdg-basedir-1.0.2
1.0.2
|
00001 /* Copyright (c) 2007 Mark Nevill 00002 * 00003 * Permission is hereby granted, free of charge, to any person 00004 * obtaining a copy of this software and associated documentation 00005 * files (the "Software"), to deal in the Software without 00006 * restriction, including without limitation the rights to use, 00007 * copy, modify, merge, publish, distribute, sublicense, and/or sell 00008 * copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following 00010 * conditions: 00011 * 00012 * The above copyright notice and this permission notice shall be 00013 * included in all copies or substantial portions of the Software. 00014 * 00015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00016 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 00017 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00018 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 00019 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 00020 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00021 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00022 * OTHER DEALINGS IN THE SOFTWARE. 00023 */ 00024 00028 #if defined(HAVE_CONFIG_H) || defined(_DOXYGEN) 00029 #include <config.h> 00030 #endif 00031 00032 #if STDC_HEADERS || HAVE_STDLIB_H || !defined(HAVE_CONFIG_H) 00033 # include <stdlib.h> 00034 #endif 00035 #if HAVE_MEMORY_H || !defined(HAVE_CONFIG_H) 00036 # include <memory.h> 00037 #endif 00038 #if HAVE_STRING_H || !defined(HAVE_CONFIG_H) 00039 # include <string.h> 00040 #endif 00041 #if HAVE_STRINGS_H 00042 # include <strings.h> 00043 #endif 00044 00045 #include <errno.h> 00046 00047 #ifdef FALSE 00048 #undef FALSE 00049 #endif 00050 #ifdef TRUE 00051 #undef TRUE 00052 #endif 00053 #define FALSE 0 00054 #define TRUE 1 00055 00056 #if HAVE_MEMSET || !defined(HAVE_CONFIG_H) 00057 # define xdgZeroMemory(p, n) memset(p, 0, n) 00058 #elif HAVE_BZERO 00059 # define xdgZeroMemory(p, n) bzero(p, n) 00060 #else 00061 static void xdgZeroMemory(void* p, size_t n) 00062 { 00063 while (n > 0) { ((char*)p)[n] = 0; ++n; } 00064 } 00065 #endif 00066 00067 #if defined _WIN32 && !defined __CYGWIN__ 00068 /* Use Windows separators on all _WIN32 defining 00069 environments, except Cygwin. */ 00070 # define DIR_SEPARATOR_CHAR '\\' 00071 # define DIR_SEPARATOR_STR "\\" 00072 # define PATH_SEPARATOR_CHAR ';' 00073 # define PATH_SEPARATOR_STR ";" 00074 # define NO_ESCAPES_IN_PATHS 00075 #else 00076 # define DIR_SEPARATOR_CHAR '/' 00077 # define DIR_SEPARATOR_STR "/" 00078 # define PATH_SEPARATOR_CHAR ':' 00079 # define PATH_SEPARATOR_STR ":" 00080 # define NO_ESCAPES_IN_PATHS 00081 #endif 00082 00083 #include <basedir.h> 00084 #include <basedir_fs.h> 00085 00086 #ifndef MAX 00087 #define MAX(a, b) ((b) > (a) ? (b) : (a)) 00088 #endif 00089 00090 static const char 00091 DefaultRelativeDataHome[] = DIR_SEPARATOR_STR ".local" DIR_SEPARATOR_STR "share", 00092 DefaultRelativeConfigHome[] = DIR_SEPARATOR_STR ".config", 00093 DefaultDataDirectories1[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "local" DIR_SEPARATOR_STR "share", 00094 DefaultDataDirectories2[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "share", 00095 DefaultConfigDirectories[] = DIR_SEPARATOR_STR "etc" DIR_SEPARATOR_STR "xdg", 00096 DefaultRelativeCacheHome[] = DIR_SEPARATOR_STR ".cache"; 00097 00098 static const char 00099 *DefaultDataDirectoriesList[] = { DefaultDataDirectories1, DefaultDataDirectories2, NULL }, 00100 *DefaultConfigDirectoriesList[] = { DefaultConfigDirectories, NULL }; 00101 00102 typedef struct _xdgCachedData 00103 { 00104 char * dataHome; 00105 char * configHome; 00106 char * cacheHome; 00107 /* Note: string lists are null-terminated and all items */ 00108 /* except the first are assumed to be allocated using malloc. */ 00109 /* The first item is assumed to be allocated by malloc only if */ 00110 /* it is not equal to the appropriate home directory string above. */ 00111 char ** searchableDataDirectories; 00112 char ** searchableConfigDirectories; 00113 } xdgCachedData; 00114 00116 static xdgCachedData* xdgGetCache(xdgHandle *handle) 00117 { 00118 return ((xdgCachedData*)(handle->reserved)); 00119 } 00120 00121 xdgHandle * xdgInitHandle(xdgHandle *handle) 00122 { 00123 if (!handle) return 0; 00124 handle->reserved = 0; /* So xdgUpdateData() doesn't free it */ 00125 if (xdgUpdateData(handle)) 00126 return handle; 00127 return 0; 00128 } 00129 00131 static void xdgFreeStringList(char** list) 00132 { 00133 char** ptr = list; 00134 if (!list) return; 00135 for (; *ptr; ptr++) 00136 free(*ptr); 00137 free(list); 00138 } 00139 00141 static void xdgFreeData(xdgCachedData *cache) 00142 { 00143 if (cache->dataHome); 00144 { 00145 /* the first element of the directory lists is usually the home directory */ 00146 if (cache->searchableDataDirectories[0] != cache->dataHome) 00147 free(cache->dataHome); 00148 cache->dataHome = 0; 00149 } 00150 if (cache->configHome); 00151 { 00152 if (cache->searchableConfigDirectories[0] != cache->configHome) 00153 free(cache->configHome); 00154 cache->configHome = 0; 00155 } 00156 if (cache->cacheHome) 00157 { 00158 free(cache->cacheHome); 00159 cache->cacheHome = 0; 00160 } 00161 xdgFreeStringList(cache->searchableDataDirectories); 00162 cache->searchableDataDirectories = 0; 00163 xdgFreeStringList(cache->searchableConfigDirectories); 00164 cache->searchableConfigDirectories = 0; 00165 } 00166 00167 void xdgWipeHandle(xdgHandle *handle) 00168 { 00169 xdgCachedData* cache = xdgGetCache(handle); 00170 xdgFreeData(cache); 00171 free(cache); 00172 } 00173 00179 static char* xdgGetEnv(const char* name, const char* defaultValue) 00180 { 00181 const char* env; 00182 char* value; 00183 00184 env = getenv(name); 00185 if (env && env[0]) 00186 { 00187 if (!(value = (char*)malloc(strlen(env)+1))) return 0; 00188 strcpy(value, env); 00189 } 00190 else 00191 { 00192 if (!(value = (char*)malloc(strlen(defaultValue)+1))) return 0; 00193 strcpy(value, defaultValue); 00194 } 00195 return value; 00196 } 00197 00201 static char** xdgSplitPath(const char* string) 00202 { 00203 unsigned int size, i, j, k; 00204 char** itemlist; 00205 00206 /* Get the number of paths */ 00207 size=2; /* One item more than seperators + terminating null item */ 00208 for (i = 0; string[i]; ++i) 00209 { 00210 #ifndef NO_ESCAPES_IN_PATHS 00211 if (string[i] == '\\' && string[i+1]) 00212 { 00213 /* skip escaped characters including seperators */ 00214 ++i; 00215 continue; 00216 } 00217 #endif 00218 if (string[i] == PATH_SEPARATOR_CHAR) ++size; 00219 } 00220 00221 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return 0; 00222 xdgZeroMemory(itemlist, sizeof(char*)*size); 00223 00224 for (i = 0; *string; ++i) 00225 { 00226 /* get length of current string */ 00227 for (j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j) 00228 #ifndef NO_ESCAPES_IN_PATHS 00229 if (string[j] == '\\' && string[j+1]) ++j 00230 #endif 00231 ; 00232 00233 if (!(itemlist[i] = (char*)malloc(j+1))) { xdgFreeStringList(itemlist); return 0; } 00234 00235 /* transfer string, unescaping any escaped seperators */ 00236 for (k = j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j, ++k) 00237 { 00238 #ifndef NO_ESCAPES_IN_PATHS 00239 if (string[j] == '\\' && string[j+1] == PATH_SEPARATOR_CHAR) ++j; /* replace escaped ':' with just ':' */ 00240 else if (string[j] == '\\' && string[j+1]) /* skip escaped characters so escaping remains aligned to pairs. */ 00241 { 00242 itemlist[i][k]=string[j]; 00243 ++j, ++k; 00244 } 00245 #endif 00246 itemlist[i][k] = string[j]; 00247 } 00248 itemlist[i][k] = 0; /* Bugfix provided by Diego 'Flameeyes' Pettenò */ 00249 /* move to next string */ 00250 string += j; 00251 if (*string == PATH_SEPARATOR_CHAR) string++; /* skip seperator */ 00252 } 00253 return itemlist; 00254 } 00255 00261 static char** xdgGetPathListEnv(const char* name, const char ** strings) 00262 { 00263 const char* env; 00264 char* item; 00265 char** itemlist; 00266 int i, size; 00267 00268 env = getenv(name); 00269 if (env && env[0]) 00270 { 00271 if (!(item = (char*)malloc(strlen(env)+1))) return NULL; 00272 strcpy(item, env); 00273 00274 itemlist = xdgSplitPath(item); 00275 free(item); 00276 } 00277 else 00278 { 00279 if (!strings) return NULL; 00280 for (size = 0; strings[size]; ++size) ; ++size; 00281 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return NULL; 00282 xdgZeroMemory(itemlist, sizeof(char*)*(size)); 00283 00284 /* Copy defaults into itemlist. */ 00285 /* Why all this funky stuff? So the result can be handled uniformly by xdgFreeStringList. */ 00286 for (i = 0; strings[i]; ++i) 00287 { 00288 if (!(item = (char*)malloc(strlen(strings[i])+1))) { xdgFreeStringList(itemlist); return NULL; } 00289 strcpy(item, strings[i]); 00290 itemlist[i] = item; 00291 } 00292 } 00293 return itemlist; 00294 } 00295 00300 static int xdgUpdateHomeDirectories(xdgCachedData* cache) 00301 { 00302 const char* env; 00303 char* home, *defVal; 00304 00305 env = getenv("HOME"); 00306 if (!env || !env[0]) 00307 return FALSE; 00308 if (!(home = (char*)malloc(strlen(env)+1))) return FALSE; 00309 strcpy(home, env); 00310 00311 /* Allocate maximum needed for any of the 3 default values */ 00312 defVal = (char*)malloc(strlen(home)+ 00313 MAX(MAX(sizeof(DefaultRelativeDataHome), sizeof(DefaultRelativeConfigHome)), sizeof(DefaultRelativeCacheHome))); 00314 if (!defVal) return FALSE; 00315 00316 strcpy(defVal, home); 00317 strcat(defVal, DefaultRelativeDataHome); 00318 if (!(cache->dataHome = xdgGetEnv("XDG_DATA_HOME", defVal))) return FALSE; 00319 00320 defVal[strlen(home)] = 0; 00321 strcat(defVal, DefaultRelativeConfigHome); 00322 if (!(cache->configHome = xdgGetEnv("XDG_CONFIG_HOME", defVal))) return FALSE; 00323 00324 defVal[strlen(home)] = 0; 00325 strcat(defVal, DefaultRelativeCacheHome); 00326 if (!(cache->cacheHome = xdgGetEnv("XDG_CACHE_HOME", defVal))) return FALSE; 00327 00328 free(defVal); 00329 free(home); 00330 00331 return TRUE; 00332 } 00333 00338 static int xdgUpdateDirectoryLists(xdgCachedData* cache) 00339 { 00340 char** itemlist; 00341 int size; 00342 00343 itemlist = xdgGetPathListEnv("XDG_DATA_DIRS", DefaultDataDirectoriesList); 00344 00345 if (!itemlist) return FALSE; 00346 for (size = 0; itemlist[size]; size++) ; /* Get list size */ 00347 if (!(cache->searchableDataDirectories = (char**)malloc(sizeof(char*)*(size+2)))) 00348 { 00349 xdgFreeStringList(itemlist); 00350 return FALSE; 00351 } 00352 /* "home" directory has highest priority according to spec */ 00353 cache->searchableDataDirectories[0] = cache->dataHome; 00354 memcpy(&(cache->searchableDataDirectories[1]), itemlist, sizeof(char*)*(size+1)); 00355 free(itemlist); 00356 00357 itemlist = xdgGetPathListEnv("XDG_CONFIG_DIRS", DefaultConfigDirectoriesList); 00358 if (!itemlist) return FALSE; 00359 for (size = 0; itemlist[size]; size++) ; /* Get list size */ 00360 if (!(cache->searchableConfigDirectories = (char**)malloc(sizeof(char*)*(size+2)))) 00361 { 00362 xdgFreeStringList(itemlist); 00363 return FALSE; 00364 } 00365 cache->searchableConfigDirectories[0] = cache->configHome; 00366 memcpy(&(cache->searchableConfigDirectories[1]), itemlist, sizeof(char*)*(size+1)); 00367 free(itemlist); 00368 00369 return TRUE; 00370 } 00371 00372 int xdgUpdateData(xdgHandle *handle) 00373 { 00374 xdgCachedData* cache = (xdgCachedData*)malloc(sizeof(xdgCachedData)); 00375 xdgCachedData* oldCache; 00376 if (!cache) return FALSE; 00377 xdgZeroMemory(cache, sizeof(xdgCachedData)); 00378 00379 if (xdgUpdateHomeDirectories(cache) && 00380 xdgUpdateDirectoryLists(cache)) 00381 { 00382 /* Update successful, replace pointer to old cache with pointer to new cache */ 00383 oldCache = xdgGetCache(handle); 00384 handle->reserved = cache; 00385 if (oldCache) 00386 { 00387 xdgFreeData(oldCache); 00388 free(oldCache); 00389 } 00390 return TRUE; 00391 } 00392 else 00393 { 00394 /* Update failed, discard new cache and leave old cache unmodified */ 00395 xdgFreeData(cache); 00396 free(cache); 00397 return FALSE; 00398 } 00399 } 00400 00407 static char * xdgFindExisting(const char * relativePath, const char * const * dirList) 00408 { 00409 char * fullPath; 00410 char * returnString = 0; 00411 char * tmpString; 00412 int strLen = 0; 00413 FILE * testFile; 00414 const char * const * item; 00415 00416 for (item = dirList; *item; item++) 00417 { 00418 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2))) 00419 { 00420 if (returnString) free(returnString); 00421 return 0; 00422 } 00423 strcpy(fullPath, *item); 00424 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR) 00425 strcat(fullPath, DIR_SEPARATOR_STR); 00426 strcat(fullPath, relativePath); 00427 testFile = fopen(fullPath, "r"); 00428 if (testFile) 00429 { 00430 if (!(tmpString = (char*)realloc(returnString, strLen+strlen(fullPath)+2))) 00431 { 00432 free(returnString); 00433 free(fullPath); 00434 return 0; 00435 } 00436 returnString = tmpString; 00437 strcpy(&returnString[strLen], fullPath); 00438 strLen = strLen+strlen(fullPath)+1; 00439 fclose(testFile); 00440 } 00441 free(fullPath); 00442 } 00443 if (returnString) 00444 returnString[strLen] = 0; 00445 else 00446 { 00447 if ((returnString = (char*)malloc(2))) 00448 strcpy(returnString, "\0"); 00449 } 00450 return returnString; 00451 } 00452 00459 static FILE * xdgFileOpen(const char * relativePath, const char * mode, const char * const * dirList) 00460 { 00461 char * fullPath; 00462 FILE * testFile; 00463 const char * const * item; 00464 00465 for (item = dirList; *item; item++) 00466 { 00467 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2))) 00468 return 0; 00469 strcpy(fullPath, *item); 00470 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR) 00471 strcat(fullPath, DIR_SEPARATOR_STR); 00472 strcat(fullPath, relativePath); 00473 testFile = fopen(fullPath, mode); 00474 free(fullPath); 00475 if (testFile) 00476 return testFile; 00477 } 00478 return 0; 00479 } 00480 00481 int xdgMakePath(const char * path, mode_t mode) 00482 { 00483 int length = strlen(path); 00484 char * tmpPath; 00485 char * tmpPtr; 00486 int ret; 00487 00488 if (length == 0 || (length == 1 && path[0] == DIR_SEPARATOR_CHAR)) 00489 return 0; 00490 00491 if (!(tmpPath = (char*)malloc(length+1))) 00492 { 00493 errno = ENOMEM; 00494 return -1; 00495 } 00496 strcpy(tmpPath, path); 00497 if (tmpPath[length-1] == DIR_SEPARATOR_CHAR) 00498 tmpPath[length-1] = '\0'; 00499 00500 /* skip tmpPath[0] since if it's a seperator we have an absolute path */ 00501 for (tmpPtr = tmpPath+1; *tmpPtr; ++tmpPtr) 00502 { 00503 if (*tmpPtr == DIR_SEPARATOR_CHAR) 00504 { 00505 *tmpPtr = '\0'; 00506 if (mkdir(tmpPath, mode) == -1) 00507 { 00508 if (errno != EEXIST) 00509 { 00510 free(tmpPath); 00511 return -1; 00512 } 00513 } 00514 *tmpPtr = DIR_SEPARATOR_CHAR; 00515 } 00516 } 00517 ret = mkdir(tmpPath, mode); 00518 free(tmpPath); 00519 return ret; 00520 } 00521 00522 const char * xdgDataHome(xdgHandle *handle) 00523 { 00524 return xdgGetCache(handle)->dataHome; 00525 } 00526 const char * xdgConfigHome(xdgHandle *handle) 00527 { 00528 return xdgGetCache(handle)->configHome; 00529 } 00530 const char * const * xdgDataDirectories(xdgHandle *handle) 00531 { 00532 return (const char * const *)&(xdgGetCache(handle)->searchableDataDirectories[1]); 00533 } 00534 const char * const * xdgSearchableDataDirectories(xdgHandle *handle) 00535 { 00536 return (const char * const *)xdgGetCache(handle)->searchableDataDirectories; 00537 } 00538 const char * const * xdgConfigDirectories(xdgHandle *handle) 00539 { 00540 return (const char * const *)&(xdgGetCache(handle)->searchableConfigDirectories[1]); 00541 } 00542 const char * const * xdgSearchableConfigDirectories(xdgHandle *handle) 00543 { 00544 return (const char * const *)xdgGetCache(handle)->searchableConfigDirectories; 00545 } 00546 const char * xdgCacheHome(xdgHandle *handle) 00547 { 00548 return xdgGetCache(handle)->cacheHome; 00549 } 00550 char * xdgDataFind(const char * relativePath, xdgHandle *handle) 00551 { 00552 return xdgFindExisting(relativePath, xdgSearchableDataDirectories(handle)); 00553 } 00554 char * xdgConfigFind(const char * relativePath, xdgHandle *handle) 00555 { 00556 return xdgFindExisting(relativePath, xdgSearchableConfigDirectories(handle)); 00557 } 00558 FILE * xdgDataOpen(const char * relativePath, const char * mode, xdgHandle *handle) 00559 { 00560 return xdgFileOpen(relativePath, mode, xdgSearchableDataDirectories(handle)); 00561 } 00562 FILE * xdgConfigOpen(const char * relativePath, const char * mode, xdgHandle *handle) 00563 { 00564 return xdgFileOpen(relativePath, mode, xdgSearchableConfigDirectories(handle)); 00565 } 00566