Fixed potential memory leaks in xdg{Data,Config}{Find,Open} functions.
[libxdg-basedir.git] / src / basedir.c
blob551d9ef6ebaa7fce78293f55f37f4f68e5310753
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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.
25 /** @file basedir.c
26 * @brief Implementation of the XDG Base Directory specification. */
28 #if defined(HAVE_CONFIG_H) || defined(_DOXYGEN)
29 #include <config.h>
30 #endif
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
45 #include <errno.h>
47 #ifdef FALSE
48 #undef FALSE
49 #endif
50 #ifdef TRUE
51 #undef TRUE
52 #endif
53 #define FALSE 0
54 #define TRUE 1
56 #if HAVE_MEMSET || !defined(HAVE_CONFIG_H)
57 # define xdgZeroMemory(p, n) memset(p, 0, n)
58 #elif HAVE_BZERO
59 # define xdgZeroMemory(p, n) bzero(p, n)
60 #else
61 static void xdgZeroMemory(void* p, size_t n)
63 while (n > 0) { ((char*)p)[n] = 0; ++n; }
65 #endif
67 #if defined _WIN32 && !defined __CYGWIN__
68 /* Use Windows separators on all _WIN32 defining
69 environments, except Cygwin. */
70 # define DIR_SEPARATOR_CHAR '\\'
71 # define DIR_SEPARATOR_STR "\\"
72 # define PATH_SEPARATOR_CHAR ';'
73 # define PATH_SEPARATOR_STR ";"
74 # define NO_ESCAPES_IN_PATHS
75 #else
76 # define DIR_SEPARATOR_CHAR '/'
77 # define DIR_SEPARATOR_STR "/"
78 # define PATH_SEPARATOR_CHAR ':'
79 # define PATH_SEPARATOR_STR ":"
80 # define NO_ESCAPES_IN_PATHS
81 #endif
83 #include <basedir.h>
84 #include <basedir_fs.h>
86 #ifndef MAX
87 #define MAX(a, b) ((b) > (a) ? (b) : (a))
88 #endif
90 static const char
91 DefaultRelativeDataHome[] = DIR_SEPARATOR_STR ".local" DIR_SEPARATOR_STR "share",
92 DefaultRelativeConfigHome[] = DIR_SEPARATOR_STR ".config",
93 DefaultDataDirectories1[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "local" DIR_SEPARATOR_STR "share",
94 DefaultDataDirectories2[] = DIR_SEPARATOR_STR "usr" DIR_SEPARATOR_STR "share",
95 DefaultConfigDirectories[] = DIR_SEPARATOR_STR "etc" DIR_SEPARATOR_STR "xdg",
96 DefaultRelativeCacheHome[] = DIR_SEPARATOR_STR ".cache";
98 static const char
99 *DefaultDataDirectoriesList[] = { DefaultDataDirectories1, DefaultDataDirectories2, NULL },
100 *DefaultConfigDirectoriesList[] = { DefaultConfigDirectories, NULL };
102 typedef struct _xdgCachedData
104 char * dataHome;
105 char * configHome;
106 char * cacheHome;
107 /* Note: string lists are null-terminated and all items */
108 /* except the first are assumed to be allocated using malloc. */
109 /* The first item is assumed to be allocated by malloc only if */
110 /* it is not equal to the appropriate home directory string above. */
111 char ** searchableDataDirectories;
112 char ** searchableConfigDirectories;
113 } xdgCachedData;
115 /** Get cache object associated with a handle */
116 static xdgCachedData* xdgGetCache(xdgHandle *handle)
118 return ((xdgCachedData*)(handle->reserved));
121 xdgHandle * xdgInitHandle(xdgHandle *handle)
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;
130 /** Free all memory used by a NULL-terminated string list */
131 static void xdgFreeStringList(char** list)
133 char** ptr = list;
134 if (!list) return;
135 for (; *ptr; ptr++)
136 free(*ptr);
137 free(list);
140 /** Free all data in the cache and set pointers to null. */
141 static void xdgFreeData(xdgCachedData *cache)
143 if (cache->dataHome);
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;
150 if (cache->configHome);
152 if (cache->searchableConfigDirectories && cache->searchableConfigDirectories[0] != cache->configHome)
153 free(cache->configHome);
154 cache->configHome = 0;
156 if (cache->cacheHome)
158 free(cache->cacheHome);
159 cache->cacheHome = 0;
161 xdgFreeStringList(cache->searchableDataDirectories);
162 cache->searchableDataDirectories = 0;
163 xdgFreeStringList(cache->searchableConfigDirectories);
164 cache->searchableConfigDirectories = 0;
167 void xdgWipeHandle(xdgHandle *handle)
169 xdgCachedData* cache = xdgGetCache(handle);
170 xdgFreeData(cache);
171 free(cache);
174 /** Split string at ':', return null-terminated list of resulting strings.
175 * @param string String to be split
177 static char** xdgSplitPath(const char* string)
179 unsigned int size, i, j, k;
180 char** itemlist;
182 /* Get the number of paths */
183 size=2; /* One item more than seperators + terminating null item */
184 for (i = 0; string[i]; ++i)
186 #ifndef NO_ESCAPES_IN_PATHS
187 if (string[i] == '\\' && string[i+1])
189 /* skip escaped characters including seperators */
190 ++i;
191 continue;
193 #endif
194 if (string[i] == PATH_SEPARATOR_CHAR) ++size;
197 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return 0;
198 xdgZeroMemory(itemlist, sizeof(char*)*size);
200 for (i = 0; *string; ++i)
202 /* get length of current string */
203 for (j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j)
204 #ifndef NO_ESCAPES_IN_PATHS
205 if (string[j] == '\\' && string[j+1]) ++j
206 #endif
209 if (!(itemlist[i] = (char*)malloc(j+1))) { xdgFreeStringList(itemlist); return 0; }
211 /* transfer string, unescaping any escaped seperators */
212 for (k = j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j, ++k)
214 #ifndef NO_ESCAPES_IN_PATHS
215 if (string[j] == '\\' && string[j+1] == PATH_SEPARATOR_CHAR) ++j; /* replace escaped ':' with just ':' */
216 else if (string[j] == '\\' && string[j+1]) /* skip escaped characters so escaping remains aligned to pairs. */
218 itemlist[i][k]=string[j];
219 ++j, ++k;
221 #endif
222 itemlist[i][k] = string[j];
224 itemlist[i][k] = 0; /* Bugfix provided by Diego 'Flameeyes' Pettenò */
225 /* move to next string */
226 string += j;
227 if (*string == PATH_SEPARATOR_CHAR) string++; /* skip seperator */
229 return itemlist;
232 /** Get $PATH-style environment variable as list of strings.
233 * If $name is unset or empty, use default strings specified by variable arguments.
234 * @param name Name of environment variable
235 * @param defaults NULL-terminated list of strings to be copied and used as defaults
237 static char** xdgGetPathListEnv(const char* name, const char ** defaults)
239 const char* env;
240 char* item;
241 char** itemlist;
242 int i, size;
244 env = getenv(name);
245 if (env && env[0])
247 if (!(item = (char*)malloc(strlen(env)+1))) return NULL;
248 strcpy(item, env);
250 itemlist = xdgSplitPath(item);
251 free(item);
253 else
255 if (!defaults) return NULL;
256 for (size = 0; defaults[size]; ++size) ; ++size;
257 if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return NULL;
258 xdgZeroMemory(itemlist, sizeof(char*)*(size));
260 /* Copy defaults into itemlist. */
261 /* Why all this funky stuff? So the result can be handled uniformly by xdgFreeStringList. */
262 for (i = 0; defaults[i]; ++i)
264 if (!(item = (char*)malloc(strlen(defaults[i])+1))) { xdgFreeStringList(itemlist); return NULL; }
265 strcpy(item, defaults[i]);
266 itemlist[i] = item;
269 return itemlist;
272 /** Get value of an environment variable.
273 * Sets @c errno to @c EINVAL if variable is not set or empty.
274 * @param name Name of environment variable.
275 * @return The environment variable or NULL if an error occurs.
277 static char* xdgGetEnv(const char *name)
279 char *env = getenv(name);
280 if (env && env[0])
281 return env;
282 /* What errno signifies missing env var? */
283 errno = EINVAL;
284 return NULL;
287 /** Duplicate an environment variable.
288 * Sets @c errno to @c ENOMEM if unable to allocate duplicate string.
289 * Sets @c errno to @c EINVAL if variable is not set or empty.
290 * @return The duplicated string or NULL if an error occurs.
292 static char* xdgEnvDup(const char *name)
294 const char *env;
295 if ((env = xdgGetEnv(name)))
296 return strdup(env);
297 else
298 return NULL;
301 /** Update all *Home variables of cache.
302 * This includes xdgCachedData::dataHome, xdgCachedData::configHome and xdgCachedData::cacheHome.
303 * @param cache Data cache to be updated
305 static int xdgUpdateHomeDirectories(xdgCachedData* cache)
307 const char* homeenv;
308 char* home, *value;
309 unsigned int homelen;
310 static const unsigned int extralen =
311 MAX(MAX(sizeof(DefaultRelativeDataHome),
312 sizeof(DefaultRelativeConfigHome)),
313 sizeof(DefaultRelativeCacheHome));
315 if (!(cache->dataHome = xdgEnvDup("XDG_DATA_HOME")) && errno == ENOMEM) return FALSE;
316 if (!(cache->configHome = xdgEnvDup("XDG_CONFIG_HOME")) && errno == ENOMEM) return FALSE;
317 if (!(cache->cacheHome = xdgEnvDup("XDG_CACHE_HOME")) && errno == ENOMEM) return FALSE;
318 errno = 0;
320 if (cache->dataHome && cache->configHome && cache->cacheHome) return TRUE;
322 if (!(homeenv = xdgGetEnv("HOME")))
323 return FALSE;
325 /* Allocate maximum needed for any of the 3 default values */
326 if (!(value = (char*)malloc((homelen = strlen(homeenv))+extralen))) return FALSE;
327 memcpy(value, homeenv, homelen+1);
329 if (!cache->dataHome)
331 memcpy(value+homelen, DefaultRelativeDataHome, sizeof(DefaultRelativeDataHome));
332 cache->dataHome = strdup(value);
335 if (!cache->configHome)
337 memcpy(value+homelen, DefaultRelativeConfigHome, sizeof(DefaultRelativeConfigHome));
338 cache->configHome = strdup(value);
341 if (!cache->cacheHome)
343 memcpy(value+homelen, DefaultRelativeCacheHome, sizeof(DefaultRelativeCacheHome));
344 cache->cacheHome = strdup(value);
347 free(value);
349 /* free does not change errno, and the prev call *must* have been a strdup,
350 * so errno is already set. */
351 return cache->dataHome && cache->configHome && cache->cacheHome;
354 /** Get directory lists with initial home directory.
355 * @param envname Environment variable with colon-seperated directories.
356 * @param homedir Home directory for this directory list or NULL. This
357 * parameter should be allocated on the heap. The returned list
358 * will start with this path, and should be considered as owning
359 * the memory.
360 * @param defaults Default directories if environment variable is not set.
361 * @return An array of strings. Both the array and its contents are allocated
362 * with malloc(). The function xdgFreeStringList is provided for
363 * conveniantly free()-ing the list and all its elements.
365 static char** xdgGetDirectoryLists(const char *envname, char *homedir, const char **defaults)
367 char **envlist;
368 char **dirlist;
369 unsigned int size;
371 if (!(envlist = xdgGetPathListEnv(envname, defaults)))
372 return NULL;
374 for (size = 0; envlist[size]; size++) ; /* Get list size */
375 if (!(dirlist = (char**)malloc(sizeof(char*)*(size+1+!!homedir))))
377 xdgFreeStringList(envlist);
378 return NULL;
380 /* "home" directory has highest priority according to spec */
381 if (homedir)
382 dirlist[0] = homedir;
383 memcpy(dirlist+!!homedir, envlist, sizeof(char*)*(size+1));
384 /* only free the envlist since its elements are now referenced by dirlist */
385 free(envlist);
387 return dirlist;
390 /** Update all *Directories variables of cache.
391 * This includes xdgCachedData::searchableDataDirectories and xdgCachedData::searchableConfigDirectories.
392 * @param cache Data cache to be updated.
394 static int xdgUpdateDirectoryLists(xdgCachedData* cache)
396 if (!(cache->searchableDataDirectories = xdgGetDirectoryLists(
397 "XDG_DATA_DIRS", cache->dataHome, DefaultDataDirectoriesList)))
398 return FALSE;
399 if (!(cache->searchableConfigDirectories = xdgGetDirectoryLists(
400 "XDG_CONFIG_DIRS", cache->configHome, DefaultConfigDirectoriesList)))
401 return FALSE;
403 return TRUE;
406 int xdgUpdateData(xdgHandle *handle)
408 xdgCachedData* cache = (xdgCachedData*)malloc(sizeof(xdgCachedData));
409 xdgCachedData* oldCache;
410 if (!cache) return FALSE;
411 xdgZeroMemory(cache, sizeof(xdgCachedData));
413 if (xdgUpdateHomeDirectories(cache) &&
414 xdgUpdateDirectoryLists(cache))
416 /* Update successful, replace pointer to old cache with pointer to new cache */
417 oldCache = xdgGetCache(handle);
418 handle->reserved = cache;
419 if (oldCache)
421 xdgFreeData(oldCache);
422 free(oldCache);
424 return TRUE;
426 else
428 /* Update failed, discard new cache and leave old cache unmodified */
429 xdgFreeData(cache);
430 free(cache);
431 return FALSE;
435 /** Find all existing files corresponding to relativePath relative to each item in dirList.
436 * @param relativePath Relative path to search for.
437 * @param dirList <tt>NULL</tt>-terminated list of directory paths.
438 * @return A sequence of null-terminated strings terminated by a
439 * double-<tt>NULL</tt> (empty string) and allocated using malloc().
441 static char * xdgFindExisting(const char * relativePath, const char * const * dirList)
443 char * fullPath;
444 char * returnString = 0;
445 char * tmpString;
446 int strLen = 0;
447 FILE * testFile;
448 const char * const * item;
450 for (item = dirList; *item; item++)
452 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
454 if (returnString) free(returnString);
455 return 0;
457 strcpy(fullPath, *item);
458 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
459 strcat(fullPath, DIR_SEPARATOR_STR);
460 strcat(fullPath, relativePath);
461 testFile = fopen(fullPath, "r");
462 if (testFile)
464 if (!(tmpString = (char*)realloc(returnString, strLen+strlen(fullPath)+2)))
466 free(returnString);
467 free(fullPath);
468 return 0;
470 returnString = tmpString;
471 strcpy(&returnString[strLen], fullPath);
472 strLen = strLen+strlen(fullPath)+1;
473 fclose(testFile);
475 free(fullPath);
477 if (returnString)
478 returnString[strLen] = 0;
479 else
481 if ((returnString = (char*)malloc(2)))
482 strcpy(returnString, "\0");
484 return returnString;
487 /** Open first possible config file corresponding to relativePath.
488 * @param relativePath Path to scan for.
489 * @param mode Mode with which to attempt to open files (see fopen modes).
490 * @param dirList <tt>NULL</tt>-terminated list of paths in which to search for relativePath.
491 * @return File pointer if successful else @c NULL. Client must use @c fclose to close file.
493 static FILE * xdgFileOpen(const char * relativePath, const char * mode, const char * const * dirList)
495 char * fullPath;
496 FILE * testFile;
497 const char * const * item;
499 for (item = dirList; *item; item++)
501 if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
502 return 0;
503 strcpy(fullPath, *item);
504 if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
505 strcat(fullPath, DIR_SEPARATOR_STR);
506 strcat(fullPath, relativePath);
507 testFile = fopen(fullPath, mode);
508 free(fullPath);
509 if (testFile)
510 return testFile;
512 return 0;
515 int xdgMakePath(const char * path, mode_t mode)
517 int length = strlen(path);
518 char * tmpPath;
519 char * tmpPtr;
520 int ret;
522 if (length == 0 || (length == 1 && path[0] == DIR_SEPARATOR_CHAR))
523 return 0;
525 if (!(tmpPath = (char*)malloc(length+1)))
527 errno = ENOMEM;
528 return -1;
530 strcpy(tmpPath, path);
531 if (tmpPath[length-1] == DIR_SEPARATOR_CHAR)
532 tmpPath[length-1] = '\0';
534 /* skip tmpPath[0] since if it's a seperator we have an absolute path */
535 for (tmpPtr = tmpPath+1; *tmpPtr; ++tmpPtr)
537 if (*tmpPtr == DIR_SEPARATOR_CHAR)
539 *tmpPtr = '\0';
540 if (mkdir(tmpPath, mode) == -1)
542 if (errno != EEXIST)
544 free(tmpPath);
545 return -1;
548 *tmpPtr = DIR_SEPARATOR_CHAR;
551 ret = mkdir(tmpPath, mode);
552 free(tmpPath);
553 return ret;
556 /** Get a home directory from the environment or a fallback relative to @c \$HOME.
557 * Sets @c errno to @c ENOMEM if unable to allocate duplicate string.
558 * Sets @c errno to @c EINVAL if variable is not set or empty.
559 * @param envname Name of environment variable.
560 * @param relativefallback Path starting with "/" and relative to @c \$HOME to use as fallback.
561 * @param fallbacklength @c strlen(relativefallback).
562 * @return The home directory path or @c NULL of an error occurs.
564 static char * xdgGetRelativeHome(const char *envname, const char *relativefallback, unsigned int fallbacklength)
566 char *relhome;
567 if (!(relhome = xdgEnvDup(envname)) && errno != ENOMEM)
569 errno = 0;
570 const char *home;
571 unsigned int homelen;
572 if (!(home = xdgGetEnv("HOME")))
573 return NULL;
574 if (!(relhome = (char*)malloc((homelen = strlen(home))+fallbacklength))) return NULL;
575 memcpy(relhome, home, homelen);
576 memcpy(relhome+homelen, relativefallback, fallbacklength+1);
578 return relhome;
581 const char * xdgDataHome(xdgHandle *handle)
583 if (handle)
584 return xdgGetCache(handle)->dataHome;
585 else
586 return xdgGetRelativeHome("XDG_DATA_HOME", DefaultRelativeDataHome, sizeof(DefaultRelativeDataHome)-1);
588 const char * xdgConfigHome(xdgHandle *handle)
590 if (handle)
591 return xdgGetCache(handle)->configHome;
592 else
593 return xdgGetRelativeHome("XDG_CONFIG_HOME", DefaultRelativeConfigHome, sizeof(DefaultRelativeConfigHome)-1);
595 const char * const * xdgDataDirectories(xdgHandle *handle)
597 if (handle)
598 return (const char * const *)&(xdgGetCache(handle)->searchableDataDirectories[1]);
599 else
600 return (const char * const *)xdgGetDirectoryLists("XDG_DATA_DIRS", NULL, DefaultDataDirectoriesList);
602 const char * const * xdgSearchableDataDirectories(xdgHandle *handle)
604 if (handle)
605 return (const char * const *)xdgGetCache(handle)->searchableDataDirectories;
606 else
608 char *datahome = (char*)xdgDataHome(NULL);
609 char **datadirs = 0;
610 if (datahome && !(datadirs = xdgGetDirectoryLists("XDG_DATA_DIRS", datahome, DefaultDataDirectoriesList)))
611 free(datahome);
612 return (const char * const *)datadirs;
615 const char * const * xdgConfigDirectories(xdgHandle *handle)
617 if (handle)
618 return (const char * const *)&(xdgGetCache(handle)->searchableConfigDirectories[1]);
619 else
620 return (const char * const *)xdgGetDirectoryLists("XDG_CONFIG_DIRS", NULL, DefaultConfigDirectoriesList);
622 const char * const * xdgSearchableConfigDirectories(xdgHandle *handle)
624 if (handle)
625 return (const char * const *)xdgGetCache(handle)->searchableConfigDirectories;
626 else
628 char *confighome = (char*)xdgConfigHome(NULL);
629 char **configdirs = 0;
630 if (confighome && !(configdirs = xdgGetDirectoryLists("XDG_CONFIG_DIRS", confighome, DefaultConfigDirectoriesList)))
631 free(confighome);
632 return (const char * const *)configdirs;
635 const char * xdgCacheHome(xdgHandle *handle)
637 if (handle)
638 return xdgGetCache(handle)->cacheHome;
639 else
640 return xdgGetRelativeHome("XDG_CACHE_HOME", DefaultRelativeCacheHome, sizeof(DefaultRelativeCacheHome)-1);
642 char * xdgDataFind(const char * relativePath, xdgHandle *handle)
644 const char * const * dirs = xdgSearchableDataDirectories(handle);
645 char * result = xdgFindExisting(relativePath, dirs);
646 if (!handle) xdgFreeStringList((char**)dirs);
647 return result;
649 char * xdgConfigFind(const char * relativePath, xdgHandle *handle)
651 const char * const * dirs = xdgSearchableConfigDirectories(handle);
652 char * result = xdgFindExisting(relativePath, dirs);
653 if (!handle) xdgFreeStringList((char**)dirs);
654 return result;
656 FILE * xdgDataOpen(const char * relativePath, const char * mode, xdgHandle *handle)
658 const char * const * dirs = xdgSearchableDataDirectories(handle);
659 FILE * result = xdgFileOpen(relativePath, mode, dirs);
660 if (!handle) xdgFreeStringList((char**)dirs);
661 return result;
663 FILE * xdgConfigOpen(const char * relativePath, const char * mode, xdgHandle *handle)
665 const char * const * dirs = xdgSearchableConfigDirectories(handle);
666 FILE * result = xdgFileOpen(relativePath, mode, dirs);
667 if (!handle) xdgFreeStringList((char**)dirs);
668 return result;