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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <zsv.h>
#include <zsv/utils/prop.h>
#include <zsv/utils/cache.h>
#include <zsv/utils/file.h>
#include <zsv/utils/overwrite.h>
#include <yajl_helper/yajl_helper.h>
#ifndef ZSVTLS
#ifndef NO_THREADING
#define ZSVTLS _Thread_local
#else
#define ZSVTLS
#endif
#endif
// see arg.c
static struct zsv_prop_handler *zsv_with_default_custom_prop_handler(char mode) {
ZSVTLS static char zsv_default_custom_prop_initd = 0;
ZSVTLS static struct zsv_prop_handler zsv_default_custom_prop_handler = {0};
switch (mode) {
case 'c': // clear
memset(&zsv_default_custom_prop_handler, 0, sizeof(zsv_default_custom_prop_handler));
zsv_default_custom_prop_initd = 0;
break;
case 'g': // get
if (!zsv_default_custom_prop_initd) {
zsv_default_custom_prop_initd = 1;
zsv_default_custom_prop_handler.handler = NULL;
zsv_default_custom_prop_handler.ctx = NULL;
}
break;
}
return &zsv_default_custom_prop_handler;
}
ZSV_EXPORT
void zsv_clear_default_custom_prop_handler(void) {
zsv_with_default_custom_prop_handler('c');
}
ZSV_EXPORT
struct zsv_prop_handler zsv_get_default_custom_prop_handler(void) {
return *zsv_with_default_custom_prop_handler('g');
}
ZSV_EXPORT
void zsv_set_default_custom_prop_handler(struct zsv_prop_handler custom_prop_handler) {
*zsv_with_default_custom_prop_handler(0) = custom_prop_handler;
}
// TO DO: import these through a proper header
static int zsv_properties_parse_process_value(yajl_helper_t yh, struct json_value *value);
struct zsv_properties_parser {
yajl_helper_t yh;
yajl_status stat;
// queryable data
struct zsv_file_properties *fp;
struct zsv_opts *opts;
struct zsv_prop_handler *custom_prop_handler;
const unsigned char *filepath; // path to this properties file
};
const unsigned char *zsv_properties_parser_get_filepath(void *p_) {
struct zsv_properties_parser *p = p_;
return p ? p->filepath : NULL;
}
void *zsv_properties_parser_get_custom_ctx(void *p_) {
struct zsv_properties_parser *p = p_;
return p && p->custom_prop_handler ? p->custom_prop_handler->ctx : NULL;
}
struct zsv_opts *zsv_properties_parser_get_opts(void *p_) {
struct zsv_properties_parser *p = p_;
return p ? p->opts : NULL;
}
/**
* Create a new properties parser
*/
struct zsv_properties_parser *zsv_properties_parser_new(const unsigned char *path,
struct zsv_prop_handler *custom_prop_handler,
struct zsv_file_properties *fp, struct zsv_opts *opts) {
struct zsv_properties_parser *parser = calloc(1, sizeof(*parser));
if (parser) {
parser->yh = yajl_helper_new(32,
NULL, // start_map,
NULL, // end_map,
NULL, // map_key,
NULL, // start_array,
NULL, // end_array,
zsv_properties_parse_process_value, parser);
if (!parser->yh)
free(parser);
else {
parser->custom_prop_handler = custom_prop_handler;
parser->fp = fp;
parser->filepath = path;
parser->opts = opts;
parser->stat = yajl_status_ok;
return parser;
}
}
// error
return NULL;
}
/**
* Finished parsing
*/
enum zsv_status zsv_properties_parse_complete(struct zsv_properties_parser *parser) {
if (parser && parser->stat == yajl_status_ok)
parser->stat = yajl_complete_parse(yajl_helper_yajl(parser->yh));
return parser && parser->stat == yajl_status_ok ? zsv_status_ok : zsv_status_error;
}
/**
* Clean up
*/
enum zsv_status zsv_properties_parser_destroy(struct zsv_properties_parser *parser) {
yajl_helper_delete(parser->yh);
yajl_status stat = parser->stat;
free(parser);
return stat == yajl_status_ok ? zsv_status_ok : zsv_status_error;
}
/**
* Load cached file properties into a zsp_opts and/or zsv_file_properties struct
*
* @param data_filepath required file path
* @param opts (optional) parser options to load. will be updated to reflect
* what is actually used
* @param custom_prop_handler (optional) handler for custom properties
* @return zsv_status_ok on success
*/
struct zsv_file_properties zsv_cache_load_props(const char *data_filepath, struct zsv_opts *opts,
struct zsv_prop_handler *custom_prop_handler) {
// we need some memory to save the parsed properties
// if the caller did not provide that, use our own
struct zsv_file_properties tmp = {0};
if (!(data_filepath && *data_filepath))
return tmp; // e.g. input = stdin
struct zsv_file_properties *fp = &tmp;
struct zsv_properties_parser *p = NULL;
unsigned char *fn = zsv_cache_filepath((const unsigned char *)data_filepath, zsv_cache_type_property, 0, 0);
if (!fn)
tmp.stat = zsv_status_memory;
else {
FILE *f;
int err;
if (!zsv_file_readable((char *)fn, &err, &f)) {
if (err != ENOENT) {
perror((const char *)fn);
tmp.stat = zsv_status_error;
}
} else {
p = zsv_properties_parser_new(fn, custom_prop_handler, fp, opts);
if (!p)
tmp.stat = zsv_status_memory;
else if (p->stat != yajl_status_ok)
tmp.stat = zsv_status_error;
else {
unsigned char buff[1024];
size_t bytes_read;
while ((bytes_read = fread(buff, 1, sizeof(buff), f))) {
if ((p->stat = yajl_parse(yajl_helper_yajl(p->yh), buff, bytes_read)) != yajl_status_ok) {
tmp.stat = zsv_status_error;
break;
}
}
if (tmp.stat == zsv_status_ok)
zsv_properties_parse_complete(p);
}
fclose(f);
}
free(fn);
}
if (tmp.stat == zsv_status_ok) {
// warn if the loaded properties conflict with command-line options
if (fp->skip_specified) {
if (opts && opts->option_overrides.skip_head && opts->rows_to_ignore != fp->skip)
fprintf(stderr, "Warning: file property 'skip-head' overridden by command option\n");
else if (opts)
opts->rows_to_ignore = fp->skip;
}
if (fp->header_span_specified) {
if (opts && opts->option_overrides.header_row_span && opts->header_span != fp->header_span)
fprintf(stderr, "Warning: file property 'header-row-span' overridden by command option\n");
else if (opts)
opts->header_span = fp->header_span;
}
}
if (p && tmp.stat == zsv_status_ok && zsv_properties_parser_destroy(p) != zsv_status_ok)
tmp.stat = zsv_status_error;
return tmp;
}
static int zsv_properties_parse_process_value(yajl_helper_t yh, struct json_value *value) {
struct zsv_properties_parser *parser = yajl_helper_ctx(yh);
struct zsv_file_properties *fp = parser->fp;
if (yajl_helper_level_raw(yh) == 1) {
const char *prop_name = yajl_helper_get_map_key(yh, 0);
unsigned int *target = NULL;
if (!strcmp(prop_name, "skip-head")) {
target = &fp->skip;
fp->skip_specified = 1;
} else if (!strcmp(prop_name, "header-row-span")) {
target = &fp->header_span;
fp->header_span_specified = 1;
}
if (!target) {
int rc = 1;
struct zsv_prop_handler *custom_prop_handler = parser->custom_prop_handler;
if (custom_prop_handler && custom_prop_handler->handler)
rc = custom_prop_handler->handler(parser, prop_name, value);
if (rc) {
fprintf(stderr, "Unrecognized property: %s\n", prop_name);
fp->stat = zsv_status_error;
}
} else {
int err = 0;
long long i = json_value_long(value, &err);
if (err || i < 0 || i > UINT_MAX) {
fp->stat = zsv_status_error;
fprintf(stderr, "Invalid %s property value: should be an integer between 0 and %u", prop_name, UINT_MAX);
} else
*target = (unsigned int)i;
}
}
return 1;
}
/**
* zsv_new_with_properties(): use in lieu of zsv_new() to also merge zsv options
* with any saved properties (such as rows_to_ignore or header_span) for the
* specified input file. In the event that saved properties conflict with a
* command-line option, the command-line option "wins" (the property value is
* ignored), but a warning is printed
*
* optional `struct zsv_file_properties` supports custom file property processing
*/
enum zsv_status zsv_new_with_properties(struct zsv_opts *opts, struct zsv_prop_handler *custom_prop_handler,
const char *input_path, zsv_parser *handle_out) {
*handle_out = NULL;
if (input_path) {
struct zsv_file_properties fp = zsv_cache_load_props(input_path, opts, custom_prop_handler);
if (fp.stat != zsv_status_ok)
return fp.stat;
}
#ifdef ZSV_EXTRAS
if (opts->overwrite_auto)
zsv_overwrite_auto(opts, input_path);
#endif
if ((*handle_out = zsv_new(opts)))
return zsv_status_ok;
return zsv_status_memory;
}
|