libxdg-basedir-1.1.1 1.1.1
|
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 #include <sys/stat.h> 00047 00048 #ifdef FALSE 00049 #undef FALSE 00050 #endif 00051 #ifdef TRUE 00052 #undef TRUE 00053 #endif 00054 #define FALSE 0 00055 #define TRUE 1 00056 00057 #if HAVE_MEMSET || !defined(HAVE_CONFIG_H) 00058 # define xdgZeroMemory(p, n) memset(p, 0, n) 00059 #elif HAVE_BZERO 00060 # define xdgZeroMemory(p, n) bzero(p, n) 00061 #else 00062 static void xdgZeroMemory(void* p, int n) 00063 { 00064 while (n > 0) { ((char*)p)[n] = 0; ++n; } 00065 } 00066 #endif 00067 00068 #if defined _WIN32 && !defined __CYGWIN__ 00069 /* Use Windows separators on all _WIN32 defining 00070 environments, except Cygwin. */ 00071 # define DIR_SEPARATOR_CHAR '\\' 00072 # define DIR_SEPARATOR_STR "\\" 00073 # define PATH_SEPARATOR_CHAR ';' 00074 # define PATH_SEPARATOR_STR ";" 00075 # define NO_ESCAPES_IN_PATHS 00076 #else 00077 # define DIR_SEPARATOR_CHAR '/' 00078 # define DIR_SEPARATOR_STR "/" 00079 # define PATH_SEPARATOR_CHAR ':' 00080 # define PATH_SEPARATOR_STR ":" 00081 # define NO_ESCAPES_IN_PATHS 00082 #endif 00083 00084 #include <basedir.h> 00085 #include <basedir_fs.h> 00086 00087 #ifndef MAX 00088 #define MAX(a, b) ((b) > (a) ? (b) : (a)) 00089 #endif 00090 00091 static const char 00092 DefaultRelativeDataHome[] = DIR_SEPARATOR_STR ".local" DIR_SEPARATOR_STR "share", 00093 DefaultRelativeConfigHome[] = DIR_SEPARATOR_STR ".config", 00094 DefaultDataDirectories1[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "local" DIR_SEPARATOR_STR "share", 00095 DefaultDataDirectories2[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "share", 00096 DefaultConfigDirectories[] = DIR_SEPARATOR_STR "etc" DIR_SEPARATOR_STR "xdg", 00097 DefaultRelativeCacheHome[] = DIR_SEPARATOR_STR ".cache"; 00098 00099 static const char 00100 *DefaultDataDirectoriesList[] = { DefaultDataDirectories1, DefaultDataDirectories2, NULL }, 00101 *DefaultConfigDirectoriesList[] = { DefaultConfigDirectories, NULL }; 00102 00103 typedef struct _xdgCachedData 00104 { 00105 char * dataHome; 00106 char * configHome; 00107 char * cacheHome; 00108 /* Note: string lists are null-terminated and all items */ 00109 /* except the first are assumed to be allocated using malloc. */ 00110 /* The first item is assumed to be allocated by malloc only if */ 00111 /* it is not equal to the appropriate home directory string above. */ 00112 char ** searchableDataDirectories; 00113 char ** searchableConfigDirectories; 00114 } xdgCachedData; 00115 00117 static xdgCachedData* xdgGetCache(xdgHandle *handle) 00118 { 00119 return ((xdgCachedData*)(handle->reserved)); 00120 } 00121 00122 xdgHandle * xdgInitHandle(xdgHandle *handle) 00123 { 00124 if (!handle) return 0; 00125 handle->reserved = 0; /* So xdgUpdateData() doesn't free it */ 00126 if (xdgUpdateData(handle)) 00127 return handle; 00128 return 0; 00129 } 00130 00132 static void xdgFreeStringList(char** list) 00133 { 00134 char** ptr = list; 00135 if (!list) return; 00136 for (; *ptr; ptr++) 00137 free(*ptr); 00138 free(list); 00139 } 00140 00142 static void xdgFreeData(xdgCachedData *cache) 00143 { 00144 if (cache->dataHome); 00145 { 00146 /* the first element of the directory lists is usually the home directory */ 00147 if (cache->searchableDataDirectories && cache->searchableDataDirectories[0] != cache->dataHome) 00148 free(cache->dataHome); 00149 cache->dataHome = 0; 00150 } 00151 if (cache->configHome); 00152 { 00153 if (cache->searchableConfigDirectories && cache->searchableConfigDirectories[0] != cache->configHome) 00154 free(cache->configHome); 00155 cache->configHome = 0; 00156 } 00157 if (cache->cacheHome) 00158 { 00159 free(cache->cacheHome); 00160 cache->cacheHome = 0; 00161 } 00162 xdgFreeStringList(cache->searchableDataDirectories); 00163 cache->searchableDataDirectories = 0; 00164 xdgFreeStringList(cache->searchableConfigDirectories); 00165 cache->searchableConfigDirectories = 0; 00166 } 00167 00168 void xdgWipeHandle(xdgHandle *handle) 00169 { 00170 xdgCachedData* cache = xdgGetCache(handle); 00171 xdgFreeData(cache); 00172 free(cache); 00173 } 00174 00178 static char** xdgSplitPath(const char* string) 00179 { 00180 unsigned int size, i, j, k; 00181 char** itemlist; 00182 00183 /* Get the number of paths */ 00184 size=2; /* One item more than seperators + terminating null item */ 00185 for (i = 0; string[i]; ++i) 00186 { 00187 #ifndef NO_ESCAPES_IN_PATHS 00188 if (string[i] == '\\' && string[i+1]) 00189 { 00190 /* skip escaped characters including seperators */ 00191 ++i; 00192 continue; 00193 } 00194 #endif 00195 if (string[i] == PATH_SEPARATOR_CHAR) ++size; 00196 } 00197 00198 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return 0; 00199 xdgZeroMemory(itemlist, sizeof(char*)*size); 00200 00201 for (i = 0; *string; ++i) 00202 { 00203 /* get length of current string */ 00204 for (j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j) 00205 #ifndef NO_ESCAPES_IN_PATHS 00206 if (string[j] == '\\' && string[j+1]) ++j 00207 #endif 00208 ; 00209 00210 if (!(itemlist[i] = (char*)malloc(j+1))) { xdgFreeStringList(itemlist); return 0; } 00211 00212 /* transfer string, unescaping any escaped seperators */ 00213 for (k = j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j, ++k) 00214 { 00215 #ifndef NO_ESCAPES_IN_PATHS 00216 if (string[j] == '\\' && string[j+1] == PATH_SEPARATOR_CHAR) ++j; /* replace escaped ':' with just ':' */ 00217 else if (string[j] == '\\' && string[j+1]) /* skip escaped characters so escaping remains aligned to pairs. */ 00218 { 00219 itemlist[i][k]=string[j]; 00220 ++j, ++k; 00221 } 00222 #endif 00223 itemlist[i][k] = string[j]; 00224 } 00225 itemlist[i][k] = 0; /* Bugfix provided by Diego 'Flameeyes' Pettenò */ 00226 /* move to next string */ 00227 string += j; 00228 if (*string == PATH_SEPARATOR_CHAR) string++; /* skip seperator */ 00229 } 00230 return itemlist; 00231 } 00232 00238 static char** xdgGetPathListEnv(const char* name, const char ** defaults) 00239 { 00240 const char* env; 00241 char* item; 00242 char** itemlist; 00243 int i, size; 00244 00245 env = getenv(name); 00246 if (env && env[0]) 00247 { 00248 if (!(item = (char*)malloc(strlen(env)+1))) return NULL; 00249 strcpy(item, env); 00250 00251 itemlist = xdgSplitPath(item); 00252 free(item); 00253 } 00254 else 00255 { 00256 if (!defaults) return NULL; 00257 for (size = 0; defaults[size]; ++size) ; ++size; 00258 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return NULL; 00259 xdgZeroMemory(itemlist, sizeof(char*)*(size)); 00260 00261 /* Copy defaults into itemlist. */ 00262 /* Why all this funky stuff? So the result can be handled uniformly by xdgFreeStringList. */ 00263 for (i = 0; defaults[i]; ++i) 00264 { 00265 if (!(item = (char*)malloc(strlen(defaults[i])+1))) { xdgFreeStringList(itemlist); return NULL; } 00266 strcpy(item, defaults[i]); 00267 itemlist[i] = item; 00268 } 00269 } 00270 return itemlist; 00271 } 00272 00278 static char* xdgGetEnv(const char *name) 00279 { 00280 char *env = getenv(name); 00281 if (env && env[0]) 00282 return env; 00283 /* What errno signifies missing env var? */ 00284 errno = EINVAL; 00285 return NULL; 00286 } 00287 00293 static char* xdgEnvDup(const char *name) 00294 { 00295 const char *env; 00296 if ((env = xdgGetEnv(name))) 00297 return strdup(env); 00298 else 00299 return NULL; 00300 } 00301 00306 static int xdgUpdateHomeDirectories(xdgCachedData* cache) 00307 { 00308 const char *homeenv; 00309 char *value; 00310 unsigned int homelen; 00311 static const unsigned int extralen = 00312 MAX(MAX(sizeof(DefaultRelativeDataHome), 00313 sizeof(DefaultRelativeConfigHome)), 00314 sizeof(DefaultRelativeCacheHome)); 00315 00316 if (!(cache->dataHome = xdgEnvDup("XDG_DATA_HOME")) && errno == ENOMEM) return FALSE; 00317 if (!(cache->configHome = xdgEnvDup("XDG_CONFIG_HOME")) && errno == ENOMEM) return FALSE; 00318 if (!(cache->cacheHome = xdgEnvDup("XDG_CACHE_HOME")) && errno == ENOMEM) return FALSE; 00319 errno = 0; 00320 00321 if (cache->dataHome && cache->configHome && cache->cacheHome) return TRUE; 00322 00323 if (!(homeenv = xdgGetEnv("HOME"))) 00324 return FALSE; 00325 00326 /* Allocate maximum needed for any of the 3 default values */ 00327 if (!(value = (char*)malloc((homelen = strlen(homeenv))+extralen))) return FALSE; 00328 memcpy(value, homeenv, homelen+1); 00329 00330 if (!cache->dataHome) 00331 { 00332 memcpy(value+homelen, DefaultRelativeDataHome, sizeof(DefaultRelativeDataHome)); 00333 cache->dataHome = strdup(value); 00334 } 00335 00336 if (!cache->configHome) 00337 { 00338 memcpy(value+homelen, DefaultRelativeConfigHome, sizeof(DefaultRelativeConfigHome)); 00339 cache->configHome = strdup(value); 00340 } 00341 00342 if (!cache->cacheHome) 00343 { 00344 memcpy(value+homelen, DefaultRelativeCacheHome, sizeof(DefaultRelativeCacheHome)); 00345 cache->cacheHome = strdup(value); 00346 } 00347 00348 free(value); 00349 00350 /* free does not change errno, and the prev call *must* have been a strdup, 00351 * so errno is already set. */ 00352 return cache->dataHome && cache->configHome && cache->cacheHome; 00353 } 00354 00366 static char** xdgGetDirectoryLists(const char *envname, char *homedir, const char **defaults) 00367 { 00368 char **envlist; 00369 char **dirlist; 00370 unsigned int size; 00371 00372 if (!(envlist = xdgGetPathListEnv(envname, defaults))) 00373 return NULL; 00374 00375 for (size = 0; envlist[size]; size++) ; /* Get list size */ 00376 if (!(dirlist = (char**)malloc(sizeof(char*)*(size+1+!!homedir)))) 00377 { 00378 xdgFreeStringList(envlist); 00379 return NULL; 00380 } 00381 /* "home" directory has highest priority according to spec */ 00382 if (homedir) 00383 dirlist[0] = homedir; 00384 memcpy(dirlist+!!homedir, envlist, sizeof(char*)*(size+1)); 00385 /* only free the envlist since its elements are now referenced by dirlist */ 00386 free(envlist); 00387 00388 return dirlist; 00389 } 00390 00395 static int xdgUpdateDirectoryLists(xdgCachedData* cache) 00396 { 00397 if (!(cache->searchableDataDirectories = xdgGetDirectoryLists( 00398 "XDG_DATA_DIRS", cache->dataHome, DefaultDataDirectoriesList))) 00399 return FALSE; 00400 if (!(cache->searchableConfigDirectories = xdgGetDirectoryLists( 00401 "XDG_CONFIG_DIRS", cache->configHome, DefaultConfigDirectoriesList))) 00402 return FALSE; 00403 00404 return TRUE; 00405 } 00406 00407 int xdgUpdateData(xdgHandle *handle) 00408 { 00409 xdgCachedData* cache = (xdgCachedData*)malloc(sizeof(xdgCachedData)); 00410 xdgCachedData* oldCache; 00411 if (!cache) return FALSE; 00412 xdgZeroMemory(cache, sizeof(xdgCachedData)); 00413 00414 if (xdgUpdateHomeDirectories(cache) && 00415 xdgUpdateDirectoryLists(cache)) 00416 { 00417 /* Update successful, replace pointer to old cache with pointer to new cache */ 00418 oldCache = xdgGetCache(handle); 00419 handle->reserved = cache; 00420 if (oldCache) 00421 { 00422 xdgFreeData(oldCache); 00423 free(oldCache); 00424 } 00425 return TRUE; 00426 } 00427 else 00428 { 00429 /* Update failed, discard new cache and leave old cache unmodified */ 00430 xdgFreeData(cache); 00431 free(cache); 00432 return FALSE; 00433 } 00434 } 00435 00442 static char * xdgFindExisting(const char * relativePath, const char * const * dirList) 00443 { 00444 char * fullPath; 00445 char * returnString = 0; 00446 char * tmpString; 00447 int strLen = 0; 00448 FILE * testFile; 00449 const char * const * item; 00450 00451 for (item = dirList; *item; item++) 00452 { 00453 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2))) 00454 { 00455 if (returnString) free(returnString); 00456 return 0; 00457 } 00458 strcpy(fullPath, *item); 00459 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR) 00460 strcat(fullPath, DIR_SEPARATOR_STR); 00461 strcat(fullPath, relativePath); 00462 testFile = fopen(fullPath, "r"); 00463 if (testFile) 00464 { 00465 if (!(tmpString = (char*)realloc(returnString, strLen+strlen(fullPath)+2))) 00466 { 00467 free(returnString); 00468 free(fullPath); 00469 return 0; 00470 } 00471 returnString = tmpString; 00472 strcpy(&returnString[strLen], fullPath); 00473 strLen = strLen+strlen(fullPath)+1; 00474 fclose(testFile); 00475 } 00476 free(fullPath); 00477 } 00478 if (returnString) 00479 returnString[strLen] = 0; 00480 else 00481 { 00482 if ((returnString = (char*)malloc(2))) 00483 strcpy(returnString, "\0"); 00484 } 00485 return returnString; 00486 } 00487 00494 static FILE * xdgFileOpen(const char * relativePath, const char * mode, const char * const * dirList) 00495 { 00496 char * fullPath; 00497 FILE * testFile; 00498 const char * const * item; 00499 00500 for (item = dirList; *item; item++) 00501 { 00502 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2))) 00503 return 0; 00504 strcpy(fullPath, *item); 00505 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR) 00506 strcat(fullPath, DIR_SEPARATOR_STR); 00507 strcat(fullPath, relativePath); 00508 testFile = fopen(fullPath, mode); 00509 free(fullPath); 00510 if (testFile) 00511 return testFile; 00512 } 00513 return 0; 00514 } 00515 00516 int xdgMakePath(const char * path, mode_t mode) 00517 { 00518 int length = strlen(path); 00519 char * tmpPath; 00520 char * tmpPtr; 00521 int ret; 00522 00523 if (length == 0 || (length == 1 && path[0] == DIR_SEPARATOR_CHAR)) 00524 return 0; 00525 00526 if (!(tmpPath = (char*)malloc(length+1))) 00527 { 00528 errno = ENOMEM; 00529 return -1; 00530 } 00531 strcpy(tmpPath, path); 00532 if (tmpPath[length-1] == DIR_SEPARATOR_CHAR) 00533 tmpPath[length-1] = '\0'; 00534 00535 /* skip tmpPath[0] since if it's a seperator we have an absolute path */ 00536 for (tmpPtr = tmpPath+1; *tmpPtr; ++tmpPtr) 00537 { 00538 if (*tmpPtr == DIR_SEPARATOR_CHAR) 00539 { 00540 *tmpPtr = '\0'; 00541 if (mkdir(tmpPath, mode) == -1) 00542 { 00543 if (errno != EEXIST) 00544 { 00545 free(tmpPath); 00546 return -1; 00547 } 00548 } 00549 *tmpPtr = DIR_SEPARATOR_CHAR; 00550 } 00551 } 00552 ret = mkdir(tmpPath, mode); 00553 free(tmpPath); 00554 return ret; 00555 } 00556 00565 static char * xdgGetRelativeHome(const char *envname, const char *relativefallback, unsigned int fallbacklength) 00566 { 00567 char *relhome; 00568 if (!(relhome = xdgEnvDup(envname)) && errno != ENOMEM) 00569 { 00570 errno = 0; 00571 const char *home; 00572 unsigned int homelen; 00573 if (!(home = xdgGetEnv("HOME"))) 00574 return NULL; 00575 if (!(relhome = (char*)malloc((homelen = strlen(home))+fallbacklength))) return NULL; 00576 memcpy(relhome, home, homelen); 00577 memcpy(relhome+homelen, relativefallback, fallbacklength+1); 00578 } 00579 return relhome; 00580 } 00581 00582 const char * xdgDataHome(xdgHandle *handle) 00583 { 00584 if (handle) 00585 return xdgGetCache(handle)->dataHome; 00586 else 00587 return xdgGetRelativeHome("XDG_DATA_HOME", DefaultRelativeDataHome, sizeof(DefaultRelativeDataHome)-1); 00588 } 00589 const char * xdgConfigHome(xdgHandle *handle) 00590 { 00591 if (handle) 00592 return xdgGetCache(handle)->configHome; 00593 else 00594 return xdgGetRelativeHome("XDG_CONFIG_HOME", DefaultRelativeConfigHome, sizeof(DefaultRelativeConfigHome)-1); 00595 } 00596 const char * const * xdgDataDirectories(xdgHandle *handle) 00597 { 00598 if (handle) 00599 return (const char * const *)&(xdgGetCache(handle)->searchableDataDirectories[1]); 00600 else 00601 return (const char * const *)xdgGetDirectoryLists("XDG_DATA_DIRS", NULL, DefaultDataDirectoriesList); 00602 } 00603 const char * const * xdgSearchableDataDirectories(xdgHandle *handle) 00604 { 00605 if (handle) 00606 return (const char * const *)xdgGetCache(handle)->searchableDataDirectories; 00607 else 00608 { 00609 char *datahome = (char*)xdgDataHome(NULL); 00610 char **datadirs = 0; 00611 if (datahome && !(datadirs = xdgGetDirectoryLists("XDG_DATA_DIRS", datahome, DefaultDataDirectoriesList))) 00612 free(datahome); 00613 return (const char * const *)datadirs; 00614 } 00615 } 00616 const char * const * xdgConfigDirectories(xdgHandle *handle) 00617 { 00618 if (handle) 00619 return (const char * const *)&(xdgGetCache(handle)->searchableConfigDirectories[1]); 00620 else 00621 return (const char * const *)xdgGetDirectoryLists("XDG_CONFIG_DIRS", NULL, DefaultConfigDirectoriesList); 00622 } 00623 const char * const * xdgSearchableConfigDirectories(xdgHandle *handle) 00624 { 00625 if (handle) 00626 return (const char * const *)xdgGetCache(handle)->searchableConfigDirectories; 00627 else 00628 { 00629 char *confighome = (char*)xdgConfigHome(NULL); 00630 char **configdirs = 0; 00631 if (confighome && !(configdirs = xdgGetDirectoryLists("XDG_CONFIG_DIRS", confighome, DefaultConfigDirectoriesList))) 00632 free(confighome); 00633 return (const char * const *)configdirs; 00634 } 00635 } 00636 const char * xdgCacheHome(xdgHandle *handle) 00637 { 00638 if (handle) 00639 return xdgGetCache(handle)->cacheHome; 00640 else 00641 return xdgGetRelativeHome("XDG_CACHE_HOME", DefaultRelativeCacheHome, sizeof(DefaultRelativeCacheHome)-1); 00642 } 00643 char * xdgDataFind(const char * relativePath, xdgHandle *handle) 00644 { 00645 const char * const * dirs = xdgSearchableDataDirectories(handle); 00646 char * result = xdgFindExisting(relativePath, dirs); 00647 if (!handle) xdgFreeStringList((char**)dirs); 00648 return result; 00649 } 00650 char * xdgConfigFind(const char * relativePath, xdgHandle *handle) 00651 { 00652 const char * const * dirs = xdgSearchableConfigDirectories(handle); 00653 char * result = xdgFindExisting(relativePath, dirs); 00654 if (!handle) xdgFreeStringList((char**)dirs); 00655 return result; 00656 } 00657 FILE * xdgDataOpen(const char * relativePath, const char * mode, xdgHandle *handle) 00658 { 00659 const char * const * dirs = xdgSearchableDataDirectories(handle); 00660 FILE * result = xdgFileOpen(relativePath, mode, dirs); 00661 if (!handle) xdgFreeStringList((char**)dirs); 00662 return result; 00663 } 00664 FILE * xdgConfigOpen(const char * relativePath, const char * mode, xdgHandle *handle) 00665 { 00666 const char * const * dirs = xdgSearchableConfigDirectories(handle); 00667 FILE * result = xdgFileOpen(relativePath, mode, dirs); 00668 if (!handle) xdgFreeStringList((char**)dirs); 00669 return result; 00670 } 00671