/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ /** * @addtogroup config-parser * @{ */ #include #include #include #include /** * @brief Config parser magic value used to check sanity of passed structs */ #define CONFIG_PARSER_MAGIC 0x464a6e2bUL /** * @brief Config parser type casting macro */ #define CONFIG_PARSER(p) ((struct config_parser *)(p)) /** * @brief Check if the supplied pointer is a valid @ref config_parser_handle_t * * If the pointer is invalid, the function using this macro will return with * CONFIG_PARSER_PARAM_ERR */ #define config_parser_check_handle(handle) do { \ if (!(handle) || \ ((struct config_parser *)(handle))->magic != CONFIG_PARSER_MAGIC) \ return CONFIG_PARSER_PARAM_ERR; \ } while (0) config_parser_handle_t config_parser_open_file(struct config_parser *config_parser, bool write, const char *file_name, char *working_buffer, size_t buff_size) { FRESULT res; if (!config_parser || !file_name || !working_buffer) return NULL; config_parser->magic = CONFIG_PARSER_MAGIC; config_parser->write = write; config_parser->buffer = working_buffer; config_parser->buff_size = buff_size; res = f_open(&config_parser->file, file_name, (write ? FA_CREATE_ALWAYS | FA_WRITE : FA_READ)); if (res != FR_OK) return NULL; return (config_parser_handle_t)config_parser; } /** * @brief Token delimiters for the config parser. */ static const char * const token_delim = " \t"; /** * @brief Parse a value in the configuration * @param entry Entry to parse the value in to * @param value_start_token char pointer holding the value. Must be null-terminated * @return 0 if successful */ static int parse_value(struct config_parser_entry *entry, char *value_start_token) { char *dot; char *endptr; /* Check if token is a float number */ dot = strstr(value_start_token, "."); if (dot) { /* Try parsing as float */ entry->value.float_val = strtof(value_start_token, &endptr); if (endptr == value_start_token) return -1; entry->type = CONFIG_PARSER_TYPE_FLOAT; goto exit; } if (value_start_token[0] != '-') { /* Try parsing as ul */ entry->value.uint_val = strtoul(value_start_token, &endptr, 0); if (endptr == value_start_token) return -1; entry->type = CONFIG_PARSER_TYPE_UINT; goto exit; } else { /* Try parsing as int */ entry->value.int_val = strtod(value_start_token, &endptr); if (endptr == value_start_token) return -1; entry->type = CONFIG_PARSER_TYPE_INT; } exit: return 0; } enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, struct config_parser_entry *entry, bool force_float) { struct config_parser *p; char *token; int token_round = 0; config_parser_check_handle(handle); p = CONFIG_PARSER(handle); if (!entry) return CONFIG_PARSER_PARAM_ERR; p->buffer[0] = '\0'; if (f_gets(p->buffer, (int)p->buff_size, &p->file) == NULL) return CONFIG_PARSER_IOERR; token = strtok(p->buffer, token_delim); while (token != NULL) { /* Check for comment */ if (token[0] == '#') { if (token_round == 0) return CONFIG_PARSER_LINE_COMMENT; break; } switch (token_round) { case 0: /* KEY */ entry->name = token; break; case 1: /* = Symbol */ if (strcmp(token, "=")) return CONFIG_PARSER_LINE_MALFORM; break; case 2: /* VALUE */ if (parse_value(entry, token)) return CONFIG_PARSER_LINE_MALFORM; break; default: return CONFIG_PARSER_LINE_MALFORM; } token_round++; token = strtok(NULL, token_delim); } if (force_float) { if (entry->type == CONFIG_PARSER_TYPE_INT) entry->value.float_val = (float)entry->value.int_val; if (entry->type == CONFIG_PARSER_TYPE_UINT) entry->value.float_val = (float)entry->value.uint_val; entry->type = CONFIG_PARSER_TYPE_FLOAT; } return CONFIG_PARSER_OK; } enum config_parser_ret config_parser_reset_to_start(config_parser_handle_t handle) { FRESULT res; struct config_parser *p; config_parser_check_handle(handle); p = CONFIG_PARSER(handle); res = f_rewind(&p->file); if (res != FR_OK) return CONFIG_PARSER_IOERR; return CONFIG_PARSER_OK; } enum config_parser_ret config_parser_write_entry(config_parser_handle_t handle, const struct config_parser_entry *entry) { (void)entry; config_parser_check_handle(handle); return CONFIG_PARSER_OK; } enum config_parser_ret config_parser_close_file(config_parser_handle_t handle) { struct config_parser *p; FRESULT res; config_parser_check_handle(handle); p = CONFIG_PARSER(handle); res = f_close(&p->file); return (res == FR_OK ? CONFIG_PARSER_OK : CONFIG_PARSER_IOERR); } bool config_parser_ret_is_abort_condition(enum config_parser_ret return_val) { if (return_val == CONFIG_PARSER_END_REACHED || return_val == CONFIG_PARSER_GENERIC_ERR || return_val == CONFIG_PARSER_IOERR || return_val == CONFIG_PARSER_PARAM_ERR) return true; return false; } /** @} */