/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #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; }