ocarina/core/file.c

203 lines
3.8 KiB
C

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/file.h>
#include <core/string.h>
#include <core/version.h>
#include <errno.h>
#include <unistd.h>
#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))
static gchar *__file_path(const gchar *name)
{
return g_strjoin("/", g_get_user_data_dir(), OCARINA_NAME, name, NULL);
}
static FILE *__file_open(gchar *path, const gchar *mode)
{
FILE *ret = g_fopen(path, mode);
if (!ret)
REPORT_ERRNO(path);
g_free(path);
return ret;
}
static bool __file_mkdir()
{
gchar *dir = __file_path(NULL);
int ret = g_mkdir_with_parents(dir, 0755);
if (ret != 0)
REPORT_ERRNO(dir);
g_free(dir);
return ret == 0;
}
static bool __file_can_write(struct file *file)
{
gchar *path = file_path(file);
bool ret = true;
if (g_access(path, F_OK) == 0 && g_access(path, W_OK) < 0)
ret = false;
g_free(path);
return ret;
}
static void __file_rename_tmp(struct file *file)
{
gchar *path = file_path(file);
gchar *real = file_write_path(file);
g_rename(real, path);
g_free(real);
g_free(path);
}
void file_init(struct file *file, const gchar *name,
unsigned int version, unsigned int min)
{
file->f_mode = OPEN_READ;
file->f_version = version;
file->f_prev = 0;
file->f_min = 0;
file->f_file = NULL;
file->f_name = name;
}
gchar *file_path(struct file *file)
{
if (string_length(file->f_name) != 0)
return __file_path(file->f_name);
return g_strdup("");
}
gchar *file_write_path(struct file *file)
{
gchar *fname, *res;
if (string_length(file->f_name) == 0)
return g_strdup("");
fname = g_strdup_printf(".%s.tmp", file->f_name);
res = __file_path(fname);
g_free(fname);
return res;
}
const unsigned int file_version(struct file *file)
{
if (file->f_file && (file->f_mode == OPEN_READ))
return file->f_prev;
return file->f_version;
}
bool file_exists(struct file *file)
{
gchar *path = file_path(file);
bool ret = g_file_test(path, G_FILE_TEST_EXISTS);
g_free(path);
return ret;
}
static bool __file_open_read(struct file *file)
{
if (!file_exists(file))
return false;
file->f_file = __file_open(file_path(file), "r");
if (!file->f_file)
return false;
file->f_mode = OPEN_READ;
if (file_readf(file, "%u\n", &file->f_prev) != 1)
return false;
if (file->f_prev < file->f_min) {
REPORT_ERROR(file->f_name, "File too old to be upgraded.");
file_close(file);
return false;
}
return true;
}
static bool __file_open_write(struct file *file)
{
if (!__file_mkdir())
return false;
if (!__file_can_write(file))
return false;
file->f_file = __file_open(file_write_path(file), "w");
if (!file->f_file)
return false;
file->f_mode = OPEN_WRITE;
return file_writef(file, "%d\n", file->f_version) > 0;
}
bool file_open(struct file *file, enum open_mode mode)
{
if ((string_length(file->f_name) == 0) || (file->f_file != NULL))
return false;
if (mode == OPEN_READ)
return __file_open_read(file);
return __file_open_write(file);
}
void file_close(struct file *file)
{
if (file->f_file) {
fclose(file->f_file);
if (file->f_mode == OPEN_WRITE)
__file_rename_tmp(file);
}
file->f_file = NULL;
}
int file_readf(struct file *file, const char *fmt, ...)
{
va_list argp;
int ret;
va_start(argp, fmt);
ret = vfscanf(file->f_file, fmt, argp);
va_end(argp);
return ret;
}
gchar *file_readl(struct file *file)
{
gchar *res;
if (file_readf(file, "%m[^\n]\n", &res) == 0)
return g_strdup("");
g_strstrip(res);
return res;
}
int file_writef(struct file *file, const char *fmt, ...)
{
va_list argp;
int ret;
va_start(argp, fmt);
ret = g_vfprintf(file->f_file, fmt, argp);
va_end(argp);
if (ret < 0)
REPORT_ERRNO(file->f_name);
return ret;
}