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 xdgFreeStringList(cache->searchableDataDirectories);
00157 cache->searchableDataDirectories = 0;
00158 xdgFreeStringList(cache->searchableConfigDirectories);
00159 cache->searchableConfigDirectories = 0;
00160 }
00161
00162 void xdgWipeHandle(xdgHandle *handle)
00163 {
00164 xdgCachedData* cache = xdgGetCache(handle);
00165 xdgFreeData(cache);
00166 free(cache);
00167 }
00168
00174 static char* xdgGetEnv(const char* name, const char* defaultValue)
00175 {
00176 const char* env;
00177 char* value;
00178
00179 env = getenv(name);
00180 if (env && env[0])
00181 {
00182 if (!(value = (char*)malloc(strlen(env)+1))) return 0;
00183 strcpy(value, env);
00184 }
00185 else
00186 {
00187 if (!(value = (char*)malloc(strlen(defaultValue)+1))) return 0;
00188 strcpy(value, defaultValue);
00189 }
00190 return value;
00191 }
00192
00196 static char** xdgSplitPath(const char* string)
00197 {
00198 unsigned int size, i, j, k;
00199 char** itemlist;
00200
00201
00202 size=2;
00203 for (i = 0; string[i]; ++i)
00204 {
00205 #ifndef NO_ESCAPES_IN_PATHS
00206 if (string[i] == '\\' && string[i+1])
00207 {
00208
00209 ++i;
00210 continue;
00211 }
00212 #endif
00213 if (string[i] == PATH_SEPARATOR_CHAR) ++size;
00214 }
00215
00216 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return 0;
00217 xdgZeroMemory(itemlist, sizeof(char*)*size);
00218
00219 for (i = 0; *string; ++i)
00220 {
00221
00222 for (j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j)
00223 #ifndef NO_ESCAPES_IN_PATHS
00224 if (string[j] == '\\' && string[j+1]) ++j
00225 #endif
00226 ;
00227
00228 if (!(itemlist[i] = (char*)malloc(j+1))) { xdgFreeStringList(itemlist); return 0; }
00229
00230
00231 for (k = j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j, ++k)
00232 {
00233 #ifndef NO_ESCAPES_IN_PATHS
00234 if (string[j] == '\\' && string[j+1] == PATH_SEPARATOR_CHAR) ++j;
00235 else if (string[j] == '\\' && string[j+1])
00236 {
00237 itemlist[i][k]=string[j];
00238 ++j, ++k;
00239 }
00240 #endif
00241 itemlist[i][k] = string[j];
00242 }
00243 itemlist[i][k] = 0;
00244
00245 string += j;
00246 if (*string == PATH_SEPARATOR_CHAR) string++;
00247 }
00248 return itemlist;
00249 }
00250
00256 static char** xdgGetPathListEnv(const char* name, const char ** strings)
00257 {
00258 const char* env;
00259 char* item;
00260 char** itemlist;
00261 int i, size;
00262
00263 env = getenv(name);
00264 if (env && env[0])
00265 {
00266 if (!(item = (char*)malloc(strlen(env)+1))) return NULL;
00267 strcpy(item, env);
00268
00269 itemlist = xdgSplitPath(item);
00270 free(item);
00271 }
00272 else
00273 {
00274 if (!strings) return NULL;
00275 for (size = 0; strings[size]; ++size) ; ++size;
00276 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return NULL;
00277 xdgZeroMemory(itemlist, sizeof(char*)*(size));
00278
00279
00280
00281 for (i = 0; strings[i]; ++i)
00282 {
00283 if (!(item = (char*)malloc(strlen(strings[i])+1))) { xdgFreeStringList(itemlist); return NULL; }
00284 strcpy(item, strings[i]);
00285 itemlist[i] = item;
00286 }
00287 }
00288 return itemlist;
00289 }
00290
00295 static int xdgUpdateHomeDirectories(xdgCachedData* cache)
00296 {
00297 const char* env;
00298 char* home, *defVal;
00299
00300 env = getenv("HOME");
00301 if (!env || !env[0])
00302 return FALSE;
00303 if (!(home = (char*)malloc(strlen(env)+1))) return FALSE;
00304 strcpy(home, env);
00305
00306
00307 defVal = (char*)malloc(strlen(home)+
00308 MAX(MAX(sizeof(DefaultRelativeDataHome), sizeof(DefaultRelativeConfigHome)), sizeof(DefaultRelativeCacheHome)));
00309 if (!defVal) return FALSE;
00310
00311 strcpy(defVal, home);
00312 strcat(defVal, DefaultRelativeDataHome);
00313 if (!(cache->dataHome = xdgGetEnv("XDG_DATA_HOME", defVal))) return FALSE;
00314
00315 defVal[strlen(home)] = 0;
00316 strcat(defVal, DefaultRelativeConfigHome);
00317 if (!(cache->configHome = xdgGetEnv("XDG_CONFIG_HOME", defVal))) return FALSE;
00318
00319 defVal[strlen(home)] = 0;
00320 strcat(defVal, DefaultRelativeCacheHome);
00321 if (!(cache->cacheHome = xdgGetEnv("XDG_CACHE_HOME", defVal))) return FALSE;
00322
00323 free(defVal);
00324 free(home);
00325
00326 return TRUE;
00327 }
00328
00333 static int xdgUpdateDirectoryLists(xdgCachedData* cache)
00334 {
00335 char** itemlist;
00336 int size;
00337
00338 itemlist = xdgGetPathListEnv("XDG_DATA_DIRS", DefaultDataDirectoriesList);
00339
00340 if (!itemlist) return FALSE;
00341 for (size = 0; itemlist[size]; size++) ;
00342 if (!(cache->searchableDataDirectories = (char**)malloc(sizeof(char*)*(size+2))))
00343 {
00344 xdgFreeStringList(itemlist);
00345 return FALSE;
00346 }
00347
00348 cache->searchableDataDirectories[0] = cache->dataHome;
00349 memcpy(&(cache->searchableDataDirectories[1]), itemlist, sizeof(char*)*(size+1));
00350 free(itemlist);
00351
00352 itemlist = xdgGetPathListEnv("XDG_CONFIG_DIRS", DefaultConfigDirectoriesList);
00353 if (!itemlist) return FALSE;
00354 for (size = 0; itemlist[size]; size++) ;
00355 if (!(cache->searchableConfigDirectories = (char**)malloc(sizeof(char*)*(size+2))))
00356 {
00357 xdgFreeStringList(itemlist);
00358 return FALSE;
00359 }
00360 cache->searchableConfigDirectories[0] = cache->configHome;
00361 memcpy(&(cache->searchableConfigDirectories[1]), itemlist, sizeof(char*)*(size+1));
00362 free(itemlist);
00363
00364 return TRUE;
00365 }
00366
00367 int xdgUpdateData(xdgHandle *handle)
00368 {
00369 xdgCachedData* cache = (xdgCachedData*)malloc(sizeof(xdgCachedData));
00370 xdgCachedData* oldCache;
00371 if (!cache) return FALSE;
00372 xdgZeroMemory(cache, sizeof(xdgCachedData));
00373
00374 if (xdgUpdateHomeDirectories(cache) &&
00375 xdgUpdateDirectoryLists(cache))
00376 {
00377
00378 oldCache = xdgGetCache(handle);
00379 handle->reserved = cache;
00380 if (oldCache)
00381 {
00382 xdgFreeData(oldCache);
00383 free(oldCache);
00384 }
00385 return TRUE;
00386 }
00387 else
00388 {
00389
00390 xdgFreeData(cache);
00391 free(cache);
00392 return FALSE;
00393 }
00394 }
00395
00402 static char * xdgFindExisting(const char * relativePath, const char * const * dirList)
00403 {
00404 char * fullPath;
00405 char * returnString = 0;
00406 char * tmpString;
00407 int strLen = 0;
00408 FILE * testFile;
00409 const char * const * item;
00410
00411 for (item = dirList; *item; item++)
00412 {
00413 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
00414 {
00415 if (returnString) free(returnString);
00416 return 0;
00417 }
00418 strcpy(fullPath, *item);
00419 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
00420 strcat(fullPath, DIR_SEPARATOR_STR);
00421 strcat(fullPath, relativePath);
00422 testFile = fopen(fullPath, "r");
00423 if (testFile)
00424 {
00425 if (!(tmpString = (char*)realloc(returnString, strLen+strlen(fullPath)+2)))
00426 {
00427 free(returnString);
00428 free(fullPath);
00429 return 0;
00430 }
00431 returnString = tmpString;
00432 strcpy(&returnString[strLen], fullPath);
00433 strLen = strLen+strlen(fullPath)+1;
00434 fclose(testFile);
00435 }
00436 free(fullPath);
00437 }
00438 if (returnString)
00439 returnString[strLen] = 0;
00440 else
00441 {
00442 if ((returnString = (char*)malloc(2)))
00443 strcpy(returnString, "\0");
00444 }
00445 return returnString;
00446 }
00447
00454 static FILE * xdgFileOpen(const char * relativePath, const char * mode, const char * const * dirList)
00455 {
00456 char * fullPath;
00457 FILE * testFile;
00458 const char * const * item;
00459
00460 for (item = dirList; *item; item++)
00461 {
00462 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
00463 return 0;
00464 strcpy(fullPath, *item);
00465 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
00466 strcat(fullPath, DIR_SEPARATOR_STR);
00467 strcat(fullPath, relativePath);
00468 testFile = fopen(fullPath, mode);
00469 free(fullPath);
00470 if (testFile)
00471 return testFile;
00472 }
00473 return 0;
00474 }
00475
00476 int xdgMakePath(const char * path, mode_t mode)
00477 {
00478 int length = strlen(path);
00479 char * tmpPath;
00480 char * tmpPtr;
00481 int ret;
00482
00483 if (length == 0 || (length == 1 && path[0] == DIR_SEPARATOR_CHAR))
00484 return 0;
00485
00486 if (!(tmpPath = (char*)malloc(length+1)))
00487 {
00488 errno = ENOMEM;
00489 return -1;
00490 }
00491 strcpy(tmpPath, path);
00492 if (tmpPath[length-1] == DIR_SEPARATOR_CHAR)
00493 tmpPath[length-1] = '\0';
00494
00495
00496 for (tmpPtr = tmpPath+1; *tmpPtr; ++tmpPtr)
00497 {
00498 if (*tmpPtr == DIR_SEPARATOR_CHAR)
00499 {
00500 *tmpPtr = '\0';
00501 if (mkdir(tmpPath, mode) == -1)
00502 {
00503 if (errno != EEXIST)
00504 {
00505 free(tmpPath);
00506 return -1;
00507 }
00508 }
00509 *tmpPtr = DIR_SEPARATOR_CHAR;
00510 }
00511 }
00512 ret = mkdir(tmpPath, mode);
00513 free(tmpPath);
00514 return ret;
00515 }
00516
00517 const char * xdgDataHome(xdgHandle *handle)
00518 {
00519 return xdgGetCache(handle)->dataHome;
00520 }
00521 const char * xdgConfigHome(xdgHandle *handle)
00522 {
00523 return xdgGetCache(handle)->configHome;
00524 }
00525 const char * const * xdgDataDirectories(xdgHandle *handle)
00526 {
00527 return (const char * const *)&(xdgGetCache(handle)->searchableDataDirectories[1]);
00528 }
00529 const char * const * xdgSearchableDataDirectories(xdgHandle *handle)
00530 {
00531 return (const char * const *)xdgGetCache(handle)->searchableDataDirectories;
00532 }
00533 const char * const * xdgConfigDirectories(xdgHandle *handle)
00534 {
00535 return (const char * const *)&(xdgGetCache(handle)->searchableConfigDirectories[1]);
00536 }
00537 const char * const * xdgSearchableConfigDirectories(xdgHandle *handle)
00538 {
00539 return (const char * const *)xdgGetCache(handle)->searchableConfigDirectories;
00540 }
00541 const char * xdgCacheHome(xdgHandle *handle)
00542 {
00543 return xdgGetCache(handle)->cacheHome;
00544 }
00545 char * xdgDataFind(const char * relativePath, xdgHandle *handle)
00546 {
00547 return xdgFindExisting(relativePath, xdgSearchableDataDirectories(handle));
00548 }
00549 char * xdgConfigFind(const char * relativePath, xdgHandle *handle)
00550 {
00551 return xdgFindExisting(relativePath, xdgSearchableConfigDirectories(handle));
00552 }
00553 FILE * xdgDataOpen(const char * relativePath, const char * mode, xdgHandle *handle)
00554 {
00555 return xdgFileOpen(relativePath, mode, xdgSearchableDataDirectories(handle));
00556 }
00557 FILE * xdgConfigOpen(const char * relativePath, const char * mode, xdgHandle *handle)
00558 {
00559 return xdgFileOpen(relativePath, mode, xdgSearchableConfigDirectories(handle));
00560 }
00561