/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2016 James Willis (james.s.willis@durham.ac.uk)
* 2017-2018 Peter W. Draper (p.w.draper@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* Some standard headers. */
/* Needs to be included so that strtok returns char * instead of a int *. */
#include
#include
#include
/* This object's header. */
#include "parser.h"
/* Local headers. */
#include "common_io.h"
#include "error.h"
#include "restart.h"
#include "tools.h"
#include "version.h"
#define PARSER_COMMENT_STRING "#"
#define PARSER_COMMENT_CHAR '#'
#define PARSER_VALUE_CHAR ':'
#define PARSER_VALUE_STRING ":"
#define PARSER_START_OF_FILE "---"
#define PARSER_END_OF_FILE "..."
#define CHUNK 10
/* Alias of long long needed and used only for the preprocessor definitions */
typedef long long longlong;
/* Private functions. */
static int is_empty(const char *str);
static int count_indentation(const char *str);
static void parse_line(char *line, struct swift_params *params);
static void parse_value(char *line, struct swift_params *params);
static void parse_section_param(char *line, int *isFirstParam,
char *sectionName, struct swift_params *params);
static void find_duplicate_params(const struct swift_params *params,
const char *param_name);
static void find_duplicate_section(const struct swift_params *params,
const char *section_name);
static int lineNumber = 0;
/**
* @brief parse a YAML list of strings returning a set of pointers to
* the strings.
*
* It is assumed that the [] have been removed (also no lists in lists)
* words are separated by commas and the strings may or may not be quoted.
* So lines like:
*
* 'xyz', 'ABC', "ab'c", "de:f", "g,hi", "zzz", Hello World, again
*
* Are supported as expected.
*
* @param line the line to parse.
* @param result array of pointers to the strings.
* @return the number of strings
*/
static int parse_quoted_strings(const char *line, char ***result) {
char word[PARSER_MAX_LINE_SIZE];
int nchar = 0;
int nwords = 0;
char quote = '\0';
/* Preallocate a number of pointers. */
char **strings;
int count = CHUNK;
strings = (char **)malloc(count * sizeof(char *));
word[0] = '\0';
for (unsigned int i = 0; i < strlen(line); i++) {
char c = line[i];
if (c == '"' || c == '\'') {
if (c == quote) {
quote = '\0';
} else if (!quote) {
quote = c;
} else {
word[nchar++] = c;
}
} else if (c == ',') {
if (!quote) {
/* Save word. */
word[nchar++] = '\0';
if (count <= nwords) {
count += CHUNK;
strings = (char **)realloc(strings, count * sizeof(char *));
}
strings[nwords] = (char *)malloc((strlen(word) + 1) * sizeof(char));
strcpy(strings[nwords], trim_both(word));
nwords++;
/* Ready for next. */
nchar = 0;
word[0] = '\0';
} else {
word[nchar++] = c;
}
} else {
word[nchar++] = c;
}
}
/* Keep unfinished words. */
if (nchar > 0) {
word[nchar] = '\0';
if (count <= nwords) {
count += 1;
strings = (char **)realloc(strings, count * sizeof(char *));
}
strings[nwords] = (char *)malloc((strlen(word) + 1) * sizeof(char));
strcpy(strings[nwords], trim_both(word));
nwords++;
}
*result = strings;
return nwords;
}
/**
* @brief Initialize the parser structure.
*
* @param file_name Name of file to be read
* @param params Structure to be populated from file
*/
void parser_init(const char *file_name, struct swift_params *params) {
params->paramCount = 0;
params->sectionCount = 0;
strcpy(params->fileName, file_name);
}
/**
* @brief Reads an input file and stores each parameter in a structure.
*
* @param file_name Name of file to be read
* @param params Structure to be populated from file
*/
void parser_read_file(const char *file_name, struct swift_params *params) {
/* Open file for reading */
FILE *file = fopen(file_name, "r");
/* Line to parsed. */
char line[PARSER_MAX_LINE_SIZE];
/* Initialise parameter count. */
parser_init(file_name, params);
/* Check if parameter file exits. */
if (file == NULL) {
error("Error opening parameter file: %s", file_name);
}
/* Read until the end of the file is reached.*/
while (!feof(file)) {
if (fgets(line, PARSER_MAX_LINE_SIZE, file) != NULL) {
lineNumber++;
parse_line(line, params);
}
}
fclose(file);
}
/**
* @brief Set or update a parameter using a compressed format.
*
* The compressed format allows a value to be given as a single
* string and has the format "section:parameter:value", with all
* names as would be given in the parameter file.
*
* @param params Structure that holds the parameters.
* @param namevalue the parameter name and value as described.
*/
void parser_set_param(struct swift_params *params, const char *namevalue) {
/* Get the various parts. */
char name[PARSER_MAX_LINE_SIZE];
char value[PARSER_MAX_LINE_SIZE];
char section[PARSER_MAX_LINE_SIZE];
name[0] = '\0';
value[0] = '\0';
/* Name is part until second colon. */
const char *p1 = strchr(namevalue, ':');
if (p1 != NULL) {
/* Section is first part until a colon. */
memcpy(section, namevalue, p1 - namevalue);
section[p1 - namevalue] = ':';
section[p1 - namevalue + 1] = '\0';
const char *p2 = strchr(p1 + 1, ':');
if (p2 != NULL) {
memcpy(name, namevalue, p2 - namevalue);
name[p2 - namevalue] = '\0';
/* Value is rest after second colon. */
p2++;
strcpy(value, p2);
}
}
/* Sanity check. */
if (strlen(name) == 0 || strlen(value) == 0 || strchr(value, ':') != NULL)
error(
"Cannot parse compressed parameter string: '%s', check syntax "
"should be section:parameter:value",
namevalue);
/* And update or set. */
int updated = 0;
for (int i = 0; i < params->paramCount; i++) {
if (strcmp(name, params->data[i].name) == 0) {
message("Value of '%s' changed from '%s' to '%s'", params->data[i].name,
params->data[i].value, value);
strcpy(params->data[i].value, trim_both(value));
updated = 1;
}
}
if (!updated) {
/* Is this a new section? */
int newsection = 1;
for (int i = 0; i < params->sectionCount; i++) {
if (strcmp(section, params->section[i].name) == 0) {
newsection = 0;
break;
}
}
if (newsection) {
strcpy(params->section[params->sectionCount].name, section);
params->sectionCount++;
if (params->sectionCount == PARSER_MAX_NO_OF_SECTIONS)
error("Too many sections, current maximum is %d.",
params->sectionCount);
}
strcpy(params->data[params->paramCount].name, name);
strcpy(params->data[params->paramCount].value, value);
params->data[params->paramCount].used = 0;
params->data[params->paramCount].is_default = 0;
params->paramCount++;
if (params->paramCount == PARSER_MAX_NO_OF_PARAMS)
error("Too many parameters, current maximum is %d.", params->paramCount);
}
}
/**
* @brief Counts the number of white spaces that prefix a string.
*
* @param str String to be checked
*
* @return Number of white spaces prefixing str
*/
static int count_indentation(const char *str) {
int count = 0;
/* Check if the line contains the character */
while (*(++str) == ' ') {
count++;
}
return count;
}
/**
* @brief Checks if a string is empty.
*
* @param str String to be checked
*
* @return Returns 1 if str is empty, 0 otherwise
*/
static int is_empty(const char *str) {
int retParam = 1;
while (*str != '\0') {
if (!isspace(*str)) {
retParam = 0;
break;
}
str++;
}
return retParam;
}
/**
* @brief Look for duplicate parameters.
*
* @param params Structure that holds the parameters
* @param param_name Name of parameter to be searched for
*/
static void find_duplicate_params(const struct swift_params *params,
const char *param_name) {
for (int i = 0; i < params->paramCount; i++) {
if (!strcmp(param_name, params->data[i].name)) {
error("Invalid line:%d '%s', parameter is a duplicate.", lineNumber,
param_name);
}
}
}
/**
* @brief Look for duplicate sections.
*
* @param params Structure that holds the parameters
* @param section_name Name of section to be searched for
*/
static void find_duplicate_section(const struct swift_params *params,
const char *section_name) {
for (int i = 0; i < params->sectionCount; i++) {
if (!strcmp(section_name, params->section[i].name)) {
error("Invalid line:%d '%s', section is a duplicate.", lineNumber,
section_name);
}
}
}
/**
* @brief Parses a line from a file and stores any parameters in a structure.
*
* @param line Line to be parsed.
* @param params Structure to be populated from file.
*/
static void parse_line(char *line, struct swift_params *params) {
/* Parse line if it doesn't begin with a comment. */
if (line[0] != PARSER_COMMENT_CHAR) {
char trim_line[PARSER_MAX_LINE_SIZE];
char tmp_str[PARSER_MAX_LINE_SIZE];
char *token;
/* Remove comments at the end of a line. */
token = strtok(line, PARSER_COMMENT_STRING);
strcpy(tmp_str, token);
/* Check if the line is just white space. */
if (!is_empty(tmp_str)) {
/* Trim '\n' characters from string. */
token = strtok(tmp_str, "\n");
strcpy(trim_line, token);
/* Check if the line contains a value and parse it. */
if (strchr(trim_line, PARSER_VALUE_CHAR)) {
/* Trim trailing space before parsing line for a value. */
char no_space_line[PARSER_MAX_LINE_SIZE];
strcpy(no_space_line, trim_trailing(trim_line));
parse_value(no_space_line, params);
}
/* Check for invalid lines,not including the start and end of file. */
else if (!strcmp(trim_line, PARSER_START_OF_FILE) &&
!strcmp(trim_line, PARSER_END_OF_FILE)) {
error("Invalid line:%d '%s'.", lineNumber, trim_line);
}
}
}
}
/**
* @brief Performs error checking and stores a parameter in a structure.
*
* @param line Line containing the parameter
* @param params Structure to be written to
*
*/
static void parse_value(char *line, struct swift_params *params) {
static int inSection = 0;
static char section[PARSER_MAX_LINE_SIZE]; /* Keeps track of current section
name. */
static int isFirstParam = 1;
char tmpStr[PARSER_MAX_LINE_SIZE];
char tmpSectionName[PARSER_MAX_LINE_SIZE];
char *token;
/* Check that standalone parameters have correct indentation. */
if (!inSection && line[0] == ' ') {
error(
"Invalid line:%d '%s', standalone parameter defined with incorrect "
"indentation.",
lineNumber, line);
}
/* Check that it is a parameter inside a section.*/
if (line[0] == ' ' || line[0] == '\t') {
parse_section_param(line, &isFirstParam, section, params);
} else {
/* It is the start of a new section or standalone parameter.
* Take first token as the parameter name. */
token = strtok(line, ":\t");
strcpy(tmpStr, trim_trailing(token));
/* Take second token as the parameter value. */
token = trim_both(strtok(NULL, "#\n"));
/* If second token is NULL or empty then the line must be a section
* heading. */
if (token == NULL || strlen(token) == 0) {
strcpy(tmpSectionName, tmpStr);
strcat(tmpSectionName, PARSER_VALUE_STRING);
/* Check for duplicate section name. */
find_duplicate_section(params, tmpSectionName);
/* Check for duplicate standalone parameter name used as a section name.
*/
find_duplicate_params(params, tmpStr);
strcpy(section, tmpSectionName);
strcpy(params->section[params->sectionCount].name, tmpSectionName);
if (params->sectionCount == PARSER_MAX_NO_OF_SECTIONS - 1) {
error(
"Maximal number of sections in parameter file reached. Aborting !");
} else {
params->sectionCount++;
}
inSection = 1;
isFirstParam = 1;
} else {
/* Create string with standalone parameter name appended with ":" to aid
* duplicate search as section names are stored with ":" at the end.*/
strcpy(tmpSectionName, tmpStr);
strcat(tmpSectionName, PARSER_VALUE_STRING);
/* Check for duplicate parameter name. */
find_duplicate_params(params, tmpStr);
/* Check for duplicate section name used as standalone parameter name. */
find_duplicate_section(params, tmpSectionName);
/* Must be a standalone parameter so no need to prefix name with a
* section. */
strcpy(params->data[params->paramCount].name, tmpStr);
strcpy(params->data[params->paramCount].value, token);
params->data[params->paramCount].used = 0;
params->data[params->paramCount].is_default = 0;
if (params->paramCount == PARSER_MAX_NO_OF_PARAMS - 1) {
error(
"Maximal number of parameters in parameter file reached. Aborting "
"!");
} else {
params->paramCount++;
}
inSection = 0;
isFirstParam = 1;
}
}
}
/**
* @brief Parses a parameter that appears in a section and stores it in a
*structure.
*
* @param line Line containing the parameter
* @param isFirstParam Shows if the first parameter of a section has been found
* @param sectionName String containing the current section name
* @param params Structure to be written to
*
*/
static void parse_section_param(char *line, int *isFirstParam,
char *sectionName,
struct swift_params *params) {
static int sectionIndent = 0;
char tmpStr[PARSER_MAX_LINE_SIZE];
char paramName[PARSER_MAX_LINE_SIZE];
char *token;
/* Count indentation of each parameter and check that it
* is consistent with the first parameter in the section. */
if (*isFirstParam) {
sectionIndent = count_indentation(line);
*isFirstParam = 0;
} else if (count_indentation(line) != sectionIndent) {
error("Invalid line:%d '%s', parameter has incorrect indentation.",
lineNumber, line);
}
/* Take first token as the parameter name and trim leading white space. */
token = trim_both(strtok(line, ":\t"));
strcpy(tmpStr, token);
/* Take second token as the parameter value. */
token = trim_both(strtok(NULL, "#\n"));
/* Prefix the parameter name with its section name and
* copy it into the parameter structure. */
strcpy(paramName, sectionName);
strcat(paramName, tmpStr);
/* Check for duplicate parameter name. */
find_duplicate_params(params, paramName);
/* Choke on invalid input */
if (token == NULL)
error(
"Invalid parameter value for parameter '%s'. Cannot be an empty "
"string.",
paramName);
strcpy(params->data[params->paramCount].name, paramName);
strcpy(params->data[params->paramCount].value, token);
params->data[params->paramCount].used = 0;
params->data[params->paramCount].is_default = 0;
if (params->paramCount == PARSER_MAX_NO_OF_PARAMS - 1) {
error("Maximal number of parameters in parameter file reached. Aborting !");
} else {
params->paramCount++;
}
}
/**
* @brief Checks whether a parameter is currently present in the params
* structure. Do not use this function to test if a parameter exists,
* and then provide an optional value instead. That can be achieved
* using the parser_get_opt_param_TYPE functions.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @return Whether or not the parameter exists as a boolean.
**/
int parser_does_param_exist(struct swift_params *params, const char *name) {
for (int i = 0; i < params->paramCount; i++) {
if (strcmp(name, params->data[i].name) == 0) {
return 1;
}
}
return 0;
}
// Retrieve parameter value from structure. TYPE is the data type, float, int
// etc. FMT the format required for that data type, i.e. %f, %d etc. and DESC
// a one word description of the type, "float", "int" etc.
#define PARSER_GET_VALUE(TYPE, FMT, DESC) \
static int get_param_##TYPE(struct swift_params *params, const char *name, \
TYPE *def, TYPE *result) { \
char str[PARSER_MAX_LINE_SIZE]; \
for (int i = 0; i < params->paramCount; i++) { \
if (strcmp(name, params->data[i].name) == 0) { \
/* Check that exactly one number is parsed, capture junk. */ \
if (sscanf(params->data[i].value, " " FMT "%s ", result, str) != 1) { \
error("Tried parsing " DESC \
" '%s' but found '%s' with " \
"illegal trailing characters '%s'.", \
params->data[i].name, params->data[i].value, str); \
} \
/* Ensure same behavior if called multiple times for same parameter */ \
if (params->data[i].is_default && def == NULL) \
error( \
"Tried parsing %s again but cannot parse a default " \
"parameter as mandatory", \
name); \
if (params->data[i].is_default && *def != *result) \
error( \
"Tried parsing %s again but cannot parse a parameter with " \
"two different default value (" FMT "!=" FMT ")", \
name, *def, *result); \
/* This parameter has been used */ \
params->data[i].used = 1; \
return 1; \
} \
} \
if (def == NULL) \
error("Cannot find '%s' in the structure, in file '%s'.", name, \
params->fileName); \
return 0; \
}
// Set a parameter to a value and save for dumping.
#define PARSER_SAVE_VALUE(PREFIX, TYPE, FMT) \
static void save_param_##PREFIX(struct swift_params *params, \
const char *name, TYPE value) { \
char str[PARSER_MAX_LINE_SIZE]; \
sprintf(str, "%s:" FMT, name, value); \
parser_set_param(params, str); \
params->data[params->paramCount - 1].used = 1; \
params->data[params->paramCount - 1].is_default = 0; \
}
/* Instantiations. */
PARSER_GET_VALUE(char, "%c", "char");
PARSER_GET_VALUE(int, "%d", "int");
PARSER_GET_VALUE(float, "%f", "float");
PARSER_GET_VALUE(double, "%lf", "double");
PARSER_GET_VALUE(longlong, "%lld", "long long");
PARSER_SAVE_VALUE(char, char, "%c");
PARSER_SAVE_VALUE(int, int, "%d");
PARSER_SAVE_VALUE(float, float, "%g");
PARSER_SAVE_VALUE(double, double, "%g");
PARSER_SAVE_VALUE(longlong, longlong, "%lld");
PARSER_SAVE_VALUE(string, const char *, "%s");
/**
* @brief Retrieve integer parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @return Value of the parameter found
*/
int parser_get_param_int(struct swift_params *params, const char *name) {
int result = 0;
get_param_int(params, name, NULL, &result);
return result;
}
/**
* @brief Retrieve char parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @return Value of the parameter found
*/
char parser_get_param_char(struct swift_params *params, const char *name) {
char result = 0;
get_param_char(params, name, NULL, &result);
return result;
}
/**
* @brief Retrieve float parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @return Value of the parameter found
*/
float parser_get_param_float(struct swift_params *params, const char *name) {
float result = 0;
get_param_float(params, name, NULL, &result);
return result;
}
/**
* @brief Retrieve double parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @return Value of the parameter found
*/
double parser_get_param_double(struct swift_params *params, const char *name) {
double result = 0;
get_param_double(params, name, NULL, &result);
return result;
}
/**
* @brief Retrieve long long parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @return Value of the parameter found
*/
long long parser_get_param_longlong(struct swift_params *params,
const char *name) {
long long result = 0;
get_param_longlong(params, name, NULL, &result);
return result;
}
/**
* @brief Retrieve string parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param retParam (return) Value of the parameter found
*/
void parser_get_param_string(struct swift_params *params, const char *name,
char *retParam) {
for (int i = 0; i < params->paramCount; i++) {
if (!strcmp(name, params->data[i].name)) {
if (params->data[i].is_default)
error(
"Tried parsing %s again but cannot parse a "
"default parameter as mandatory",
name);
strcpy(retParam, params->data[i].value);
/* this parameter has been used */
params->data[i].used = 1;
return;
}
}
error("Cannot find '%s' in the structure.", name);
}
/**
* @brief Retrieve optional integer parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param def Default value of the parameter of not found.
* @return Value of the parameter found
*/
int parser_get_opt_param_int(struct swift_params *params, const char *name,
int def) {
int result = 0;
if (get_param_int(params, name, &def, &result)) return result;
save_param_int(params, name, def);
params->data[params->paramCount - 1].is_default = 1;
return def;
}
/**
* @brief Retrieve optional char parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param def Default value of the parameter of not found.
* @return Value of the parameter found
*/
char parser_get_opt_param_char(struct swift_params *params, const char *name,
char def) {
char result = 0;
if (get_param_char(params, name, &def, &result)) return result;
save_param_char(params, name, def);
params->data[params->paramCount - 1].is_default = 1;
return def;
}
/**
* @brief Retrieve optional float parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param def Default value of the parameter of not found.
* @return Value of the parameter found
*/
float parser_get_opt_param_float(struct swift_params *params, const char *name,
float def) {
float result = 0;
if (get_param_float(params, name, &def, &result)) return result;
save_param_float(params, name, def);
params->data[params->paramCount - 1].is_default = 1;
return def;
}
/**
* @brief Retrieve optional double parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param def Default value of the parameter of not found.
* @return Value of the parameter found
*/
double parser_get_opt_param_double(struct swift_params *params,
const char *name, double def) {
double result = 0;
if (get_param_double(params, name, &def, &result)) return result;
save_param_double(params, name, def);
params->data[params->paramCount - 1].is_default = 1;
return def;
}
/**
* @brief Retrieve optional long long parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param def Default value of the parameter of not found.
* @return Value of the parameter found
*/
long long parser_get_opt_param_longlong(struct swift_params *params,
const char *name, long long def) {
long long result = 0;
if (get_param_longlong(params, name, &def, &result)) return result;
save_param_longlong(params, name, def);
params->data[params->paramCount - 1].is_default = 1;
return def;
}
/**
* @brief Retrieve string parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param def Default value of the parameter of not found.
* @param retParam (return) Value of the parameter found
*/
void parser_get_opt_param_string(struct swift_params *params, const char *name,
char *retParam, const char *def) {
for (int i = 0; i < params->paramCount; i++) {
if (!strcmp(name, params->data[i].name)) {
strcpy(retParam, params->data[i].value);
/* Ensure same behavior if called multiple times for same parameter */
if (params->data[i].is_default && strcmp(def, retParam) != 0)
error(
"Tried parsing %s again but cannot parse a parameter with "
"two different default values ('%s' != '%s')",
name, def, retParam);
/* this parameter has been used */
params->data[i].used = 1;
return;
}
}
save_param_string(params, name, def);
params->data[params->paramCount - 1].is_default = 1;
strcpy(retParam, def);
}
/* Macro defining functions that get primitive types as simple one-line YAML
* arrays, that is SEC: [v1,v2,v3...] format, with the extension that the []
* are optional. TYPE is the data type, float etc. FMT a format to parse a
* single value, so "%f" for a float and DESC the type description
* i.e. "float".
*/
#define PARSER_GET_ARRAY(TYPE, FMT, DESC) \
static int get_param_##TYPE##_array(struct swift_params *params, \
const char *name, int required, \
int nval, TYPE *values) { \
char str[PARSER_MAX_LINE_SIZE]; \
char cpy[PARSER_MAX_LINE_SIZE]; \
\
for (int i = 0; i < params->paramCount; i++) { \
if (!strcmp(name, params->data[i].name)) { \
if (params->data[i].is_default && required) \
error( \
"Tried parsing %s again but cannot parse a default " \
"parameter as mandatory", \
name); \
char *cp = cpy; \
strcpy(cp, params->data[i].value); \
cp = trim_both(cp); \
\
/* Strip off [], if present. */ \
if (cp[0] == '[') cp++; \
int l = strlen(cp); \
if (cp[l - 1] == ']') cp[l - 1] = '\0'; \
cp = trim_both(cp); \
\
/* Format that captures spaces and trailing junk. */ \
char fmt[20]; \
sprintf(fmt, " %s%%s ", FMT); \
\
/* Parse out values which should now be "v, v, v" with \
* internal whitespace variations. */ \
char *p = strtok(cp, ","); \
for (int k = 0; k < nval; k++) { \
if (p != NULL) { \
TYPE tmp_value; \
if (sscanf(p, fmt, &tmp_value, str) != 1) { \
error("Tried parsing " DESC \
" '%s' but found '%s' with " \
"illegal " DESC " characters '%s'.", \
name, p, str); \
} \
if (params->data[i].is_default && tmp_value != values[k]) \
error( \
"Tried parsing %s again but cannot parse a " \
"parameter with two different default value " \
"(" FMT "!=" FMT ")", \
name, tmp_value, values[k]); \
values[k] = tmp_value; \
} else { \
error( \
"Array '%s' with value '%s' has too few values, " \
"expected %d", \
name, params->data[i].value, nval); \
} \
if (k < nval - 1) p = strtok(NULL, ","); \
} \
params->data[i].used = 1; \
return 1; \
} \
} \
if (required) \
error("Cannot find '%s' in the structure, in file '%s'.", name, \
params->fileName); \
return 0; \
}
// Set values of a default parameter so they will be saved correctly.
#define PARSER_SAVE_ARRAY(TYPE, FMT) \
static int save_param_##TYPE##_array( \
struct swift_params *params, const char *name, int nval, TYPE *values) { \
/* Save values against the parameter. */ \
char str[PARSER_MAX_LINE_SIZE]; \
int k = sprintf(str, "%s: [", name); \
for (int i = 0; i < nval - 1; i++) \
k += sprintf(&str[k], FMT ", ", values[i]); \
sprintf(&str[k], FMT "]", values[nval - 1]); \
parser_set_param(params, str); \
params->data[params->paramCount - 1].used = 1; \
params->data[params->paramCount - 1].is_default = 0; \
return 0; \
}
/* Instantiations. */
PARSER_GET_ARRAY(char, "%c", "char");
PARSER_GET_ARRAY(int, "%d", "int");
PARSER_GET_ARRAY(float, "%f", "float");
PARSER_GET_ARRAY(double, "%lf", "double");
PARSER_GET_ARRAY(longlong, "%lld", "long long");
PARSER_SAVE_ARRAY(char, "%c");
PARSER_SAVE_ARRAY(int, "%d");
PARSER_SAVE_ARRAY(float, "%g");
PARSER_SAVE_ARRAY(double, "%g");
PARSER_SAVE_ARRAY(longlong, "%lld");
/**
* @brief Retrieve char array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals.
*/
void parser_get_param_char_array(struct swift_params *params, const char *name,
int nval, char *values) {
get_param_char_array(params, name, 1, nval, values);
}
/**
* @brief Retrieve optional char array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals. If the
* parameter is not found these values will be returned
* unmodified, so should be set to the default values.
* @return whether the parameter has been found.
*/
int parser_get_opt_param_char_array(struct swift_params *params,
const char *name, int nval, char *values) {
if (get_param_char_array(params, name, 0, nval, values) != 1) {
save_param_char_array(params, name, nval, values);
params->data[params->paramCount - 1].is_default = 1;
return 0;
}
return 1;
}
/**
* @brief Retrieve int array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals.
*/
void parser_get_param_int_array(struct swift_params *params, const char *name,
int nval, int *values) {
get_param_int_array(params, name, 1, nval, values);
}
/**
* @brief Retrieve optional int array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals. If the
* parameter is not found these values will be returned
* unmodified, so should be set to the default values.
* @return whether the parameter has been found.
*/
int parser_get_opt_param_int_array(struct swift_params *params,
const char *name, int nval, int *values) {
if (get_param_int_array(params, name, 0, nval, values) != 1) {
save_param_int_array(params, name, nval, values);
params->data[params->paramCount - 1].is_default = 1;
return 0;
}
return 1;
}
/**
* @brief Retrieve float array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals.
*/
void parser_get_param_float_array(struct swift_params *params, const char *name,
int nval, float *values) {
get_param_float_array(params, name, 1, nval, values);
}
/**
* @brief Retrieve optional float array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals. If the
* parameter is not found these values will be returned
* unmodified, so should be set to the default values.
* @return whether the parameter has been found.
*/
int parser_get_opt_param_float_array(struct swift_params *params,
const char *name, int nval,
float *values) {
if (get_param_float_array(params, name, 0, nval, values) != 1) {
save_param_float_array(params, name, nval, values);
params->data[params->paramCount - 1].is_default = 1;
return 0;
}
return 1;
}
/**
* @brief Retrieve double array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals.
*/
void parser_get_param_double_array(struct swift_params *params,
const char *name, int nval, double *values) {
get_param_double_array(params, name, 1, nval, values);
}
/**
* @brief Retrieve optional double array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals. If the
* parameter is not found these values will be returned
* unmodified, so should be set to the default values.
* @return whether the parameter has been found.
*/
int parser_get_opt_param_double_array(struct swift_params *params,
const char *name, int nval,
double *values) {
if (get_param_double_array(params, name, 0, nval, values) != 1) {
save_param_double_array(params, name, nval, values);
params->data[params->paramCount - 1].is_default = 1;
return 0;
}
return 1;
}
/**
* @brief Retrieve long long array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals.
*/
void parser_get_param_longlong_array(struct swift_params *params,
const char *name, int nval,
long long *values) {
get_param_longlong_array(params, name, 1, nval, values);
}
/**
* @brief Retrieve optional long long array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values expected.
* @param values Values of the parameter found, of size at least nvals. If the
* parameter is not found these values will be returned
* unmodified, so should be set to the default values.
* @return whether the parameter has been found.
*/
int parser_get_opt_param_longlong_array(struct swift_params *params,
const char *name, int nval,
long long *values) {
if (get_param_longlong_array(params, name, 0, nval, values) != 1) {
save_param_longlong_array(params, name, nval, values);
params->data[params->paramCount - 1].is_default = 1;
return 0;
}
return 1;
}
/**
* @brief Retrieve string array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param required whether the parameter is required or not.
* @param nval number of values located.
* @param values pointer to an array of [nval] pointers to the strings.
* Note this must be freed by a call to
* parser_free_param_string_array when no longer required.
* @result whether the parameter was found or not. Note if required
* an error will be thrown.
*/
static int get_string_array(struct swift_params *params, const char *name,
int required, int *nval, char ***values) {
char cpy[PARSER_MAX_LINE_SIZE];
*nval = 0;
for (int i = 0; i < params->paramCount; i++) {
if (!strcmp(name, params->data[i].name)) {
char *cp = cpy;
strcpy(cp, params->data[i].value);
cp = trim_both(cp);
/* Strip off [], if present. */
if (cp[0] == '[') cp++;
int l = strlen(cp);
if (cp[l - 1] == ']') cp[l - 1] = '\0';
cp = trim_both(cp);
*nval = parse_quoted_strings(cp, values);
params->data[i].used = 1;
return 1;
}
}
if (required)
error("Cannot find '%s' in the structure, in file '%s'.", name,
params->fileName);
return 0;
}
/**
* @brief Retrieve string array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values located.
* @param values pointer to an array of [nval] pointers to the strings.
* Note this must be freed by a call to
* parser_free_param_string_array when no longer required.
*/
void parser_get_param_string_array(struct swift_params *params,
const char *name, int *nval,
char ***values) {
get_string_array(params, name, 1, nval, values);
}
/**
* @brief Retrieve optional string array parameter from structure.
*
* @param params Structure that holds the parameters
* @param name Name of the parameter to be found
* @param nval number of values located.
* @param values pointer to an array of [nval] pointers to the strings.
* Note this must be freed by a call to
* parser_free_param_string_array when no longer required.
* @param ndef the number of default values.
* @param def the default values as an array of pointers to strings.
* Note copied to values if used.
* @result whether the parameter was found or not.
*/
int parser_get_opt_param_string_array(struct swift_params *params,
const char *name, int *nval,
char ***values, int ndef,
const char *def[]) {
if (get_string_array(params, name, 0, nval, values) == 1) return 1;
/* Not found, so save the default values against the parameter. Look for
* single quotes in value and use if not found, otherwise use double
* quotes. We don't support having both in a string. */
char cpy[PARSER_MAX_LINE_SIZE];
int k = sprintf(cpy, "%s: [", name);
int i = 0;
for (i = 0; i < ndef - 1; i++) {
if (strchr(def[i], '\'') == 0)
k += sprintf(&cpy[k], "'%s', ", def[i]);
else
k += sprintf(&cpy[k], "\"%s\", ", def[i]);
}
if (strchr(def[i], '\'') == 0)
sprintf(&cpy[k], "'%s']", def[i]);
else
sprintf(&cpy[k], "\"%s\"]", def[i]);
parser_set_param(params, cpy);
params->data[params->paramCount - 1].is_default = 1;
params->data[params->paramCount - 1].used = 1;
/* Now copy to output space. */
char **strings;
strings = (char **)malloc(ndef * sizeof(char *));
for (int j = 0; j < ndef; j++) {
strings[j] = (char *)malloc((strlen(def[j]) + 1) * sizeof(char));
strcpy(strings[j], def[j]);
}
*values = strings;
*nval = ndef;
return 0;
}
/**
* @brief Free string array allocated by parser_get_param_string_array.
*
* @param nval number of strings returned.
* @param values pointer to the returned values.
*/
void parser_free_param_string_array(int nval, char **values) {
for (int i = 0; i < nval; i++) {
free(values[i]);
}
free(values);
return;
}
/**
* @brief Prints the contents of the parameter structure.
*
* @param params Structure that holds the parameters
*/
void parser_print_params(const struct swift_params *params) {
printf("\n--------------------------\n");
printf("| SWIFT Parameter File |\n");
printf("--------------------------\n");
for (int i = 0; i < params->paramCount; i++) {
printf("Parameter name: %s\n", params->data[i].name);
printf("Parameter value: %s\n", params->data[i].value);
printf("Parameter used: %i\n", params->data[i].used);
}
}
/**
* @brief Write the contents of the parameter structure to a file in YAML
*format.
*
* @param params Structure that holds the parameters
* @param file_name Name of file to be written
* @param write_used Write used fields or unused fields.
*/
void parser_write_params_to_file(const struct swift_params *params,
const char *file_name, int write_used) {
FILE *file = fopen(file_name, "w");
if (file == NULL) error("Error opening file '%s' to write.", file_name);
char param_name[PARSER_MAX_LINE_SIZE] = {0};
char section[PARSER_MAX_LINE_SIZE] = {0};
char *token;
/* Start of file identifier in YAML. */
fprintf(file, "%s\n\n", PARSER_START_OF_FILE);
if (write_used)
fprintf(file, "# SWIFT used parameter file\n");
else
fprintf(file, "# SWIFT unused parameter file\n");
fprintf(file, "# code version: %s\n", package_version());
fprintf(file, "# git revision: %s\n", git_revision());
fprintf(file, "# git branch: %s\n", git_branch());
fprintf(file, "# git date: %s\n", git_date());
fprintf(file, "# current date: %s\n", clocks_now(1 /* swift */));
/* Flags to track which parameters are written. */
int *written = (int *)calloc(params->paramCount, sizeof(int));
int nwritten = 0;
/* Loop over all sections. These are not contiguous when storing optional
* values. */
for (int k = 0; k < params->sectionCount; k++) {
int first = 1;
/* Locate parameters in this section. */
for (int i = 0; i < params->paramCount; i++) {
if (!written[i] && ((write_used && params->data[i].used) ||
(!write_used && !params->data[i].used))) {
/* Find section part of name, if have one. */
if (strchr(params->data[i].name, PARSER_VALUE_CHAR)) {
strcpy(param_name, params->data[i].name);
token = strtok(param_name, PARSER_VALUE_STRING);
strcpy(section, token);
strcat(section, PARSER_VALUE_STRING);
/* If in our section name print it to the file. */
if (strcmp(section, params->section[k].name) == 0) {
if (first) {
fprintf(file, "\n%s\n", section);
first = 0;
}
/* Remove white space from parameter name and write it to the
* file. */
token = trim_both(strtok(NULL, "#\n"));
fprintf(file, " %s%c %s\n", token, PARSER_VALUE_CHAR,
params->data[i].value);
written[i] = 1;
nwritten++;
}
}
}
}
}
/* Write out any parameters outside of sections. */
if (nwritten < params->paramCount) {
for (int i = 0; i < params->paramCount; i++) {
if (!written[i] && ((write_used && params->data[i].used) ||
(!write_used && !params->data[i].used))) {
fprintf(file, "\n%s%c %s\n", params->data[i].name, PARSER_VALUE_CHAR,
params->data[i].value);
}
}
}
/* End of file identifier in YAML. */
fprintf(file, "%s\n", PARSER_END_OF_FILE);
free(written);
fclose(file);
}
#if defined(HAVE_HDF5)
/**
* @brief Write the contents of the parameter structure to a hdf5 file
*
* @param params Structure that holds the parameters
* @param grp HDF5 group
* @param write_used Write used fields or unused fields.
*/
void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp,
int write_used) {
for (int i = 0; i < params->paramCount; i++) {
if (write_used && !params->data[i].used)
continue;
else if (!write_used && params->data[i].used)
continue;
io_write_attribute_s(grp, params->data[i].name, params->data[i].value);
}
}
#endif
/**
* @brief Write a swift_params struct to the given FILE as a stream of bytes.
*
* @param params the struct
* @param stream the file stream
*/
void parser_struct_dump(const struct swift_params *params, FILE *stream) {
restart_write_blocks((void *)params, sizeof(struct swift_params), 1, stream,
"parameters", "parameters");
}
/**
* @brief Restore a swift_params struct from the given FILE as a stream of
* bytes.
*
* @param params the struct
* @param stream the file stream
*/
void parser_struct_restore(const struct swift_params *params, FILE *stream) {
restart_read_blocks((void *)params, sizeof(struct swift_params), 1, stream,
NULL, "parameters");
}
/**
* @brief Return the index of a given section name in a swift_params struct.
*
* If the section could not be found, -1 is returned.
*
* @param params The swift_params struct in which to locate the section.
* @param name The section name to locate.
*/
int parser_get_section_id(const struct swift_params *params, const char *name) {
for (int section_id = 0; section_id < params->sectionCount; section_id++) {
/* Get the name of current section, *without* a trailing colon */
char section_name[FIELD_BUFFER_SIZE];
strcpy(section_name, params->section[section_id].name);
section_name[strlen(section_name) - 1] = 0;
if (strcmp(section_name, name) == 0) return section_id;
}
return -1;
}
/**
* @brief Compares two param structs and sets the used flag of any
* parameters with different values to 1, all other parameters
* are set to unused.
*
* @param refparams Structure that holds the parameters to compare to.
* @param params Structure that holds the parameters to check.
*
* @result the number of changed values found.
*/
int parser_compare_params(const struct swift_params *refparams,
struct swift_params *params) {
int changed = 0;
for (int j = 0; j < params->paramCount; j++) {
/* All parameters are unused until found to differ to a reference
* parameter. */
params->data[j].used = 0;
for (int i = 0; i < refparams->paramCount; i++) {
if (strcmp(refparams->data[i].name, params->data[j].name) == 0) {
if (strcmp(refparams->data[i].value, params->data[j].value) != 0) {
/* Same parameter, values differ. */
params->data[j].used = 1;
changed++;
}
break;
}
}
}
return changed;
}