libxdg-basedir-1.0.2  1.0.2
basedir.c
Go to the documentation of this file.
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 
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 
47 #ifdef FALSE
48 #undef FALSE
49 #endif
50 #ifdef TRUE
51 #undef TRUE
52 #endif
53 #define FALSE 0
54 #define TRUE 1
55 
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)
62 {
63  while (n > 0) { ((char*)p)[n] = 0; ++n; }
64 }
65 #endif
66 
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
82 
83 #include <basedir.h>
84 #include <basedir_fs.h>
85 
86 #ifndef MAX
87 #define MAX(a, b) ((b) > (a) ? (b) : (a))
88 #endif
89 
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";
97 
98 static const char
99  *DefaultDataDirectoriesList[] = { DefaultDataDirectories1, DefaultDataDirectories2, NULL },
100  *DefaultConfigDirectoriesList[] = { DefaultConfigDirectories, NULL };
101 
102 typedef struct _xdgCachedData
103 {
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;
114 
116 static xdgCachedData* xdgGetCache(xdgHandle *handle)
117 {
118  return ((xdgCachedData*)(handle->reserved));
119 }
120 
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 
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 
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[0] != cache->dataHome)
147  free(cache->dataHome);
148  cache->dataHome = 0;
149  }
150  if (cache->configHome);
151  {
152  if (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  xdgFreeStringList(cache->searchableDataDirectories);
162  cache->searchableDataDirectories = 0;
163  xdgFreeStringList(cache->searchableConfigDirectories);
164  cache->searchableConfigDirectories = 0;
165 }
166 
168 {
169  xdgCachedData* cache = xdgGetCache(handle);
170  xdgFreeData(cache);
171  free(cache);
172 }
173 
179 static char* xdgGetEnv(const char* name, const char* defaultValue)
180 {
181  const char* env;
182  char* value;
183 
184  env = getenv(name);
185  if (env && env[0])
186  {
187  if (!(value = (char*)malloc(strlen(env)+1))) return 0;
188  strcpy(value, env);
189  }
190  else
191  {
192  if (!(value = (char*)malloc(strlen(defaultValue)+1))) return 0;
193  strcpy(value, defaultValue);
194  }
195  return value;
196 }
197 
201 static char** xdgSplitPath(const char* string)
202 {
203  unsigned int size, i, j, k;
204  char** itemlist;
205 
206  /* Get the number of paths */
207  size=2; /* One item more than seperators + terminating null item */
208  for (i = 0; string[i]; ++i)
209  {
210 #ifndef NO_ESCAPES_IN_PATHS
211  if (string[i] == '\\' && string[i+1])
212  {
213  /* skip escaped characters including seperators */
214  ++i;
215  continue;
216  }
217 #endif
218  if (string[i] == PATH_SEPARATOR_CHAR) ++size;
219  }
220 
221  if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return 0;
222  xdgZeroMemory(itemlist, sizeof(char*)*size);
223 
224  for (i = 0; *string; ++i)
225  {
226  /* get length of current string */
227  for (j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j)
228 #ifndef NO_ESCAPES_IN_PATHS
229  if (string[j] == '\\' && string[j+1]) ++j
230 #endif
231  ;
232 
233  if (!(itemlist[i] = (char*)malloc(j+1))) { xdgFreeStringList(itemlist); return 0; }
234 
235  /* transfer string, unescaping any escaped seperators */
236  for (k = j = 0; string[j] && string[j] != PATH_SEPARATOR_CHAR; ++j, ++k)
237  {
238 #ifndef NO_ESCAPES_IN_PATHS
239  if (string[j] == '\\' && string[j+1] == PATH_SEPARATOR_CHAR) ++j; /* replace escaped ':' with just ':' */
240  else if (string[j] == '\\' && string[j+1]) /* skip escaped characters so escaping remains aligned to pairs. */
241  {
242  itemlist[i][k]=string[j];
243  ++j, ++k;
244  }
245 #endif
246  itemlist[i][k] = string[j];
247  }
248  itemlist[i][k] = 0; /* Bugfix provided by Diego 'Flameeyes' Pettenò */
249  /* move to next string */
250  string += j;
251  if (*string == PATH_SEPARATOR_CHAR) string++; /* skip seperator */
252  }
253  return itemlist;
254 }
255 
261 static char** xdgGetPathListEnv(const char* name, const char ** strings)
262 {
263  const char* env;
264  char* item;
265  char** itemlist;
266  int i, size;
267 
268  env = getenv(name);
269  if (env && env[0])
270  {
271  if (!(item = (char*)malloc(strlen(env)+1))) return NULL;
272  strcpy(item, env);
273 
274  itemlist = xdgSplitPath(item);
275  free(item);
276  }
277  else
278  {
279  if (!strings) return NULL;
280  for (size = 0; strings[size]; ++size) ; ++size;
281  if (!(itemlist = (char**)malloc(sizeof(char*)*size))) return NULL;
282  xdgZeroMemory(itemlist, sizeof(char*)*(size));
283 
284  /* Copy defaults into itemlist. */
285  /* Why all this funky stuff? So the result can be handled uniformly by xdgFreeStringList. */
286  for (i = 0; strings[i]; ++i)
287  {
288  if (!(item = (char*)malloc(strlen(strings[i])+1))) { xdgFreeStringList(itemlist); return NULL; }
289  strcpy(item, strings[i]);
290  itemlist[i] = item;
291  }
292  }
293  return itemlist;
294 }
295 
300 static int xdgUpdateHomeDirectories(xdgCachedData* cache)
301 {
302  const char* env;
303  char* home, *defVal;
304 
305  env = getenv("HOME");
306  if (!env || !env[0])
307  return FALSE;
308  if (!(home = (char*)malloc(strlen(env)+1))) return FALSE;
309  strcpy(home, env);
310 
311  /* Allocate maximum needed for any of the 3 default values */
312  defVal = (char*)malloc(strlen(home)+
313  MAX(MAX(sizeof(DefaultRelativeDataHome), sizeof(DefaultRelativeConfigHome)), sizeof(DefaultRelativeCacheHome)));
314  if (!defVal) return FALSE;
315 
316  strcpy(defVal, home);
317  strcat(defVal, DefaultRelativeDataHome);
318  if (!(cache->dataHome = xdgGetEnv("XDG_DATA_HOME", defVal))) return FALSE;
319 
320  defVal[strlen(home)] = 0;
321  strcat(defVal, DefaultRelativeConfigHome);
322  if (!(cache->configHome = xdgGetEnv("XDG_CONFIG_HOME", defVal))) return FALSE;
323 
324  defVal[strlen(home)] = 0;
325  strcat(defVal, DefaultRelativeCacheHome);
326  if (!(cache->cacheHome = xdgGetEnv("XDG_CACHE_HOME", defVal))) return FALSE;
327 
328  free(defVal);
329  free(home);
330 
331  return TRUE;
332 }
333 
338 static int xdgUpdateDirectoryLists(xdgCachedData* cache)
339 {
340  char** itemlist;
341  int size;
342 
343  itemlist = xdgGetPathListEnv("XDG_DATA_DIRS", DefaultDataDirectoriesList);
344 
345  if (!itemlist) return FALSE;
346  for (size = 0; itemlist[size]; size++) ; /* Get list size */
347  if (!(cache->searchableDataDirectories = (char**)malloc(sizeof(char*)*(size+2))))
348  {
349  xdgFreeStringList(itemlist);
350  return FALSE;
351  }
352  /* "home" directory has highest priority according to spec */
353  cache->searchableDataDirectories[0] = cache->dataHome;
354  memcpy(&(cache->searchableDataDirectories[1]), itemlist, sizeof(char*)*(size+1));
355  free(itemlist);
356 
357  itemlist = xdgGetPathListEnv("XDG_CONFIG_DIRS", DefaultConfigDirectoriesList);
358  if (!itemlist) return FALSE;
359  for (size = 0; itemlist[size]; size++) ; /* Get list size */
360  if (!(cache->searchableConfigDirectories = (char**)malloc(sizeof(char*)*(size+2))))
361  {
362  xdgFreeStringList(itemlist);
363  return FALSE;
364  }
365  cache->searchableConfigDirectories[0] = cache->configHome;
366  memcpy(&(cache->searchableConfigDirectories[1]), itemlist, sizeof(char*)*(size+1));
367  free(itemlist);
368 
369  return TRUE;
370 }
371 
373 {
374  xdgCachedData* cache = (xdgCachedData*)malloc(sizeof(xdgCachedData));
375  xdgCachedData* oldCache;
376  if (!cache) return FALSE;
377  xdgZeroMemory(cache, sizeof(xdgCachedData));
378 
379  if (xdgUpdateHomeDirectories(cache) &&
381  {
382  /* Update successful, replace pointer to old cache with pointer to new cache */
383  oldCache = xdgGetCache(handle);
384  handle->reserved = cache;
385  if (oldCache)
386  {
387  xdgFreeData(oldCache);
388  free(oldCache);
389  }
390  return TRUE;
391  }
392  else
393  {
394  /* Update failed, discard new cache and leave old cache unmodified */
395  xdgFreeData(cache);
396  free(cache);
397  return FALSE;
398  }
399 }
400 
407 static char * xdgFindExisting(const char * relativePath, const char * const * dirList)
408 {
409  char * fullPath;
410  char * returnString = 0;
411  char * tmpString;
412  int strLen = 0;
413  FILE * testFile;
414  const char * const * item;
415 
416  for (item = dirList; *item; item++)
417  {
418  if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
419  {
420  if (returnString) free(returnString);
421  return 0;
422  }
423  strcpy(fullPath, *item);
424  if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
425  strcat(fullPath, DIR_SEPARATOR_STR);
426  strcat(fullPath, relativePath);
427  testFile = fopen(fullPath, "r");
428  if (testFile)
429  {
430  if (!(tmpString = (char*)realloc(returnString, strLen+strlen(fullPath)+2)))
431  {
432  free(returnString);
433  free(fullPath);
434  return 0;
435  }
436  returnString = tmpString;
437  strcpy(&returnString[strLen], fullPath);
438  strLen = strLen+strlen(fullPath)+1;
439  fclose(testFile);
440  }
441  free(fullPath);
442  }
443  if (returnString)
444  returnString[strLen] = 0;
445  else
446  {
447  if ((returnString = (char*)malloc(2)))
448  strcpy(returnString, "\0");
449  }
450  return returnString;
451 }
452 
459 static FILE * xdgFileOpen(const char * relativePath, const char * mode, const char * const * dirList)
460 {
461  char * fullPath;
462  FILE * testFile;
463  const char * const * item;
464 
465  for (item = dirList; *item; item++)
466  {
467  if (!(fullPath = (char*)malloc(strlen(*item)+strlen(relativePath)+2)))
468  return 0;
469  strcpy(fullPath, *item);
470  if (fullPath[strlen(fullPath)-1] != DIR_SEPARATOR_CHAR)
471  strcat(fullPath, DIR_SEPARATOR_STR);
472  strcat(fullPath, relativePath);
473  testFile = fopen(fullPath, mode);
474  free(fullPath);
475  if (testFile)
476  return testFile;
477  }
478  return 0;
479 }
480 
481 int xdgMakePath(const char * path, mode_t mode)
482 {
483  int length = strlen(path);
484  char * tmpPath;
485  char * tmpPtr;
486  int ret;
487 
488  if (length == 0 || (length == 1 && path[0] == DIR_SEPARATOR_CHAR))
489  return 0;
490 
491  if (!(tmpPath = (char*)malloc(length+1)))
492  {
493  errno = ENOMEM;
494  return -1;
495  }
496  strcpy(tmpPath, path);
497  if (tmpPath[length-1] == DIR_SEPARATOR_CHAR)
498  tmpPath[length-1] = '\0';
499 
500  /* skip tmpPath[0] since if it's a seperator we have an absolute path */
501  for (tmpPtr = tmpPath+1; *tmpPtr; ++tmpPtr)
502  {
503  if (*tmpPtr == DIR_SEPARATOR_CHAR)
504  {
505  *tmpPtr = '\0';
506  if (mkdir(tmpPath, mode) == -1)
507  {
508  if (errno != EEXIST)
509  {
510  free(tmpPath);
511  return -1;
512  }
513  }
514  *tmpPtr = DIR_SEPARATOR_CHAR;
515  }
516  }
517  ret = mkdir(tmpPath, mode);
518  free(tmpPath);
519  return ret;
520 }
521 
522 const char * xdgDataHome(xdgHandle *handle)
523 {
524  return xdgGetCache(handle)->dataHome;
525 }
526 const char * xdgConfigHome(xdgHandle *handle)
527 {
528  return xdgGetCache(handle)->configHome;
529 }
530 const char * const * xdgDataDirectories(xdgHandle *handle)
531 {
532  return (const char * const *)&(xdgGetCache(handle)->searchableDataDirectories[1]);
533 }
534 const char * const * xdgSearchableDataDirectories(xdgHandle *handle)
535 {
536  return (const char * const *)xdgGetCache(handle)->searchableDataDirectories;
537 }
538 const char * const * xdgConfigDirectories(xdgHandle *handle)
539 {
540  return (const char * const *)&(xdgGetCache(handle)->searchableConfigDirectories[1]);
541 }
542 const char * const * xdgSearchableConfigDirectories(xdgHandle *handle)
543 {
544  return (const char * const *)xdgGetCache(handle)->searchableConfigDirectories;
545 }
546 const char * xdgCacheHome(xdgHandle *handle)
547 {
548  return xdgGetCache(handle)->cacheHome;
549 }
550 char * xdgDataFind(const char * relativePath, xdgHandle *handle)
551 {
552  return xdgFindExisting(relativePath, xdgSearchableDataDirectories(handle));
553 }
554 char * xdgConfigFind(const char * relativePath, xdgHandle *handle)
555 {
556  return xdgFindExisting(relativePath, xdgSearchableConfigDirectories(handle));
557 }
558 FILE * xdgDataOpen(const char * relativePath, const char * mode, xdgHandle *handle)
559 {
560  return xdgFileOpen(relativePath, mode, xdgSearchableDataDirectories(handle));
561 }
562 FILE * xdgConfigOpen(const char * relativePath, const char * mode, xdgHandle *handle)
563 {
564  return xdgFileOpen(relativePath, mode, xdgSearchableConfigDirectories(handle));
565 }
566 
const char * xdgDataHome(xdgHandle *handle)
Base directory for user specific data files.
Definition: basedir.c:522
static int xdgUpdateDirectoryLists(xdgCachedData *cache)
Update all *Directories variables of cache.
Definition: basedir.c:338
const char *const * xdgConfigDirectories(xdgHandle *handle)
Preference-ordered set of base directories to search for configuration files in addition to the $XDG_...
Definition: basedir.c:538
void * reserved
Reserved for internal use, do not modify.
Definition: basedir.h:61
FILE * xdgDataOpen(const char *relativePath, const char *mode, xdgHandle *handle)
Open first possible data file corresponding to relativePath.
Definition: basedir.c:558
char * xdgConfigFind(const char *relativePath, xdgHandle *handle)
Find all existing config files corresponding to relativePath.
Definition: basedir.c:554
static xdgCachedData * xdgGetCache(xdgHandle *handle)
Get cache object associated with a handle.
Definition: basedir.c:116
Handle to XDG data cache.
Definition: basedir.h:59
static int xdgUpdateHomeDirectories(xdgCachedData *cache)
Update all *Home variables of cache.
Definition: basedir.c:300
int xdgUpdateData(xdgHandle *handle)
Update the data cache.
Definition: basedir.c:372
const char * xdgCacheHome(xdgHandle *handle)
Base directory for user specific non-essential data files.
Definition: basedir.c:546
int xdgMakePath(const char *path, mode_t mode)
Create path by recursively creating directories.
Definition: basedir.c:481
static FILE * xdgFileOpen(const char *relativePath, const char *mode, const char *const *dirList)
Open first possible config file corresponding to relativePath.
Definition: basedir.c:459
Filesystem functions related to the XDG Base Directory specification.
const char *const * xdgSearchableDataDirectories(xdgHandle *handle)
Preference-ordered set of base directories to search for data files with $XDG_DATA_HOME prepended...
Definition: basedir.c:534
char * xdgDataFind(const char *relativePath, xdgHandle *handle)
Find all existing data files corresponding to relativePath.
Definition: basedir.c:550
static char * xdgGetEnv(const char *name, const char *defaultValue)
Get value for environment variable $name, defaulting to "defaultValue".
Definition: basedir.c:179
Functions for using the XDG Base Directory specification.
void xdgWipeHandle(xdgHandle *handle)
Wipe handle of XDG data cache.
Definition: basedir.c:167
static char ** xdgGetPathListEnv(const char *name, const char **strings)
Get $PATH-style environment variable as list of strings.
Definition: basedir.c:261
const char *const * xdgDataDirectories(xdgHandle *handle)
Preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME...
Definition: basedir.c:530
const char * xdgConfigHome(xdgHandle *handle)
Base directory for user specific configuration files.
Definition: basedir.c:526
static void xdgFreeStringList(char **list)
Free all memory used by a NULL-terminated string list.
Definition: basedir.c:131
static char ** xdgSplitPath(const char *string)
Split string at &#39;:&#39;, return null-terminated list of resulting strings.
Definition: basedir.c:201
static void xdgFreeData(xdgCachedData *cache)
Free all data in the cache and set pointers to null.
Definition: basedir.c:141
static char * xdgFindExisting(const char *relativePath, const char *const *dirList)
Find all existing files corresponding to relativePath relative to each item in dirList.
Definition: basedir.c:407
FILE * xdgConfigOpen(const char *relativePath, const char *mode, xdgHandle *handle)
Open first possible config file corresponding to relativePath.
Definition: basedir.c:562
const char *const * xdgSearchableConfigDirectories(xdgHandle *handle)
Preference-ordered set of base directories to search for configuration files with $XDG_CONFIG_HOME pr...
Definition: basedir.c:542
xdgHandle * xdgInitHandle(xdgHandle *handle)
Initialize a handle to an XDG data cache and initialize the cache.
Definition: basedir.c:121