#define _XOPEN_SOURCE #include #include #include #include #include #include #include "keyword.h" #include "linked_list.h" #include "macros.h" #include "misc.h" void *get_keyword_offset_ptr(const keyword *key, void *ptr) { char *ret = (char *)(ptr+key->offsets[0]); for (int i = 1; (int64_t)key->offsets[i] >= 0; ++i) { if (*(char **)ret != NULL) { ret = (*(char **)ret)+key->offsets[i]; } else { return NULL; } } return (void *)ret; } keyword_val get_keyword_value(const keyword *key, char *value, int *error) { keyword_val val = {0}; int dummy = 0; error = (error != NULL) ? error : &dummy; *error = 0; switch (key->type) { case TYPE_INT : case TYPE_BOOL : val.i = strtol(value, NULL, 0); break; case TYPE_TIME : if (key->time_fmt != NULL) { struct tm tm = {0}; if (strptime(value, key->time_fmt, &tm) != NULL) { tm.tm_isdst = -1; val.t = mktime(&tm); } else { *error = 3; } } else { *error = 4; } break; case TYPE_STRING: val.str = make_str(value); break; case TYPE_FLOAT : val.f = strtof(value, NULL); break; default : *error = 2; break; } return val; } keyword_val get_keyword(const keyword *key, void *data, void *ctx, int *error) { keyword_val val = {0}; int dummy = 0; error = (error != NULL) ? error : &dummy; *error = 0; if (data != NULL && key != NULL) { const int callback_ret = (key->get_callback != NULL) ? key->get_callback(ctx, data, key, &val) : 0; if (!callback_ret) { char *data_member = (char *)get_keyword_offset_ptr(key, data); if (data_member != NULL) { switch (key->type) { case TYPE_INT : case TYPE_BOOL : val.i = *(int *)data_member; break; case TYPE_TIME : val.t = *(time_t *)data_member; break; case TYPE_STRING: val.str = *(char **)data_member; break; case TYPE_FLOAT : val.f = *(float *)data_member; break; default : *error = 2; break; } } else { *error = 2; } } else if (callback_ret < 0) { *error = 1; } else { *error = -callback_ret; } } return val; } int set_keyword(const keyword *key, keyword_val val, void *ret, void *ctx) { const int callback_ret = (key->set_callback != NULL) ? key->set_callback(ctx, ret, key, val) : 0; if (callback_ret < 0 || callback_ret > 0) { return (callback_ret > 0) ? 0 : 5; } else { char *tmp_ret = (char *)get_keyword_offset_ptr(key, ret); if (tmp_ret != NULL) { switch (key->type) { case TYPE_INT : case TYPE_BOOL : *(int *)tmp_ret = val.i; break; case TYPE_TIME : *(time_t *)tmp_ret = val.t; break; case TYPE_STRING: *(char **)tmp_ret = val.str; break; case TYPE_FLOAT : *(float *)tmp_ret = val.f; break; default : return 6; break; } return 0; } else { return 7; } } } keyword_val parse_keyword(const keyword *key, char *key_str, char *value, int *error) { int dummy = 0; error = (error != NULL) ? error : &dummy; if (!strcmp(key->key, key_str)) { return get_keyword_value(key, value, error); } else { *error = 1; return (keyword_val){0}; } } int parse_keywords(const keyword **keys, char *key, char *value, void *ret, void *ctx) { keyword_val val = {0}; int error = 0; for (int i = 0; keys[i] != NULL; ++i) { val = parse_keyword(keys[i], key, value, &error); if (!error) { return set_keyword(keys[i], val, ret, ctx); } } return error; } int parse_key_value_file(void *ret, void *ctx, const keyword **keywords, char *buf, const char *delm, parse_callback *parse_cb) { if (buf != NULL) { int ret_val = 0; for (;;) { char *lhs, *rhs; /* Find the keyword before the delimiter(s). */ lhs = strtok_r(skip_whitespace(buf), delm, &buf); /* Remove any whitespace ahead of us. */ lhs = strtok_r(lhs, " \t\v\r\n", &rhs); /* Did we hit EOF? */ if (is_empty(lhs) || is_empty(buf)) { break; } else { int error; char *tmp = skip_whitespace(buf); /* Does the right hand side start with a double, or single quote? */ if (*tmp == '\"' || *tmp == '\'') { const char *delm = (*tmp == '\"') ? "\"" : "\'"; /* Get the string in between the start, and end qoute. */ rhs = get_str_delm_range(tmp, delm, delm, &buf); } else { /* Get the rest of the line. */ rhs = strtok_r(tmp, "\n", &buf); /* Remove any whitespace ahead of us. */ rhs = remove_trailing_whitespace(rhs); } /* Did we fail to parse the keyword? */ if (error = parse_keywords(keywords, lhs, rhs, ret, ctx)) { log(LOG_WARNING, "Failed to parse keyword \"%s\". Error code: %i", lhs, error); ret_val = error; } } /* Do we have a parse callback? */ if (parse_cb != NULL) { /* Did the callback return an error. */ if (parse_cb(&ret, ctx, buf) < 0) { ret_val = 8; break; } } } return ret_val; } else { return 9; } } static int has_delm(const keyword *key, keyword_val val, const char *delm) { const keyword_type type = key->type; const char *str = (type == TYPE_STRING) ? val.str : key->time_fmt; const int is_string_type = (type == TYPE_STRING || type == TYPE_TIME); return is_string_type && !is_empty(str) && !is_empty(delm) && !is_empty(find_delm((char *)str, delm, 0)); } static int delimiter_strlen(const delimiter *delim) { return format_len("%*s%*s", (int)strlen(delim->delm)+delim->lead_space, delim->delm, delim->trail_space, ""); } static char *delimiter_str(const delimiter *delim) { char *str = calloc(delimiter_strlen(delim)+1, sizeof(char)); sprintf(str, "%*s%*s", (int)strlen(delim->delm)+delim->lead_space, delim->delm, delim->trail_space, ""); return str; } static int key_value_strlen(const keyword *key, keyword_val val, const delimiter *start_delm, const delimiter *end_delm) { const char *quote = (has_delm(key, val, end_delm->delm)) ? "\"" : ""; int len = strlen(key->key)+delimiter_strlen(start_delm)+strlen(quote); struct tm tm; switch (key->type) { case TYPE_INT : case TYPE_BOOL : len += format_len("%i", val.i); break; case TYPE_TIME : localtime_r(&val.t, &tm); len += strftime(NULL, -1, key->time_fmt, &tm); break; case TYPE_STRING: len += format_len("%s", val.str); break; case TYPE_FLOAT : len += format_len("%f", val.f); break; default : break; } len += strlen(quote)+delimiter_strlen(end_delm); return len; } char *create_key_value_str(const keyword *key, keyword_val val, const delimiter *start_delm, const delimiter *end_delm) { char *start_delim = delimiter_str(start_delm); char *end_delim = delimiter_str(end_delm); char *key_value = calloc(key_value_strlen(key, val, start_delm, end_delm)+1, sizeof(char)); const char *quote = (has_delm(key, val, end_delm->delm)) ? "\"" : ""; int j = sprintf(key_value, "%s%s%s", key->key, start_delim, quote); struct tm tm; switch (key->type) { case TYPE_INT : case TYPE_BOOL : j += sprintf(key_value+j, "%i", val.i); break; case TYPE_TIME : localtime_r(&val.t, &tm); j += strftime(key_value+j, -1, key->time_fmt, &tm); break; case TYPE_STRING: j += sprintf(key_value+j, "%s", val.str); break; case TYPE_FLOAT : j += sprintf(key_value+j, "%f", val.f); break; default : break; } j += sprintf(key_value+j, "%s%s", quote, end_delim); free(start_delim); free(end_delim); return key_value; } char *create_key_value_file(void *data, void *ctx, const keyword **keywords, const delimiter *start_delm, const delimiter *end_delm) { if (data != NULL && keywords != NULL) { int buf_len = 0; char *key_value_buf; char *tmp; linked_list *key_values = NULL; for (int i = 0; keywords[i] != NULL; ++i) { int error; keyword_val val = get_keyword(keywords[i], data, ctx, &error); const int is_valid = keywords[i]->type != TYPE_STRING || (keywords[i]->type == TYPE_STRING && !is_empty(val.str)); if ((!error || error < 0) && is_valid) { char *key_value = create_key_value_str(keywords[i], val, start_delm, end_delm); buf_len += strlen(key_value); key_values = add_node(&key_values, key_value); if (keywords[i]->type == TYPE_STRING && error == -2) { free(val.str); } } } key_value_buf = calloc(buf_len+1, sizeof(char)); tmp = key_value_buf; for (linked_list *node = get_head(key_values); node != NULL; node = node->next) { char *key_value = (char *)node->data; tmp += sprintf(tmp, "%s", key_value); free(key_value); } cleanup_linked_list(key_values); return key_value_buf; } else { return NULL; } }