simple-xdg-bdirs
simple-xdg-bdirs.h is a single header file which implements calculating file paths according to the XDG Base Directory Specification.
brief summary
the XDG Base Directory Specification is a popular specification which defines predictable locations for programs to store their config, runtime, cache, and data files. these locations are calculated based on specially named environment variables, with certain directories defined as default fallbacks.
this single-header library provides a couple of simple functions to calculate those locations at runtime, as well as functions to build file paths against them.
example
the following example file is available as example.c, which you can compile and play with yourself.
#include <stdlib.h>
#include <stdio.h>
#include "../simple-xdg-bdirs.h"
/* a simple example which attempts to read a value from a configuration file
* and write it back to a cache file */
int main(int argc, char *argv[])
{
char *s, *wdir, **rdirs, **cur;
/* alloc a null-terminated array of directories in which to search for
* configuration files */
rdirs = simple_xdg_bdirs_read_dirs(SIMPLE_XDG_BDIRS_CONFIG);
/* in case of error, errno is set, so perror works */
if (rdirs == NULL) {
perror(NULL);
return 1;
}
/* search in the config directories for the given relative path. if not
* found in the first, the second will be checked as a fallback etc */
s = simple_xdg_bdirs_fullpath_read("xdg_bdirs_test/config.conf", rdirs);
/* element strings of rdirs must also be freed */
for (cur = rdirs; *cur != NULL; cur++)
free(*cur);
free(rdirs);
/* check for errors again */
if (s == NULL) {
perror(NULL);
return 1;
}
/* print the fullpath of the file that was found */
puts(s);
free(s);
/* locate the directory into which runtime files should be written
* (things like sockets or lock files) */
wdir = simple_xdg_bdirs_write_dir(SIMPLE_XDG_BDIRS_RUNTIME);
if (wdir == NULL) {
perror(NULL);
return 1;
}
/* a convenience function that builds a fullpath from a relative path and
* write directory */
s = simple_xdg_bdirs_fullpath_write("xdg_bdirs_test.lock", wdir);
free(wdir);
if (s == NULL) {
perror(NULL);
return 1;
}
puts(s);
free(s);
return 0;
}
basically, each function returns either a string or NULL
, in which case
errno
will also be set. simple!
interface
enum simple_xdg_bdirs_ftype {
SIMPLE_XDG_BDIRS_DATA,
SIMPLE_XDG_BDIRS_CONFIG,
SIMPLE_XDG_BDIRS_CACHE,
SIMPLE_XDG_BDIRS_RUNTIME,
};
one of these types is passed to each of the functions below, denoting the category of file to which the functions actions ought to correspond.
SIMPLE_XDG_BDIRS_DATA
is used for persistent data created by a programSIMPLE_XDG_BDIRS_CONFIG
is used for a program's configuration filesSIMPLE_XDG_BDIRS_CACHE
is used for transient data which may be deleted without warning by the systemSIMPLE_XDG_BDIRS_RUNTIME
is used for "runtime files", such as lock files or sockets
static char*
simple_xdg_bdirs_write_dir(enum simple_xdg_bdirs_ftype type)
this function finds and returns a single string that is the path of a
directory into which all files of type type
ought to be written.
because the location of this directory is determined at runtime, and in
particular because it relies on the state of the filesystem and environment
variables, it is possible that no good candidate directory will be found, in
which case NULL
will be returned.
if a string is returned, it is guaranteed to always have a trailing '/', meaning that a relative path can be appended directly without fear of unintended results, like '/etx/xdgherbstluftwm'
errors:
- ENOENT
: no candidate dir found
- EINVAL
: bad argument
- EOVERFLOW
: extremely-large string encountered (unlikely)
- ENOMEM
: malloc err
static char**
simple_xdg_bdirs_read_dirs(enum simple_xdg_bdirs_ftype type)
this function finds and returns a null-terminated array of strings that are
the paths, in order, where one ought to search for files of type type
when
reading.
because some filetypes have only a single, environment-determined read dir,
it's possible that none will be found, in which case NULL
will be
returned.
returned strings are guaranteed to always have a trailing '/', meaning that a relative path can be appended directly without fear of unintended results, like '/etx/xdgherbstluftwm'
errors:
- ENOENT
: no candidate dirs found
- EINVAL
: bad argument
- EOVERFLOW
: extremely-large string encountered (unlikely)
- ENOMEM
: malloc err
static char*
simple_xdg_bdirs_fullpath_read(const char *rel_path, char **read_dirs)
take a relative path and set of directories returned from
simple_xdg_bdirs_read_dirs
and return either a full path to an existing
file or, on error, NULL
errors
- ENOENT
: target file not found or readable
- EINVAL
: bad argument
- EOVERFLOW
: extremely-large string encountered (unlikely)
- ENOMEM
: malloc err
static char*
simple_xdg_bdirs_fullpath_write(const char *rel_path, char *write_dir)
take a relative path and a write directory returned from
simple_xdg_bdirs_write_dir
and return either a full path to the targeted
write path or, on error, NULL
errors
- ENOENT
: write_dir is not found or not write accessible
- EINVAL
: bad argument
- EOVERFLOW
: extremely-large string encountered (unlikely)
- ENOMEM
: malloc err