2015-10-09 13:55:34 -04:00
|
|
|
/*
|
2013-08-07 21:00:09 -04:00
|
|
|
* Copyright 2013 (c) Anna Schumaker.
|
2013-07-27 11:40:16 -04:00
|
|
|
*/
|
2014-06-05 10:19:22 -04:00
|
|
|
#include <core/file.h>
|
2015-10-08 09:41:51 -04:00
|
|
|
#include <core/string.h>
|
2016-02-05 07:57:22 -05:00
|
|
|
#include <core/version.h>
|
2015-10-08 09:41:51 -04:00
|
|
|
#include <errno.h>
|
2016-08-28 10:05:51 -04:00
|
|
|
#include <stdlib.h>
|
2016-03-30 10:11:02 -04:00
|
|
|
#include <unistd.h>
|
2013-07-27 11:40:16 -04:00
|
|
|
|
2016-03-30 14:17:12 -04:00
|
|
|
#define REPORT_ERROR(fname, error) \
|
|
|
|
g_printerr("%s (%s:%d): %s: %s\n", __func__, __FILE__, __LINE__, fname, error)
|
|
|
|
#define REPORT_ERRNO(fname) REPORT_ERROR(fname, strerror(errno))
|
2015-10-08 09:41:51 -04:00
|
|
|
|
2018-02-16 15:50:46 -05:00
|
|
|
static void __file_init_common(struct file *file, const gchar *subdir,
|
2018-02-16 16:11:21 -05:00
|
|
|
const gchar *name, const gchar *(*user_dir)(void))
|
2018-02-16 15:50:46 -05:00
|
|
|
{
|
2018-02-16 16:11:21 -05:00
|
|
|
file->f_file = NULL;
|
|
|
|
file->f_name = name;
|
|
|
|
file->f_subdir = subdir;
|
2018-02-20 09:31:40 -05:00
|
|
|
file->f_mode = CLOSED;
|
2018-02-16 16:11:21 -05:00
|
|
|
file->f_user_dir = user_dir;
|
2018-02-16 15:50:46 -05:00
|
|
|
}
|
|
|
|
|
2016-03-31 08:52:59 -04:00
|
|
|
static gchar *__file_path(const gchar *base, const gchar *dir,
|
|
|
|
const gchar *name)
|
2013-07-28 19:57:07 -04:00
|
|
|
{
|
2016-03-31 08:52:59 -04:00
|
|
|
return g_build_filename(base, OCARINA_NAME, dir ? dir : "", name, NULL);
|
|
|
|
}
|
|
|
|
|
2016-03-30 10:17:42 -04:00
|
|
|
static FILE *__file_open(gchar *path, const gchar *mode)
|
|
|
|
{
|
|
|
|
FILE *ret = g_fopen(path, mode);
|
|
|
|
|
|
|
|
if (!ret)
|
2016-03-30 14:17:12 -04:00
|
|
|
REPORT_ERRNO(path);
|
2016-03-30 10:17:42 -04:00
|
|
|
g_free(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-31 10:15:17 -04:00
|
|
|
static void __file_close(FILE *file, gchar *path, gchar *tmp)
|
|
|
|
{
|
|
|
|
if (file) {
|
|
|
|
fclose(file);
|
|
|
|
if (path && tmp)
|
|
|
|
g_rename(tmp, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(path);
|
|
|
|
g_free(tmp);
|
|
|
|
}
|
|
|
|
|
2016-03-31 09:57:24 -04:00
|
|
|
static bool __file_mkdir(const gchar *basedir, const gchar *subdir)
|
2015-09-10 09:20:43 -04:00
|
|
|
{
|
2016-03-31 09:57:24 -04:00
|
|
|
gchar *dir = __file_path(basedir, subdir, NULL);
|
2015-10-09 10:07:08 -04:00
|
|
|
int ret = g_mkdir_with_parents(dir, 0755);
|
|
|
|
|
|
|
|
if (ret != 0)
|
2016-03-30 14:17:12 -04:00
|
|
|
REPORT_ERRNO(dir);
|
2016-03-31 14:20:44 -04:00
|
|
|
g_free(dir);
|
2015-10-09 10:07:08 -04:00
|
|
|
return ret == 0;
|
2015-09-10 09:20:43 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
static bool __file_can_write(struct file *data)
|
2016-03-30 10:11:02 -04:00
|
|
|
{
|
2018-02-16 16:11:21 -05:00
|
|
|
gchar *path = file_path(data);
|
2016-03-30 10:11:02 -04:00
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
if (g_access(path, F_OK) == 0 && g_access(path, W_OK) < 0)
|
|
|
|
ret = false;
|
|
|
|
|
|
|
|
g_free(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-01 21:30:06 -04:00
|
|
|
|
2018-02-16 15:50:46 -05:00
|
|
|
void file_init_data(struct file *file, const gchar *subdir,
|
|
|
|
const gchar *name, unsigned int min)
|
2013-07-28 19:57:07 -04:00
|
|
|
{
|
2018-02-16 16:11:21 -05:00
|
|
|
__file_init_common(file, subdir, name, g_get_user_data_dir);
|
|
|
|
|
2018-02-16 15:50:46 -05:00
|
|
|
file->f_data.f_version = OCARINA_MINOR_VERSION;
|
|
|
|
file->f_data.f_prev = 0;
|
|
|
|
file->f_data.f_min = min;
|
2013-07-28 19:57:07 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 15:50:46 -05:00
|
|
|
void file_init_cache(struct file *file, const gchar *subdir, const gchar *name)
|
2016-03-31 10:55:42 -04:00
|
|
|
{
|
2018-02-16 16:11:21 -05:00
|
|
|
__file_init_common(file, subdir, name, g_get_user_cache_dir);
|
2016-03-31 10:55:42 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 16:11:21 -05:00
|
|
|
gchar *file_path(struct file *file)
|
2013-07-27 11:40:16 -04:00
|
|
|
{
|
2018-02-16 16:11:21 -05:00
|
|
|
if (string_length(file->f_name) == 0)
|
|
|
|
return g_strdup("");
|
|
|
|
return g_build_filename(file->f_user_dir(), OCARINA_NAME,
|
|
|
|
file->f_subdir, file->f_name, NULL);
|
2013-07-27 11:40:16 -04:00
|
|
|
}
|
2013-07-28 19:57:07 -04:00
|
|
|
|
2018-02-16 16:22:30 -05:00
|
|
|
gchar *file_write_path(struct file *file)
|
2016-03-30 10:11:02 -04:00
|
|
|
{
|
2018-02-16 16:22:30 -05:00
|
|
|
gchar *tmp, *res;
|
2016-03-30 10:11:02 -04:00
|
|
|
|
2018-02-16 16:22:30 -05:00
|
|
|
if (string_length(file->f_name) == 0)
|
|
|
|
return g_strdup("");
|
|
|
|
|
|
|
|
tmp = g_strdup_printf(".%s.tmp", file->f_name);
|
|
|
|
res = g_build_filename(file->f_user_dir(), OCARINA_NAME,
|
|
|
|
file->f_subdir, tmp, NULL);
|
|
|
|
g_free(tmp);
|
|
|
|
|
|
|
|
return res;
|
2016-03-30 10:11:02 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
const unsigned int data_file_version(struct file *data)
|
2013-07-28 22:33:40 -04:00
|
|
|
{
|
2018-02-16 13:30:23 -05:00
|
|
|
struct data_file *file = &data->f_data;
|
2018-02-20 09:31:40 -05:00
|
|
|
if (data->f_file && (data->f_mode == OPEN_READ))
|
2015-09-10 08:10:38 -04:00
|
|
|
return file->f_prev;
|
|
|
|
return file->f_version;
|
2013-07-28 22:33:40 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 16:51:41 -05:00
|
|
|
bool file_exists(struct file *file)
|
2013-07-28 19:57:07 -04:00
|
|
|
{
|
2018-02-16 16:51:41 -05:00
|
|
|
gchar *path = file_path(file);
|
|
|
|
bool ret = g_file_test(path, G_FILE_TEST_EXISTS);
|
|
|
|
g_free(path);
|
|
|
|
return ret;
|
2014-03-02 14:44:24 -05:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
static bool __file_open_read(struct file *data)
|
2015-09-10 09:20:43 -04:00
|
|
|
{
|
2018-02-16 13:30:23 -05:00
|
|
|
struct data_file *file = &data->f_data;
|
2018-02-16 16:51:41 -05:00
|
|
|
if (!file_exists(data))
|
2015-09-10 09:20:43 -04:00
|
|
|
return false;
|
2016-03-30 10:17:42 -04:00
|
|
|
|
2018-02-16 16:11:21 -05:00
|
|
|
data->f_file = __file_open(file_path(data), "r");
|
2018-02-16 15:02:28 -05:00
|
|
|
if (!data->f_file)
|
2014-03-09 10:34:06 -04:00
|
|
|
return false;
|
2016-03-30 10:17:42 -04:00
|
|
|
|
2018-02-20 09:31:40 -05:00
|
|
|
data->f_mode = OPEN_READ;
|
2018-02-16 13:30:23 -05:00
|
|
|
if (data_file_readf(data, "%u\n", &file->f_prev) != 1)
|
2016-03-30 11:25:14 -04:00
|
|
|
return false;
|
|
|
|
if (file->f_prev < file->f_min) {
|
2018-02-16 15:02:28 -05:00
|
|
|
REPORT_ERROR(data->f_name, "File too old to be upgraded.");
|
2018-02-16 13:30:23 -05:00
|
|
|
data_file_close(data);
|
2016-08-28 10:05:51 -04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (file->f_prev > file->f_version) {
|
2018-02-16 15:02:28 -05:00
|
|
|
REPORT_ERROR(data->f_name, "File too new to be opened.");
|
2018-02-16 13:30:23 -05:00
|
|
|
data_file_close(data);
|
2016-08-28 10:05:51 -04:00
|
|
|
exit(1);
|
2016-03-30 11:25:14 -04:00
|
|
|
}
|
|
|
|
return true;
|
2013-07-28 19:57:07 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
static bool __file_open_write(struct file *data)
|
2013-07-28 19:57:07 -04:00
|
|
|
{
|
2018-02-16 13:30:23 -05:00
|
|
|
struct data_file *file = &data->f_data;
|
2016-03-31 09:57:24 -04:00
|
|
|
if (!__file_mkdir(g_get_user_data_dir(), NULL))
|
2014-03-09 10:34:06 -04:00
|
|
|
return false;
|
2018-02-16 13:30:23 -05:00
|
|
|
if (!__file_can_write(data))
|
2016-03-30 10:11:02 -04:00
|
|
|
return false;
|
2016-03-30 10:17:42 -04:00
|
|
|
|
2018-02-16 16:22:30 -05:00
|
|
|
data->f_file = __file_open(file_write_path(data), "w");
|
2018-02-16 15:02:28 -05:00
|
|
|
if (!data->f_file)
|
2014-03-09 10:34:06 -04:00
|
|
|
return false;
|
2016-03-30 10:17:42 -04:00
|
|
|
|
2018-02-20 09:31:40 -05:00
|
|
|
data->f_mode = OPEN_WRITE;
|
2018-02-16 13:30:23 -05:00
|
|
|
return data_file_writef(data, "%d\n", file->f_version) > 0;
|
2013-07-28 19:57:07 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
bool data_file_open(struct file *data, enum open_mode mode)
|
2013-07-28 19:57:07 -04:00
|
|
|
{
|
2018-02-16 15:02:28 -05:00
|
|
|
if ((string_length(data->f_name) == 0) || (data->f_file != NULL))
|
2014-03-09 10:34:06 -04:00
|
|
|
return false;
|
2013-07-28 19:57:07 -04:00
|
|
|
|
2018-02-20 09:31:40 -05:00
|
|
|
if (mode == CLOSED)
|
|
|
|
return false;
|
|
|
|
else if (mode == OPEN_READ)
|
2018-02-16 13:30:23 -05:00
|
|
|
return __file_open_read(data);
|
|
|
|
return __file_open_write(data);
|
2013-07-28 19:57:07 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
bool cache_file_open(struct file *cache, enum open_mode mode)
|
2016-03-31 09:57:24 -04:00
|
|
|
{
|
2018-02-16 15:02:28 -05:00
|
|
|
if ((string_length(cache->f_name) == 0) || (cache->f_file != NULL))
|
2016-03-31 09:57:24 -04:00
|
|
|
return false;
|
2018-02-20 09:31:40 -05:00
|
|
|
if (mode == OPEN_READ || mode == CLOSED)
|
|
|
|
return false;
|
2018-02-16 15:50:46 -05:00
|
|
|
if (!__file_mkdir(g_get_user_cache_dir(), cache->f_subdir))
|
2016-03-31 09:57:24 -04:00
|
|
|
return false;
|
|
|
|
|
2018-02-16 16:22:30 -05:00
|
|
|
cache->f_file = __file_open(file_write_path(cache), "wb");
|
2018-02-20 09:31:40 -05:00
|
|
|
cache->f_mode = mode;
|
2018-02-16 15:02:28 -05:00
|
|
|
return cache->f_file != NULL;
|
2016-03-31 09:57:24 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
void data_file_close(struct file *data)
|
2013-07-28 19:57:07 -04:00
|
|
|
{
|
2018-02-16 15:02:28 -05:00
|
|
|
__file_close(data->f_file,
|
2018-02-20 09:31:40 -05:00
|
|
|
data->f_mode == OPEN_WRITE ? file_path(data) : NULL,
|
|
|
|
data->f_mode == OPEN_WRITE ? file_write_path(data) : NULL);
|
2018-02-16 15:02:28 -05:00
|
|
|
data->f_file = NULL;
|
2018-02-20 09:31:40 -05:00
|
|
|
data->f_mode = CLOSED;
|
2013-07-28 22:33:40 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
void cache_file_close(struct file *cache)
|
2016-03-31 10:15:17 -04:00
|
|
|
{
|
2018-02-16 15:02:28 -05:00
|
|
|
__file_close(cache->f_file,
|
2018-02-16 16:11:21 -05:00
|
|
|
file_path(cache),
|
2018-02-16 16:22:30 -05:00
|
|
|
file_write_path(cache));
|
2018-02-16 15:02:28 -05:00
|
|
|
cache->f_file = NULL;
|
2018-02-20 09:31:40 -05:00
|
|
|
cache->f_mode = CLOSED;
|
2016-03-31 10:15:17 -04:00
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
int data_file_readf(struct file *data, const char *fmt, ...)
|
2015-10-08 11:16:38 -04:00
|
|
|
{
|
|
|
|
va_list argp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(argp, fmt);
|
2018-02-16 15:02:28 -05:00
|
|
|
ret = vfscanf(data->f_file, fmt, argp);
|
2015-10-08 11:16:38 -04:00
|
|
|
va_end(argp);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
gchar *data_file_readl(struct file *data)
|
2013-07-28 22:33:40 -04:00
|
|
|
{
|
2015-10-09 13:09:47 -04:00
|
|
|
gchar *res;
|
2014-01-22 19:21:11 -05:00
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
if (data_file_readf(data, "%m[^\n]\n", &res) == 0)
|
2015-10-09 13:09:47 -04:00
|
|
|
return g_strdup("");
|
2014-01-22 19:21:11 -05:00
|
|
|
|
2015-10-09 13:09:47 -04:00
|
|
|
g_strstrip(res);
|
2013-07-28 22:33:40 -04:00
|
|
|
return res;
|
2013-07-28 19:57:07 -04:00
|
|
|
}
|
2015-10-08 09:41:51 -04:00
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
int data_file_writef(struct file *data, const char *fmt, ...)
|
2015-10-08 09:41:51 -04:00
|
|
|
{
|
|
|
|
va_list argp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(argp, fmt);
|
2018-02-16 15:02:28 -05:00
|
|
|
ret = g_vfprintf(data->f_file, fmt, argp);
|
2015-10-08 09:41:51 -04:00
|
|
|
va_end(argp);
|
|
|
|
|
|
|
|
if (ret < 0)
|
2018-02-16 15:02:28 -05:00
|
|
|
REPORT_ERRNO(data->f_name);
|
2015-10-08 09:41:51 -04:00
|
|
|
return ret;
|
|
|
|
}
|
2016-03-31 10:32:38 -04:00
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
int cache_file_write(struct file *cache, const void *data, size_t len)
|
2016-03-31 10:32:38 -04:00
|
|
|
{
|
2018-02-16 15:02:28 -05:00
|
|
|
if (fwrite(data, len, 1, cache->f_file) == 1)
|
2016-03-31 10:32:38 -04:00
|
|
|
return len;
|
|
|
|
return -1;
|
|
|
|
}
|
2016-04-01 08:15:07 -04:00
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
bool cache_file_import(struct file *cache, const gchar *srcpath)
|
2016-07-12 09:08:45 -04:00
|
|
|
{
|
|
|
|
gchar *contents = NULL;
|
|
|
|
gsize length = 0;
|
|
|
|
|
2018-02-16 15:02:28 -05:00
|
|
|
if (!cache->f_file || !srcpath)
|
2016-07-12 09:08:45 -04:00
|
|
|
return false;
|
|
|
|
if (!g_file_get_contents(srcpath, &contents, &length, NULL))
|
|
|
|
return false;
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
cache_file_write(cache, contents, length);
|
2016-07-12 09:08:45 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-16 13:30:23 -05:00
|
|
|
bool data_file_remove(struct file *data)
|
2016-04-01 08:15:07 -04:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
gchar *path;
|
|
|
|
|
2018-02-16 15:02:28 -05:00
|
|
|
if (!data->f_file) {
|
2018-02-16 16:11:21 -05:00
|
|
|
path = file_path(data);
|
2016-04-01 08:15:07 -04:00
|
|
|
ret = g_unlink(path);
|
|
|
|
g_free(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret == 0;
|
|
|
|
}
|