00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00070
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
00109
00110
00111
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;
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
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
00184 size=2;
00185 for (i = 0; string[i]; ++i)
00186 {
00187 #ifndef NO_ESCAPES_IN_PATHS
00188 if (string[i] == '\\' && string[i+1])
00189 {
00190
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
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
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;
00217 else if (string[j] == '\\' && string[j+1])
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;
00226
00227 string += j;
00228 if (*string == PATH_SEPARATOR_CHAR) string++;
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
00262
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
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
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
00351
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++) ;
00376 if (!(dirlist = (char**)malloc(sizeof(char*)*(size+1+!!homedir))))
00377 {
00378 xdgFreeStringList(envlist);
00379 return NULL;
00380 }
00381
00382 if (homedir)
00383 dirlist[0] = homedir;
00384 memcpy(dirlist+!!homedir, envlist, sizeof(char*)*(size+1));
00385
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
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
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
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