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
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
00069
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
00108
00109
00110
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;
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
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
00207 size=2;
00208 for (i = 0; string[i]; ++i)
00209 {
00210 #ifndef NO_ESCAPES_IN_PATHS
00211 if (string[i] == '\\' && string[i+1])
00212 {
00213
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
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
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;
00240 else if (string[j] == '\\' && string[j+1])
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;
00249
00250 string += j;
00251 if (*string == PATH_SEPARATOR_CHAR) string++;
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
00285
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
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++) ;
00347 if (!(cache->searchableDataDirectories = (char**)malloc(sizeof(char*)*(size+2))))
00348 {
00349 xdgFreeStringList(itemlist);
00350 return FALSE;
00351 }
00352
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++) ;
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
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
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
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