1 /* Copyright (c) 2007 Mark Nevill
2 *
3 * Permission is hereby granted, free of charge, to any person
4 * obtaining a copy of this software and associated documentation
5 * files (the "Software"), to deal in the Software without
6 * restriction, including without limitation the rights to use,
7 * copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following
10 * conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 /** @file basedir.c
26 * @brief Implementation of the XDG Base Directory specification. */
27
28 #if defined(HAVE_CONFIG_H) || defined(_DOXYGEN)
29 #include <config.h>
30 #endif
31
32 #if STDC_HEADERS || HAVE_STDLIB_H || !defined(HAVE_CONFIG_H)
33 # include <stdlib.h>
34 #endif
35 #if HAVE_MEMORY_H || !defined(HAVE_CONFIG_H)
36 # include <memory.h>
37 #endif
38 #if HAVE_STRING_H || !defined(HAVE_CONFIG_H)
39 # include <string.h>
40 #endif
41 #if HAVE_STRINGS_H
42 # include <strings.h>
43 #endif
44
45 #include <errno.h>
46 #include <sys/stat.h>
47
48 #ifdef FALSE
49 #undef FALSE
50 #endif
51 #ifdef TRUE
52 #undef TRUE
53 #endif
54 #define FALSE 0
55 #define TRUE 1
56
57 #if HAVE_MEMSET || !defined(HAVE_CONFIG_H)
58 # define xdgZeroMemory(p, n) memset(p, 0, n)
59 #elif HAVE_BZERO
60 # define xdgZeroMemory(p, n) bzero(p, n)
61 #else
xdgZeroMemory(void * p,int n)62 static void xdgZeroMemory(void* p, int n)
63 {
64 while (n > 0) { ((char*)p)[--n] = 0; }
65 }
66 #endif
67
68 #if defined _WIN32 && !defined __CYGWIN__
69 /* Use Windows separators on all _WIN32 defining
70 environments, except Cygwin. */
71 # define DIR_SEPARATOR_CHAR '\\'
72 # define DIR_SEPARATOR_STR "\\"
73 # define PATH_SEPARATOR_CHAR ';'
74 # define PATH_SEPARATOR_STR ";"
75 # define NO_ESCAPES_IN_PATHS
76 #else
77 # define DIR_SEPARATOR_CHAR '/'
78 # define DIR_SEPARATOR_STR "/"
79 # define PATH_SEPARATOR_CHAR ':'
80 # define PATH_SEPARATOR_STR ":"
81 # define NO_ESCAPES_IN_PATHS
82 #endif
83
84 #include <basedir.h>
85 #include <basedir_fs.h>
86
87 #ifndef MAX
88 #define MAX(a, b) ((b) > (a) ? (b) : (a))
89 #endif
90
91 static const char
92 DefaultRelativeDataHome[] = DIR_SEPARATOR_STR ".local" DIR_SEPARATOR_STR "share",
93 DefaultRelativeConfigHome[] = DIR_SEPARATOR_STR ".config",
94 DefaultDataDirectories1[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "local" DIR_SEPARATOR_STR "share",
95 DefaultDataDirectories2[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "share",
96 DefaultConfigDirectories[] = DIR_SEPARATOR_STR "etc" DIR_SEPARATOR_STR "xdg",
97 DefaultRelativeCacheHome[] = DIR_SEPARATOR_STR ".cache";
98
99 static const char
100 *DefaultDataDirectoriesList[] = { DefaultDataDirectories1, DefaultDataDirectories2, NULL },
101 *DefaultConfigDirectoriesList[] = { DefaultConfigDirectories, NULL };
102
103 typedef struct _xdgCachedData
104 {
105 char * dataHome;
106 char * configHome;
107 char * cacheHome;
108 char * runtimeDirectory;
109 /* Note: string lists are null-terminated and all items */
110 /* are to be allocated using malloc. */
111 char ** searchableDataDirectories;
112 char ** searchableConfigDirectories;
113 } xdgCachedData;
114
115 /** Get cache object associated with a handle */
xdgGetCache(xdgHandle * handle)116 static xdgCachedData* xdgGetCache(xdgHandle *handle)
117 {
118 return ((xdgCachedData*)(handle->reserved));
119 }
120
xdgInitHandle(xdgHandle * handle)121 xdgHandle * xdgInitHandle(xdgHandle *handle)
122 {
123 if (!handle) return 0;
124 handle->reserved = 0; /* So xdgUpdateData() doesn't free it */
125 if (xdgUpdateData(handle))
126 return handle;
127 return 0;
128 }
129
130 /** Free all memory used by a NULL-terminated string list */
xdgFreeStringList(char ** list)131 static void xdgFreeStringList(char** list)
132 {
133 char** ptr = list;
134 if (!list) return;
135 for (; *ptr; ptr++)
136 free(*ptr);
137 free(list);
138 }
139
140 /** Free all data in the cache and set pointers to null. */
xdgFreeData(xdgCachedData * cache)141 static void xdgFreeData(xdgCachedData *cache)
142 {
143 if (cache->dataHome)
144 {
145 /* the first element of the directory lists is usually the home directory */
146 if (cache->searchableDataDirectories && cache->searchableDataDirectories[0] != cache->dataHome)
147 free(cache->dataHome);
148 cache->dataHome = 0;
149 }
150 if (cache->configHome)
151 {
152 if (cache->searchableConfigDirectories && cache->searchableConfigDirectories[0] != cache->configHome)
153 free(cache->configHome);
154 cache->configHome = 0;
155 }
156 if (cache->cacheHome)
157 {
158 free(cache->cacheHome);
159 cache->cacheHome = 0;
160 }
161 if (cache->runtimeDirectory)
162 {
163 free(cache->runtimeDirectory);
164 cache->runtimeDirectory = 0;
165 }
166 xdgFreeStringList(cache->searchableDataDirectories);
167 cache->searchableDataDirectories = 0;
168 xdgFreeStringList(cache->searchableConfigDirectories);
169 cache->searchableConfigDirectories = 0;
170 }
171
xdgWipeHandle(xdgHandle * handle)172 void xdgWipeHandle(xdgHandle *handle)
173 {
174 xdgCachedData* cache = xdgGetCache(handle);
175 xdgFreeData(cache);
176 free(cache);
177 }
178
179 /** Split string at ':', return null-terminated list of resulting strings.
180 * @param string String to be split
181 */
xdgSplitPath(const char * string)182 static char** xdgSplitPath(const char* string)
183 {
184 unsigned int size, i, j, k;
185 char** itemlist;
186
187 /* Get the number of paths */
188 size=2; /* One item more than seperators + terminating null item */
189 for (i = 0; string[i]; ++i)
190 {
191 #ifndef NO_ESCAPES_IN_PATHS
192 if (string[i] == '\\' && string[i+1])
193 {
194 /* skip escaped characters including seperators */
195 ++i;
196 continue;
197 }
198 #endif
199 if (string[i] == PATH_SEPARATOR_CHAR) ++size;
200 }
201
202 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return 0;
203 xdgZeroMemory(itemlist, sizeof(char*)*size);
204
205 for (i = 0; *string; ++i)
206 {
207 /* get length of current string */
208 for (j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j)
209 #ifndef NO_ESCAPES_IN_PATHS
210 if (string[j] == '\\' && string[j+1]) ++j
211 #endif
212 ;
213
214 if (!(itemlist[i] = (char*)malloc(j+1))) { xdgFreeStringList(itemlist); return 0; }
215
216 /* transfer string, unescaping any escaped seperators */
217 for (k = j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j, ++k)
218 {
219 #ifndef NO_ESCAPES_IN_PATHS
220 if (string[j] == '\\' && string[j+1] == PATH_SEPARATOR_CHAR) ++j; /* replace escaped ':' with just ':' */
221 else if (string[j] == '\\' && string[j+1]) /* skip escaped characters so escaping remains aligned to pairs. */
222 {
223 itemlist[i][k]=string[j];
224 ++j, ++k;
225 }
226 #endif
227 itemlist[i][k] = string[j];
228 }
229 itemlist[i][k] = 0; /* Bugfix provided by Diego 'Flameeyes' Pettenò */
230 /* move to next string */
231 string += j;
232 if (*string == PATH_SEPARATOR_CHAR) string++; /* skip seperator */
233 }
234 return itemlist;
235 }
236
237 /** Get $PATH-style environment variable as list of strings.
238 * If $name is unset or empty, use default strings specified by variable arguments.
239 * @param name Name of environment variable
240 * @param defaults NULL-terminated list of strings to be copied and used as defaults
241 */
xdgGetPathListEnv(const char * name,const char ** defaults)242 static char** xdgGetPathListEnv(const char* name, const char ** defaults)
243 {
244 const char* env;
245 char* item;
246 char** itemlist;
247 int i, size;
248
249 env = getenv(name);
250 if (env && env[0])
251 {
252 if (!(item = (char*)malloc(strlen(env)+1))) return NULL;
253 strcpy(item, env);
254
255 itemlist = xdgSplitPath(item);
256 free(item);
257 }
258 else
259 {
260 if (!defaults) return NULL;
261 for (size = 0; defaults[size] != NULL; ++size);
262 ++size; // include NULL terminator
263 if (!(itemlist = (char**)malloc(sizeof(char*) * size))) return NULL;
264 xdgZeroMemory(itemlist, sizeof(char*)*(size));
265
266 /* Copy defaults into itemlist. */
267 for (i = 0; defaults[i]; ++i)
268 {
269 if (!(item = strdup(defaults[i]))) { xdgFreeStringList(itemlist); return NULL; }
270 itemlist[i] = item;
271 }
272 }
273 return itemlist;
274 }
275
276 /** Get value of an environment variable.
277 * Sets @c errno to @c EINVAL if variable is not set or empty.
278 * @param name Name of environment variable.
279 * @return The environment variable or NULL if an error occurs.
280 */
xdgGetEnv(const char * name)281 static char* xdgGetEnv(const char *name)
282 {
283 char *env = getenv(name);
284 if (env && env[0])
285 return env;
286 /* What errno signifies missing env var? */
287 errno = EINVAL;
288 return NULL;
289 }
290
291 /** Duplicate an environment variable.
292 * Sets @c errno to @c ENOMEM if unable to allocate duplicate string.
293 * Sets @c errno to @c EINVAL if variable is not set or empty.
294 * @return The duplicated string or NULL if an error occurs.
295 */
xdgEnvDup(const char * name)296 static char* xdgEnvDup(const char *name)
297 {
298 const char *env;
299 if ((env = xdgGetEnv(name)))
300 return strdup(env);
301 else
302 return NULL;
303 }
304
305 /** Update all *Home variables of cache.
306 * This includes xdgCachedData::dataHome, xdgCachedData::configHome and xdgCachedData::cacheHome.
307 * @param cache Data cache to be updated
308 */
xdgUpdateHomeDirectories(xdgCachedData * cache)309 static int xdgUpdateHomeDirectories(xdgCachedData* cache)
310 {
311 const char *homeenv;
312 char *value;
313 unsigned int homelen;
314 static const unsigned int extralen =
315 MAX(MAX(sizeof(DefaultRelativeDataHome),
316 sizeof(DefaultRelativeConfigHome)),
317 sizeof(DefaultRelativeCacheHome));
318
319 if (!(cache->dataHome = xdgEnvDup("XDG_DATA_HOME")) && errno == ENOMEM) return FALSE;
320 if (!(cache->configHome = xdgEnvDup("XDG_CONFIG_HOME")) && errno == ENOMEM) return FALSE;
321 if (!(cache->cacheHome = xdgEnvDup("XDG_CACHE_HOME")) && errno == ENOMEM) return FALSE;
322 if (!(cache->runtimeDirectory = xdgEnvDup("XDG_RUNTIME_DIR")) && errno == ENOMEM) return FALSE;
323 errno = 0;
324
325 if (cache->dataHome && cache->configHome && cache->cacheHome) return TRUE;
326
327 if (!(homeenv = xdgGetEnv("HOME")))
328 return FALSE;
329
330 /* Allocate maximum needed for any of the 3 default values */
331 homelen = strlen(homeenv);
332 value = malloc(homelen + extralen);
333 if (value == NULL) return FALSE;
334 memcpy(value, homeenv, homelen+1);
335
336 if (!cache->dataHome)
337 {
338 memcpy(value+homelen, DefaultRelativeDataHome, sizeof(DefaultRelativeDataHome));
339 cache->dataHome = strdup(value);
340 }
341
342 if (!cache->configHome)
343 {
344 memcpy(value+homelen, DefaultRelativeConfigHome, sizeof(DefaultRelativeConfigHome));
345 cache->configHome = strdup(value);
346 }
347
348 if (!cache->cacheHome)
349 {
350 memcpy(value+homelen, DefaultRelativeCacheHome, sizeof(DefaultRelativeCacheHome));
351 cache->cacheHome = strdup(value);
352 }
353
354 free(value);
355
356 /* free does not change errno, and the prev call *must* have been a strdup,
357 * so errno is already set. */
358 return cache->dataHome && cache->configHome && cache->cacheHome;
359 }
360
361 /** Get directory lists with initial home directory.
362 * @param envname Environment variable with colon-seperated directories.
363 * @param homedir Home directory for this directory list or NULL. This
364 * parameter should be allocated on the heap. The returned list
365 * will start with this path, and should be considered as owning
366 * the memory.
367 * @param defaults Default directories if environment variable is not set.
368 * @return An array of strings. Both the array and its contents are allocated
369 * with malloc(). The function xdgFreeStringList is provided for
370 * conveniantly free()-ing the list and all its elements.
371 */
xdgGetDirectoryLists(const char * envname,char * homedir,const char ** defaults)372 static char** xdgGetDirectoryLists(const char *envname, char *homedir, const char **defaults)
373 {
374 char **envlist;
375 char **dirlist;
376 unsigned int size;
377
378 if (!(envlist = xdgGetPathListEnv(envname, defaults)))
379 return NULL;
380
381 for (size = 0; envlist[size]; size++) ; /* Get list size */
382 if (!(dirlist = (char**)malloc(sizeof(char*)*(size+1+!!homedir))))
383 {
384 xdgFreeStringList(envlist);
385 return NULL;
386 }
387 /* "home" directory has highest priority according to spec */
388 if (homedir)
389 dirlist[0] = homedir;
390 memcpy(dirlist+!!homedir, envlist, sizeof(char*)*(size+1));
391 /* only free the envlist since its elements are now referenced by dirlist */
392 free(envlist);
393
394 return dirlist;
395 }
396
397 /** Update all *Directories variables of cache.
398 * This includes xdgCachedData::searchableDataDirectories and xdgCachedData::searchableConfigDirectories.
399 * @param cache Data cache to be updated.
400 */
xdgUpdateDirectoryLists(xdgCachedData * cache)401 static int xdgUpdateDirectoryLists(xdgCachedData* cache)
402 {
403 if (!(cache->searchableDataDirectories = xdgGetDirectoryLists(
404 "XDG_DATA_DIRS", cache->dataHome, DefaultDataDirectoriesList)))
405 return FALSE;
406 if (!(cache->searchableConfigDirectories = xdgGetDirectoryLists(
407 "XDG_CONFIG_DIRS", cache->configHome, DefaultConfigDirectoriesList)))
408 return FALSE;
409
410 return TRUE;
411 }
412
xdgUpdateData(xdgHandle * handle)413 int xdgUpdateData(xdgHandle *handle)
414 {
415 xdgCachedData* cache = (xdgCachedData*)malloc(sizeof(xdgCachedData));
416 xdgCachedData* oldCache;
417 if (!cache) return FALSE;
418 xdgZeroMemory(cache, sizeof(xdgCachedData));
419
420 if (xdgUpdateHomeDirectories(cache) &&
421 xdgUpdateDirectoryLists(cache))
422 {
423 /* Update successful, replace pointer to old cache with pointer to new cache */
424 oldCache = xdgGetCache(handle);
425 handle->reserved = cache;
426 if (oldCache)
427 {
428 xdgFreeData(oldCache);
429 free(oldCache);
430 }
431 return TRUE;
432 }
433 else
434 {
435 /* Update failed, discard new cache and leave old cache unmodified */
436 xdgFreeData(cache);
437 free(cache);
438 return FALSE;
439 }
440 }
441
442 /** Find all existing files corresponding to relativePath relative to each item in dirList.
443 * @param relativePath Relative path to search for.
444 * @param dirList <tt>NULL</tt>-terminated list of directory paths.
445 * @return A sequence of null-terminated strings terminated by a
446 * double-<tt>NULL</tt> (empty string) and allocated using malloc().
447 */
xdgFindExisting(const char * relativePath,const char * const * dirList)448 static char * xdgFindExisting(const char * relativePath, const char * const * dirList)
449 {
450 char * fullPath;
451 char * returnString = 0;
452 char * tmpString;
453 int strLen = 0;
454 FILE * testFile;
455 const char * const * item;
456
457 for (item = dirList; *item; item++)
458 {
459 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
460 {
461 if (returnString) free(returnString);
462 return 0;
463 }
464 strcpy(fullPath, *item);
465 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
466 strcat(fullPath, DIR_SEPARATOR_STR);
467 strcat(fullPath, relativePath);
468 testFile = fopen(fullPath, "r");
469 if (testFile)
470 {
471 if (!(tmpString = (char*)realloc(returnString, strLen+strlen(fullPath)+2)))
472 {
473 free(returnString);
474 free(fullPath);
475 return 0;
476 }
477 returnString = tmpString;
478 strcpy(&returnString[strLen], fullPath);
479 strLen = strLen+strlen(fullPath)+1;
480 fclose(testFile);
481 }
482 free(fullPath);
483 }
484 if (returnString)
485 returnString[strLen] = 0;
486 else
487 {
488 if ((returnString = (char*)malloc(2)))
489 strcpy(returnString, "\0");
490 }
491 return returnString;
492 }
493
494 /** Open first possible config file corresponding to relativePath.
495 * @param relativePath Path to scan for.
496 * @param mode Mode with which to attempt to open files (see fopen modes).
497 * @param dirList <tt>NULL</tt>-terminated list of paths in which to search for relativePath.
498 * @return File pointer if successful else @c NULL. Client must use @c fclose to close file.
499 */
xdgFileOpen(const char * relativePath,const char * mode,const char * const * dirList)500 static FILE * xdgFileOpen(const char * relativePath, const char * mode, const char * const * dirList)
501 {
502 char * fullPath;
503 FILE * testFile;
504 const char * const * item;
505
506 for (item = dirList; *item; item++)
507 {
508 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
509 return 0;
510 strcpy(fullPath, *item);
511 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
512 strcat(fullPath, DIR_SEPARATOR_STR);
513 strcat(fullPath, relativePath);
514 testFile = fopen(fullPath, mode);
515 free(fullPath);
516 if (testFile)
517 return testFile;
518 }
519 return 0;
520 }
521
xdgMakePath(const char * path,mode_t mode)522 int xdgMakePath(const char * path, mode_t mode)
523 {
524 int length = strlen(path);
525 char * tmpPath;
526 char * tmpPtr;
527 int ret;
528
529 if (length == 0 || (length == 1 && path[0] == DIR_SEPARATOR_CHAR))
530 return 0;
531
532 if (!(tmpPath = (char*)malloc(length+1)))
533 {
534 errno = ENOMEM;
535 return -1;
536 }
537 strcpy(tmpPath, path);
538 if (tmpPath[length-1] == DIR_SEPARATOR_CHAR)
539 tmpPath[length-1] = '\0';
540
541 /* skip tmpPath[0] since if it's a seperator we have an absolute path */
542 for (tmpPtr = tmpPath+1; *tmpPtr; ++tmpPtr)
543 {
544 if (*tmpPtr == DIR_SEPARATOR_CHAR)
545 {
546 *tmpPtr = '\0';
547 if (mkdir(tmpPath, mode) == -1)
548 {
549 if (errno != EEXIST)
550 {
551 free(tmpPath);
552 return -1;
553 }
554 }
555 *tmpPtr = DIR_SEPARATOR_CHAR;
556 }
557 }
558 ret = mkdir(tmpPath, mode);
559 free(tmpPath);
560 return ret;
561 }
562
563 /** Get a home directory from the environment or a fallback relative to @c \$HOME.
564 * Sets @c errno to @c ENOMEM if unable to allocate duplicate string.
565 * Sets @c errno to @c EINVAL if variable is not set or empty.
566 * @param envname Name of environment variable.
567 * @param relativefallback Path starting with "/" and relative to @c \$HOME to use as fallback.
568 * @param fallbacklength @c strlen(relativefallback).
569 * @return The home directory path or @c NULL of an error occurs.
570 */
xdgGetRelativeHome(const char * envname,const char * relativefallback,unsigned int fallbacklength)571 static char * xdgGetRelativeHome(const char *envname, const char *relativefallback, unsigned int fallbacklength)
572 {
573 char *relhome;
574 if (!(relhome = xdgEnvDup(envname)) && errno != ENOMEM)
575 {
576 errno = 0;
577 const char *home;
578 unsigned int homelen;
579 if (!(home = xdgGetEnv("HOME")))
580 return NULL;
581 if (!(relhome = (char*)malloc((homelen = strlen(home))+fallbacklength+1))) return NULL;
582 memcpy(relhome, home, homelen);
583 memcpy(relhome+homelen, relativefallback, fallbacklength+1);
584 }
585 return relhome;
586 }
587
xdgDataHome(xdgHandle * handle)588 const char * xdgDataHome(xdgHandle *handle)
589 {
590 if (handle)
591 return xdgGetCache(handle)->dataHome;
592 else
593 return xdgGetRelativeHome("XDG_DATA_HOME", DefaultRelativeDataHome, sizeof(DefaultRelativeDataHome)-1);
594 }
595
xdgConfigHome(xdgHandle * handle)596 const char * xdgConfigHome(xdgHandle *handle)
597 {
598 if (handle)
599 return xdgGetCache(handle)->configHome;
600 else
601 return xdgGetRelativeHome("XDG_CONFIG_HOME", DefaultRelativeConfigHome, sizeof(DefaultRelativeConfigHome)-1);
602 }
603
xdgDataDirectories(xdgHandle * handle)604 const char * const * xdgDataDirectories(xdgHandle *handle)
605 {
606 if (handle)
607 return (const char * const *)&(xdgGetCache(handle)->searchableDataDirectories[1]);
608 else
609 return (const char * const *)xdgGetDirectoryLists("XDG_DATA_DIRS", NULL, DefaultDataDirectoriesList);
610 }
611
xdgSearchableDataDirectories(xdgHandle * handle)612 const char * const * xdgSearchableDataDirectories(xdgHandle *handle)
613 {
614 if (handle)
615 return (const char * const *)xdgGetCache(handle)->searchableDataDirectories;
616 else
617 {
618 char *datahome = (char*)xdgDataHome(NULL);
619 char **datadirs = 0;
620 if (datahome && !(datadirs = xdgGetDirectoryLists("XDG_DATA_DIRS", datahome, DefaultDataDirectoriesList)))
621 free(datahome);
622 return (const char * const *)datadirs;
623 }
624 }
625
xdgConfigDirectories(xdgHandle * handle)626 const char * const * xdgConfigDirectories(xdgHandle *handle)
627 {
628 if (handle)
629 return (const char * const *)&(xdgGetCache(handle)->searchableConfigDirectories[1]);
630 else
631 return (const char * const *)xdgGetDirectoryLists("XDG_CONFIG_DIRS", NULL, DefaultConfigDirectoriesList);
632 }
633
xdgSearchableConfigDirectories(xdgHandle * handle)634 const char * const * xdgSearchableConfigDirectories(xdgHandle *handle)
635 {
636 if (handle)
637 return (const char * const *)xdgGetCache(handle)->searchableConfigDirectories;
638 else
639 {
640 char *confighome = (char*)xdgConfigHome(NULL);
641 char **configdirs = 0;
642 if (confighome && !(configdirs = xdgGetDirectoryLists("XDG_CONFIG_DIRS", confighome, DefaultConfigDirectoriesList)))
643 free(confighome);
644 return (const char * const *)configdirs;
645 }
646 }
647
xdgCacheHome(xdgHandle * handle)648 const char * xdgCacheHome(xdgHandle *handle)
649 {
650 if (handle)
651 return xdgGetCache(handle)->cacheHome;
652 else
653 return xdgGetRelativeHome("XDG_CACHE_HOME", DefaultRelativeCacheHome, sizeof(DefaultRelativeCacheHome)-1);
654 }
655
xdgRuntimeDirectory(xdgHandle * handle)656 const char * xdgRuntimeDirectory(xdgHandle *handle)
657 {
658 if (handle)
659 return xdgGetCache(handle)->runtimeDirectory;
660 else
661 return xdgEnvDup("XDG_RUNTIME_DIRECTORY");
662 }
663
xdgDataFind(const char * relativePath,xdgHandle * handle)664 char * xdgDataFind(const char * relativePath, xdgHandle *handle)
665 {
666 const char * const * dirs = xdgSearchableDataDirectories(handle);
667 char * result = xdgFindExisting(relativePath, dirs);
668 if (!handle) xdgFreeStringList((char**)dirs);
669 return result;
670 }
671
xdgConfigFind(const char * relativePath,xdgHandle * handle)672 char * xdgConfigFind(const char * relativePath, xdgHandle *handle)
673 {
674 const char * const * dirs = xdgSearchableConfigDirectories(handle);
675 char * result = xdgFindExisting(relativePath, dirs);
676 if (!handle) xdgFreeStringList((char**)dirs);
677 return result;
678 }
679
xdgDataOpen(const char * relativePath,const char * mode,xdgHandle * handle)680 FILE * xdgDataOpen(const char * relativePath, const char * mode, xdgHandle *handle)
681 {
682 const char * const * dirs = xdgSearchableDataDirectories(handle);
683 FILE * result = xdgFileOpen(relativePath, mode, dirs);
684 if (!handle) xdgFreeStringList((char**)dirs);
685 return result;
686 }
687
xdgConfigOpen(const char * relativePath,const char * mode,xdgHandle * handle)688 FILE * xdgConfigOpen(const char * relativePath, const char * mode, xdgHandle *handle)
689 {
690 const char * const * dirs = xdgSearchableConfigDirectories(handle);
691 FILE * result = xdgFileOpen(relativePath, mode, dirs);
692 if (!handle) xdgFreeStringList((char**)dirs);
693 return result;
694 }
695