1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h> // unlink()
#include <errno.h>
#include <zsv/utils/cache.h>
#include <zsv/utils/jq.h>
#ifndef APPNAME
#define APPNAME "cache"
#endif
#include <zsv/utils/os.h>
#include <zsv/utils/err.h>
#include <zsv/utils/dirs.h>
#include <zsv/utils/file.h>
static const char *zsv_cache_type_name(enum zsv_cache_type t) {
switch (t) {
case zsv_cache_type_property:
return ZSV_CACHE_PROPERTIES_NAME ".json";
case zsv_cache_type_tag:
return "tag.json";
case zsv_cache_type_overwrite:
return "overwrite.sqlite3";
default:
return NULL;
}
}
unsigned char *zsv_cache_filepath(const unsigned char *data_filepath, enum zsv_cache_type type, char create_dir,
char temp_file) {
if (!data_filepath || !*data_filepath)
return NULL;
const char *cache_filename_base = zsv_cache_type_name(type);
if (!cache_filename_base) {
zsv_printerr(ENOMEM, "Out of memory!");
return NULL;
}
unsigned char *cache_filename;
asprintf((char **)&cache_filename, "%s%s", cache_filename_base, temp_file ? ZSV_TEMPFILE_SUFFIX : "");
unsigned char *s = cache_filename ? zsv_cache_path(data_filepath, cache_filename, 0) : NULL;
if (s && create_dir) {
char *last_slash_s = (char *)strrchr((void *)s, FILESLASH);
int err = 0;
// temporarily truncate the string so as to only leave its parent folder
*last_slash_s = '\0';
// ensure the parent dir exists
if (!zsv_dir_exists((char *)s))
err = zsv_mkdirs((char *)s, 0);
if (err) {
fprintf(stderr, "Unable to create cache directory %s\n", s);
free(s);
s = NULL;
} else
*last_slash_s = FILESLASH;
}
free(cache_filename);
return s;
}
/*
* print cache file to stdout
*/
int zsv_cache_print(const unsigned char *filepath, enum zsv_cache_type ctype, const unsigned char *default_value) {
int err = 0;
// to do: parse the json rather than just blindly regurgitating the file
unsigned char *cache_fn = zsv_cache_filepath(filepath, ctype, 0, 0);
if (cache_fn) {
FILE *f;
if (zsv_file_readable((char *)cache_fn, &err, &f)) {
char buff[1024];
size_t bytes;
while ((bytes = fread(buff, 1, sizeof(buff), f)))
fwrite(buff, 1, bytes, stdout);
fclose(f);
} else if (err == ENOENT) {
if (default_value)
printf("%s\n", default_value);
} else {
perror((const char *)cache_fn);
if (!err)
err = 1;
}
}
free(cache_fn);
return err;
}
/*
* remove a cache file
*/
int zsv_cache_remove(const unsigned char *filepath, enum zsv_cache_type ctype) {
int err = 0;
unsigned char *fn = zsv_cache_filepath(filepath, ctype, 0, 0);
if (!fn)
err = ENOMEM;
else if (zsv_file_readable((const char *)fn, &err, NULL)) {
err = unlink((const char *)fn);
if (err)
perror((const char *)fn);
} else if (err == ENOENT)
err = 0; // file d.n. exist, nothing to do
else
perror((const char *)fn);
free(fn);
return err;
}
/*
* modify a JSON cache file, write to tmp file, then replace the cache file
*/
int zsv_modify_cache_file(const unsigned char *filepath, enum zsv_cache_type ctype, const unsigned char *json_value1,
const unsigned char *json_value2, const unsigned char *filter) {
unsigned char *cache_fn = zsv_cache_filepath((const unsigned char *)filepath, ctype, 0, 0);
unsigned char *cache_tmp_fn = zsv_cache_filepath((const unsigned char *)filepath, ctype, 1, 1);
FILE *cache_data = NULL;
if (!(cache_fn && cache_tmp_fn))
return zsv_printerr(ENOMEM, "Out of memory!");
cache_data = zsv_fopen((void *)cache_fn, "rb");
int err = 0;
if (!cache_data) {
err = errno;
if (err == ENOENT)
err = 0;
else { // file exists but could not be opened
perror((const char *)cache_fn);
return err;
}
}
if (cache_data) {
// check that we have at least 1 byte of data
fseek(cache_data, 1, SEEK_SET);
if (!ftell(cache_data)) { // empty file; will use default value of "{}"
fclose(cache_data);
cache_data = NULL;
} else
fseek(cache_data, 0, SEEK_SET);
}
// jq filter to apply to [current_properties, id, value]
FILE *tmp = zsv_fopen((const char *)cache_tmp_fn, "wb");
if (!tmp) {
if (!(err = errno))
err = 1;
perror((const char *)cache_tmp_fn);
} else {
struct jv_to_json_ctx ctx = {0};
ctx.write1 = zsv_jq_fwrite1;
ctx.ctx = tmp;
ctx.flags = JV_PRINT_PRETTY | JV_PRINT_SPACE1;
enum zsv_jq_status jqstat;
void *jqh = zsv_jq_new(filter, jv_to_json_func, &ctx, &jqstat);
if (jqstat || !jqh)
err = zsv_printerr(-1, "Unable to initialize jq filter");
else if (!(jqstat = zsv_jq_parse(jqh, "[", 1))) {
if (cache_data)
jqstat = zsv_jq_parse_file(jqh, cache_data);
else
jqstat = zsv_jq_parse(jqh, "{}", 2);
if (!jqstat && !(jqstat = zsv_jq_parse(jqh, ",", 1)) &&
!(jqstat = zsv_jq_parse(jqh, json_value1, strlen((void *)json_value1))) &&
!(jqstat = zsv_jq_parse(jqh, ",", 1)) &&
!(jqstat = zsv_jq_parse(jqh, json_value2, strlen((void *)json_value2))) &&
!(jqstat = zsv_jq_parse(jqh, "]", 1)) && !(jqstat = zsv_jq_finish(jqh))) {
;
}
}
zsv_jq_delete(jqh);
if (cache_data) {
fclose(cache_data);
cache_data = NULL;
}
fclose(tmp);
if (!jqstat && zsv_replace_file(cache_tmp_fn, cache_fn)) {
err = zsv_printerr(-1, "Unable to save %s: ", cache_fn);
zsv_perror(NULL);
}
}
if (cache_data)
fclose(cache_data);
free(cache_fn);
free(cache_tmp_fn);
return err;
}
/**
* Returns the folder or file path to the cache for a given data file
* Caller must free the returned result
*/
unsigned char *zsv_cache_path(const unsigned char *data_filepath, const unsigned char *cache_filename, char temp_file) {
if (!data_filepath)
return NULL;
const unsigned char *last_slash = (void *)strrchr((void *)data_filepath, '/');
const unsigned char *last_backslash = (void *)strrchr((void *)data_filepath, '\\');
const unsigned char *dir_end = (!last_slash && !last_backslash ? NULL
: last_backslash > last_slash ? last_backslash
: last_slash);
char *s = NULL;
char *filename_suffix = NULL;
if (cache_filename)
asprintf(&filename_suffix, "%c%s%s", FILESLASH, cache_filename, temp_file ? ZSV_TEMPFILE_SUFFIX : "");
if (!dir_end) // file is in current dir
asprintf(&s, ZSV_CACHE_DIR "%c%s%s", FILESLASH, data_filepath, filename_suffix ? filename_suffix : "");
else if (dir_end[1]) {
asprintf(&s, "%.*s%c" ZSV_CACHE_DIR "%c%s%s", (int)(dir_end - data_filepath), data_filepath, FILESLASH, FILESLASH,
dir_end + 1, filename_suffix ? filename_suffix : "");
for (int i = 0; s && s[i]; i++)
if (s[i] != FILESLASH && (s[i] == '/' || s[i] == '\\'))
s[i] = FILESLASH;
}
free(filename_suffix);
return (unsigned char *)s;
}
|