summaryrefslogtreecommitdiff
path: root/src/paths.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/paths.c')
-rw-r--r--src/paths.c1029
1 files changed, 0 insertions, 1029 deletions
diff --git a/src/paths.c b/src/paths.c
deleted file mode 100644
index 1bdc4e3..0000000
--- a/src/paths.c
+++ /dev/null
@@ -1,1029 +0,0 @@
-#include "common.h"
-
-#include "paths.h"
-#include "ref.h"
-#include "thread.h"
-#include "safe_fifo.h"
-#include "strutil.h"
-
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <glob.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#define SEARCH_CACHE_SIZE (2)
-
-typedef struct search_t
-{
- const char* dir;
- const char** dirs;
- char* match;
- const char* match_name;
- bool plain;
- paths_find_callback_t find_callback;
- void* userdata;
-} search_t;
-
-typedef struct searchargs_t
-{
- bool quit;
- safe_fifo_t* queue;
- thread_lock_t* lock;
- search_t* cache[SEARCH_CACHE_SIZE];
-} searchargs_t;
-
-struct paths_t
-{
- REF_DECLARE();
- char* user_data;
- char* user_config;
- char* user_cache;
- char* user_runtime;
- char** search_data;
- char** search_config;
-
- searchargs_t* search;
-#ifdef DEBUG
- thread_t* searcher;
-#endif
-};
-
-static void free_list(char** list);
-static void free_search_data(search_t* search);
-static bool paths_init(paths_t* paths);
-static char* build_path(paths_t* paths, paths_source_t source, ...);
-static bool create_dir(paths_t* paths, paths_source_t source,
- const char* addon, size_t addon_len);
-static void* searcher(void* _args);
-static search_t* get_search(searchargs_t* args);
-static void put_search(searchargs_t* args, search_t* search);
-
-paths_t* paths_new(void)
-{
- paths_t* paths = calloc(1, sizeof(paths_t));
- if (!paths)
- {
- return NULL;
- }
- if (!REF_INIT(paths))
- {
- free(paths);
- return NULL;
- }
- for (;;)
- {
- paths->search = calloc(1, sizeof(searchargs_t));
- if (!paths->search)
- {
- break;
- }
- paths->search->lock = thread_lock_new();
- if (!paths->search->lock)
- {
- break;
- }
- paths->search->queue = safe_fifo_new();
- if (!paths->search->queue)
- {
- break;
- }
- safe_fifo_ref(paths->search->queue);
-#ifdef DEBUG
- paths->searcher = thread_joinable_new(searcher, paths->search);
- if (!paths->searcher)
- {
- safe_fifo_unref(paths->search->queue);
- break;
- }
-#else
- if (!thread_new(searcher, paths->search))
- {
- safe_fifo_unref(paths->search->queue);
- break;
- }
-#endif
- if (!paths_init(paths))
- {
- break;
- }
- return paths;
- }
- paths_unref(paths);
- return NULL;
-}
-
-void paths_ref(paths_t* paths)
-{
- assert(paths);
- REF_INC(paths);
-}
-
-void paths_unref(paths_t* paths)
-{
- if (paths && REF_DEC(paths))
- {
- if (paths->search)
- {
- if (paths->search->queue)
- {
- search_t* search = get_search(paths->search);
- paths->search->quit = true;
- if (search)
- {
- memset(search, 0, sizeof(search_t));
- safe_fifo_push(paths->search->queue, search);
- }
- safe_fifo_unref(paths->search->queue);
- }
-#ifdef DEBUG
- thread_join(paths->searcher);
-#endif
- }
- free(paths->user_data);
- free(paths->user_config);
- free(paths->user_cache);
- free(paths->user_runtime);
- free_list(paths->search_data);
- free_list(paths->search_config);
- REF_FREE(paths);
- free(paths);
- }
-}
-
-const char* paths_user_dir(paths_t* paths, paths_source_t source)
-{
- assert(paths);
- switch (source)
- {
- case PATHS_DATA:
- return paths->user_data;
- case PATHS_CONFIG:
- return paths->user_config;
- case PATHS_CACHE:
- return paths->user_cache;
- case PATHS_RUNTIME:
- return paths->user_runtime;
- }
- assert(false);
- return NULL;
-}
-
-const char** paths_search_dirs(paths_t* paths, paths_source_t source)
-{
- assert(paths);
- switch (source)
- {
- case PATHS_DATA:
- return (const char**)paths->search_data;
- case PATHS_CONFIG:
- return (const char**)paths->search_config;
- case PATHS_CACHE:
- case PATHS_RUNTIME:
- return NULL;
- }
- assert(false);
- return NULL;
-}
-
-struct paths_file_t
-{
- paths_source_t source;
- char* path, * target;
- int fd;
-};
-
-paths_file_t* paths_write(paths_t* paths, paths_source_t source,
- const char* filename, unsigned int flags, ...)
-{
- paths_file_t* file;
- int f;
- const char* subpath;
- assert(filename);
- if (source == PATHS_RUNTIME && !paths->user_runtime)
- {
- errno = ENOENT;
- return NULL;
- }
- if (filename[0] == '/')
- {
- filename++;
- }
- subpath = strrchr(filename, '/');
- if (subpath)
- {
- if (!create_dir(paths, source, filename, subpath - filename))
- {
- return NULL;
- }
- }
- else
- {
- if (!create_dir(paths, source, NULL, 0))
- {
- return NULL;
- }
- }
- file = calloc(1, sizeof(paths_file_t));
- if (!file)
- {
- return NULL;
- }
- file->source = source;
- if (source == PATHS_CONFIG &&
- (((flags & PATHS_CREATE) && (flags & PATHS_TRUNCATE)) ||
- ((flags & PATHS_CREATE) && (flags & PATHS_EXCLUSIVE))))
- {
- file->target = build_path(paths, source, filename, NULL);
- if (!file->target)
- {
- free(file);
- return NULL;
- }
- file->path = build_path(paths, source, ".#", filename, NULL);
- }
- else
- {
- file->path = build_path(paths, source, filename, NULL);
- }
- if (!file->path)
- {
- free(file->target);
- free(file);
- return NULL;
- }
- f = O_WRONLY;
- if ((flags & PATHS_APPEND))
- {
- f |= O_APPEND;
- }
- if ((flags & PATHS_CREATE))
- {
- f |= O_CREAT;
- if ((flags & PATHS_EXCLUSIVE))
- {
- f |= O_EXCL;
- }
- }
- if ((flags & PATHS_TRUNCATE))
- {
- f |= O_TRUNC;
- }
-#ifdef O_EXLOCK
- f |= O_EXLOCK;
-#endif
- if ((f & O_CREAT))
- {
- va_list args;
- va_start(args, flags);
- file->fd = open(file->path, f, va_arg(args, int));
- va_end(args);
- }
- else
- {
- file->fd = open(file->path, f);
- }
- if (file->fd < 0)
- {
- free(file->path);
- free(file->target);
- free(file);
- return NULL;
- }
-#ifndef O_EXLOCK
- for (;;)
- {
- if (flock(file->fd, LOCK_EX) == 0)
- {
- break;
- }
- if (errno != EINTR)
- {
- if ((flags & O_CREAT))
- {
- unlink(file->path);
- }
- close(file->fd);
- free(file->path);
- free(file->target);
- free(file);
- return NULL;
- }
- }
-#endif
- return file;
-}
-
-ssize_t paths_file_write(paths_file_t* file,
- const void* data, size_t size)
-{
- size_t pos = 0;
- assert(file);
- assert(size == 0 || data);
- while (pos < size)
- {
- ssize_t ret = write(file->fd, data + pos, size - pos);
- if (ret < 0)
- {
- if (errno == EAGAIN || errno == EWOULDBLOCK ||
- errno == EINTR)
- {
- /* Non fatal errors */
- if (pos > 0)
- {
- return pos;
- }
- }
- return -1;
- }
- if (ret == 0)
- {
- return pos;
- }
- pos += ret;
- }
- return size;
-}
-
-int paths_file_close(paths_file_t* file)
-{
- assert(file);
- if (close(file->fd))
- {
- return -1;
- }
- file->fd = -1;
- if (file->target)
- {
- size_t len = strlen(file->target);
- char* tmp = malloc(len + 2);
- int ret;
- if (!tmp)
- {
- return -1;
- }
- memcpy(tmp, file->target, len);
- memcpy(tmp + len, "~", 2);
- ret = rename(file->target, tmp);
- free(tmp);
- if (ret && errno == ENOENT)
- {
- int olderrno = errno;
- /* Check if rename failed because target doesn't exist */
- if (access(file->target, F_OK) == 0 ||
- errno != ENOENT)
- {
- /* Unable to create backup */
- errno = olderrno;
- return -1;
- }
- }
- if (rename(file->path, file->target))
- {
- return -1;
- }
- free(file->target);
- }
- free(file->path);
- free(file);
- return 0;
-}
-
-void paths_file_abort(paths_file_t* file)
-{
- if (file)
- {
- if (file->fd >= 0)
- {
- close(file->fd);
- }
- unlink(file->path);
- free(file->path);
- free(file->target);
- free(file);
- }
-}
-
-bool create_dir(paths_t* paths, paths_source_t source, const char* addon,
- size_t addon_len)
-{
- const char* dir = paths_user_dir(paths, source);
- char* tmp = NULL;
- assert(dir);
- assert(addon || !addon_len);
- if (addon_len)
- {
- size_t dlen = strlen(dir);
- tmp = malloc(dlen + 1 + addon_len + 1);
- if (!tmp)
- {
- return false;
- }
- memcpy(tmp, dir, dlen);
- tmp[dlen++] = '/';
- memcpy(tmp + dlen, addon, addon_len);
- tmp[dlen + addon_len] = '\0';
- dir = tmp;
- }
- if (mkdir(dir, 0700))
- {
- free(tmp);
- return errno == EEXIST;
- }
- free(tmp);
- return true;
-}
-
-void free_list(char** list)
-{
- if (list)
- {
- char** i;
- for (i = list; *i; i++)
- {
- free(*i);
- }
- free(list);
- }
-}
-
-typedef struct homedir_t
-{
- char* path;
- size_t len;
-} homedir_t;
-
-static char* get_userdir(const char* envvar, homedir_t* homedir,
- const char* addon);
-static char** get_searchdirs(const char* envvar, const char* default_list);
-
-static bool valid_runtime(const char* path);
-
-bool paths_init(paths_t* paths)
-{
- homedir_t homedir = { NULL, 0 };
- paths->user_data = get_userdir("XDG_DATA_HOME", &homedir, "/.local/share");
- paths->user_config = get_userdir("XDG_CONFIG_HOME", &homedir, "/.config");
- paths->user_cache = get_userdir("XDG_CACHE_HOME", &homedir, "/.cache");
- paths->user_runtime = get_userdir("XDG_RUNTIME_HOME", NULL, NULL);
- free(homedir.path);
- if (!paths->user_data || !paths->user_config || !paths->user_cache)
- {
- return false;
- }
- if (paths->user_runtime)
- {
- if (!valid_runtime(paths->user_runtime))
- {
- free(paths->user_runtime);
- paths->user_runtime = NULL;
- }
- }
- paths->search_data = get_searchdirs("XDG_DATA_DIRS",
- "/usr/local/share:/usr/share");
- if (!paths->search_data)
- {
- return false;
- }
- paths->search_config = get_searchdirs("XDG_CONFIG_DIRS", "/etc/xdg");
- if (!paths->search_config)
- {
- return false;
- }
- return true;
-}
-
-static void cleanup_dir(char* path, size_t len)
-{
- while (len > 1 && path[len - 1] == '/')
- {
- path[--len] = '\0';
- }
-}
-
-static char* get_homedir(void)
-{
- const char* data = getenv("HOME");
- size_t len;
- char* ret;
- if (data && *data)
- {
- len = strlen(data);
- ret = malloc(len + 1);
- if (ret)
- {
- memcpy(ret, data, len + 1);
- cleanup_dir(ret, len);
- }
- return ret;
- }
- for (;;)
- {
- long size = sysconf(_SC_GETPW_R_SIZE_MAX);
- char* buf;
- struct passwd entry, *result = NULL;
- assert(size != -1);
- if (size == -1)
- {
- break;
- }
- buf = malloc(size);
- if (!buf)
- {
- break;
- }
- if (getpwuid_r(getuid(), &entry, buf, size, &result) != 0)
- {
- /* Error getting entry */
- free(buf);
- break;
- }
- if (!result)
- {
- /* No entry found */
- free(buf);
- break;
- }
- len = strlen(entry.pw_dir);
- if (len == 0)
- {
- free(buf);
- break;
- }
- ret = malloc(len + 1);
- if (!ret)
- {
- free(buf);
- return NULL;
- }
- memcpy(ret, entry.pw_dir, len + 1);
- free(buf);
- cleanup_dir(ret, len);
- return ret;
- }
- /* When all else fails */
- return strdup("/");
-}
-
-char* get_userdir(const char* envvar, homedir_t* homedir, const char* addon)
-{
- const char* data = getenv(envvar);
- size_t len;
- char* ret;
- assert(!addon || addon[0] == '/');
- if (data && *data)
- {
- len = strlen(data);
- ret = malloc(len + 1);
- if (ret)
- {
- memcpy(ret, data, len + 1);
- cleanup_dir(ret, len);
- }
- return ret;
- }
- if (!homedir && !addon)
- {
- return NULL;
- }
- assert(homedir && addon);
- if (!homedir->path)
- {
- homedir->path = get_homedir();
- if (!homedir->path)
- {
- return NULL;
- }
- homedir->len = strlen(homedir->path);
- }
- len = homedir->len + strlen(addon);
- ret = malloc(len + 1);
- if (ret)
- {
- memcpy(ret, homedir->path, homedir->len);
- if (homedir->len > 1)
- {
- assert(homedir->path[homedir->len - 1] != '/');
- memcpy(ret + homedir->len, addon, (len - homedir->len) + 1);
- }
- else
- {
- assert(homedir->path[0] == '/');
- len--;
- memcpy(ret + homedir->len, addon + 1, len - homedir->len);
- ret[len] = '\0';
- }
- }
- return ret;
-}
-
-static void cleanup_dirs(char** dirs)
-{
- char** d;
- for (d = dirs; *d; d++)
- {
- cleanup_dir(*d, strlen(*d));
- }
-}
-
-static char** splitlist(const char* str)
-{
- size_t count = 0, size = 2;
- char** ret = calloc(size + 1, sizeof(char*));
- const char* last = str, * pos;
- if (!ret)
- {
- return NULL;
- }
- for (;;)
- {
- size_t len;
- pos = strchr(last, ':');
- if (!pos)
- {
- pos = last + strlen(last);
- }
-
- if (count == size)
- {
- size_t ns = size * 2;
- char** tmp = realloc(ret, (ns + 1) * sizeof(char*));
- if (!tmp)
- {
- free_list(ret);
- return NULL;
- }
- memset(tmp + size, 0, (ns + 1 - size) * sizeof(char*));
- ret = tmp;
- size = ns;
- }
- len = pos - last;
- ret[count] = strdup_len(last, len);
- if (!ret[count])
- {
- free_list(ret);
- return NULL;
- }
- count++;
-
- if (*pos)
- {
- last = pos + 1;
- }
- else
- {
- break;
- }
- }
- assert(!ret[count]);
- return ret;
-}
-
-char** get_searchdirs(const char* envvar, const char* default_list)
-{
- const char* data = getenv(envvar);
- char** ret;
- bool cleanup;
- if (data && *data)
- {
- ret = splitlist(data);
- cleanup = true;
- }
- else
- {
- ret = splitlist(default_list);
- cleanup = false;
- }
- if (!ret)
- {
- return NULL;
- }
- if (cleanup)
- {
- cleanup_dirs(ret);
- }
- return ret;
-}
-
-bool valid_runtime(const char* path)
-{
- struct stat buf;
- if (stat(path, &buf) != 0)
- {
- return false;
- }
- if (buf.st_uid != getuid())
- {
- return false;
- }
- if ((buf.st_mode & 0777) != 0700)
- {
- return false;
- }
- return true;
-}
-
-char* build_path(paths_t* paths, paths_source_t source, ...)
-{
- va_list args;
- bool first = true;
- dynstr_t* ret;
- const char* base = paths_user_dir(paths, source), * part;
- assert(base);
- ret = dynstr_new_str(base);
- if (!ret)
- {
- return NULL;
- }
- va_start(args, source);
- while ((part = va_arg(args, const char*)))
- {
- if (first)
- {
- dynstr_appendc(ret, '/');
- first = false;
- }
- dynstr_append(ret, part);
- }
- return dynstr_done(ret);
-}
-
-static bool need_glob(char* match)
-{
- const char* magic_chars = "*?[]{}";
- char* s;
- bool need_unescape = false;
- for (s = match; *s; s++)
- {
- if (*s == '\\')
- {
- s++;
- need_unescape = true;
- }
- else if (strchr(magic_chars, *s))
- {
- return true;
- }
- }
- if (need_unescape)
- {
- unescape(match);
- }
- return false;
-}
-
-void paths_find(paths_t* paths, paths_source_t source,
- const char* match, paths_find_callback_t find_callback,
- void* userdata)
-{
- search_t* search;
- search = get_search(paths->search);
- search->dir = paths_user_dir(paths, source);
- search->dirs = paths_search_dirs(paths, source);
- search->match = strdup(match);
- search->find_callback = find_callback;
- search->userdata = userdata;
- if (!search->match)
- {
- find_callback(userdata, NULL);
- put_search(paths->search, search);
- return;
- }
- if (*search->match == '/')
- {
- size_t len = strlen(search->match);
- memmove(search->match, search->match + 1, len);
- }
- if (!*search->match)
- {
- find_callback(userdata, NULL);
- put_search(paths->search, search);
- return;
- }
- search->plain = !need_glob(search->match);
- if (search->plain)
- {
- search->match_name = strrchr(search->match, '/');
- if (search->match_name)
- {
- search->match_name++;
- }
- else
- {
- search->match_name = search->match;
- }
- }
- safe_fifo_push(paths->search->queue, search);
-}
-
-search_t* get_search(searchargs_t* args)
-{
- size_t i;
- thread_lock_lock(args->lock);
- for (i = 0; i < SEARCH_CACHE_SIZE; i++)
- {
- if (args->cache[i])
- {
- search_t* ret = args->cache[i];
- args->cache[i] = NULL;
- thread_lock_unlock(args->lock);
- return ret;
- }
- }
- thread_lock_unlock(args->lock);
- return malloc(sizeof(search_t));
-}
-
-void put_search(searchargs_t* args, search_t* search)
-{
- size_t i;
- thread_lock_lock(args->lock);
- for (i = 0; i < SEARCH_CACHE_SIZE; i++)
- {
- if (!args->cache[i])
- {
- args->cache[i] = search;
- free_search_data(search);
- thread_lock_unlock(args->lock);
- return;
- }
- }
- thread_lock_unlock(args->lock);
- free_search_data(search);
- free(search);
-}
-
-void free_search_data(search_t* search)
-{
- free(search->match);
-}
-
-static bool search_dir(search_t* search, const char* dir)
-{
- bool canceled = false;
- if (search->plain)
- {
- DIR* dh;
- struct dirent* d;
- char* path = NULL;
- size_t dirlen = strlen(dir);
- size_t matchlen = strlen(search->match);
- if (search->match == search->match_name)
- {
- path = malloc(dirlen + 1 + matchlen + 1);
- if (!path)
- {
- return false;
- }
- memcpy(path, dir, dirlen + 1);
- }
- else
- {
- size_t subdirlen = search->match_name - 1 - search->match;
- matchlen = strlen(search->match_name);
- path = malloc(dirlen + 1 + subdirlen + 1 + matchlen + 1);
- assert(search->match_name > search->match);
- assert(search->match_name[-1] == '/');
- if (!path)
- {
- return false;
- }
- memcpy(path, dir, dirlen);
- path[dirlen++] = '/';
- memcpy(path + dirlen, search->match, subdirlen);
- dirlen += subdirlen;
- path[dirlen] = '\0';
- }
- dh = opendir(path);
- if (!dh)
- {
- free(path);
- return false;
- }
- path[dirlen] = '/';
- while ((d = readdir(dh)))
- {
- if (strcmp(d->d_name, search->match_name) == 0)
- {
- memcpy(path + dirlen + 1, search->match_name, matchlen + 1);
- if (!search->find_callback(search->userdata, path))
- {
- canceled = true;
- break;
- }
- }
- }
- closedir(dh);
- free(path);
- }
- else
- {
- glob_t data;
- dynstr_t* str;
- char* pattern;
- str = dynstr_new_str(dir);
- if (!str)
- {
- return false;
- }
- if (!dynstr_escape(str, "*?[]{}") || !dynstr_appendc(str, '/') ||
- !dynstr_append(str, search->match))
- {
- dynstr_free(str);
- return false;
- }
- pattern = dynstr_done(str);
- memset(&data, 0, sizeof(glob_t));
- if (glob(pattern, GLOB_NOSORT, NULL, &data) == 0)
- {
- size_t i;
- for (i = 0; i < data.gl_pathc; i++)
- {
- if (!search->find_callback(search->userdata, data.gl_pathv[i]))
- {
- canceled = true;
- break;
- }
- }
- globfree(&data);
- }
- free(pattern);
- }
- return canceled;
-}
-
-void* searcher(void* _args)
-{
- searchargs_t* args = _args;
- size_t i;
-
- while (!args->quit)
- {
- search_t* search = safe_fifo_pop(args->queue);
- bool canceled = false;
- if (!search->match)
- {
- free(search);
- assert(args->quit);
- break;
- }
- do
- {
- if (search->dir)
- {
- if (args->quit ||
- (canceled = search_dir(search, search->dir)))
- {
- break;
- }
- }
- if (search->dirs)
- {
- const char** dir;
- for (dir = search->dirs; *dir; dir++)
- {
- if (args->quit ||
- (canceled = search_dir(search, *dir)))
- {
- break;
- }
- }
- }
- } while (false);
- if (!canceled)
- {
- search->find_callback(search->userdata, NULL);
- }
- put_search(args, search);
- }
-
- /* Free all items in the queue */
- for (;;)
- {
- search_t* search = safe_fifo_trypop(args->queue);
- if (!search)
- {
- break;
- }
- put_search(args, search);
- }
-
- safe_fifo_unref(args->queue);
- thread_lock_free(args->lock);
- for (i = 0; i < SEARCH_CACHE_SIZE; i++)
- {
- if (args->cache[i])
- {
- free_search_data(args->cache[i]);
- free(args->cache[i]);
- }
- }
- free(args);
- return NULL;
-}