aboutsummaryrefslogtreecommitdiffstats

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 program
  • SIMPLE_XDG_BDIRS_CONFIG is used for a program's configuration files
  • SIMPLE_XDG_BDIRS_CACHE is used for transient data which may be deleted without warning by the system
  • SIMPLE_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