/*
* Copyright 2015-2017, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* config_reader_win.cpp -- config reader module definitions
*/
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <tchar.h>
#include "config_reader.hpp"
#include "queue.h"
#include "scenario.hpp"
#define SECTION_GLOBAL TEXT("global")
#define KEY_BENCHMARK TEXT("bench")
#define KEY_GROUP TEXT("group")
/*
* Maximum section size according to MSDN documentation
*/
#define SIZEOF_SECTION 32767
#define NULL_LIST_EMPTY(x) (_tcslen(x) == 0)
#define NULL_LIST_NEXT(x) ((x) += (_tcslen(x) + 1))
#define KV_LIST_EMPTY(x) (_tcslen(x) == 0)
#define KV_FIRST(x)
#define KV_LIST_NEXT(x) \
((x) += (_tcslen(x) + 1), (x) += (_tcslen(x) + 1), \
(x) = kv_list_skip_comment(x))
#define KV_LIST_KEY(x) (x)
#define KV_LIST_VALUE(x) ((x) + _tcslen(x) + 1)
#define KV_LIST_INIT(x) kv_list_init(x)
#define LIST LPTSTR
#define KV_LIST LPTSTR
/*
* kv_list_skip_comment -- skip comment lines in ini file
*/
static KV_LIST
kv_list_skip_comment(KV_LIST list)
{
while (list[0] == TEXT('#'))
list += (_tcslen(list) + 1);
return list;
}
/*
* kv_list_init -- init KV list
*/
static KV_LIST
kv_list_init(LPTSTR list)
{
list = kv_list_skip_comment(list);
for (KV_LIST it = list; !KV_LIST_EMPTY(it); KV_LIST_NEXT(it)) {
LPTSTR c = _tcsstr(it, TEXT("="));
if (c == NULL)
return NULL;
*c = TEXT('\0');
}
return list;
}
/*
* config_reader -- handle structure
*/
struct config_reader {
LPTSTR lpFileName;
};
/*
* config_reader_alloc -- allocate config reader
*/
struct config_reader *
config_reader_alloc(void)
{
struct config_reader *cr = (struct config_reader *)malloc(sizeof(*cr));
if (cr == NULL)
return NULL;
return cr;
}
/*
* config_reader_read -- read config file
*/
int
config_reader_read(struct config_reader *cr, const char *fname)
{
DWORD len = 0;
LPTSTR buf = TEXT(" ");
/* get the length of the full pathname incl. terminating null char */
len = GetFullPathName((LPTSTR)fname, 0, buf, NULL);
if (len == 0) {
/* the function failed */
return -1;
} else {
/* allocate a buffer large enough to store the pathname */
LPTSTR buffer = (LPTSTR)malloc(len * sizeof(TCHAR));
DWORD ret = GetFullPathName((LPTSTR)fname, len, buffer, NULL);
if (_taccess(buffer, 0) != 0) {
printf("%s", strerror(errno));
return -1;
}
cr->lpFileName = (LPTSTR)buffer;
}
return 0;
}
/*
* config_reader_free -- free config reader
*/
void
config_reader_free(struct config_reader *cr)
{
free(cr);
}
/*
* is_scenario -- (internal) return true if _name_ is scenario name
*
* This filters out the _global_ and _config_ sections.
*/
static int
is_scenario(LPTSTR name)
{
return _tcscmp(name, SECTION_GLOBAL);
}
/*
* is_argument -- (internal) return true if _name_ is argument name
*
* This filters out the _benchmark_ key.
*/
static int
is_argument(LPTSTR name)
{
return _tcscmp(name, KEY_BENCHMARK) != 0 &&
_tcscmp(name, KEY_GROUP) != 0;
}
/*
* config_reader_get_scenarios -- return scenarios from config file
*
* This function reads the config file and returns a list of scenarios.
* Each scenario contains a list of key/value arguments.
* The scenario's arguments are merged with arguments from global section.
*/
int
config_reader_get_scenarios(struct config_reader *cr,
struct scenarios **scenarios)
{
/*
* Read all groups.
* The config file must have at least one group, otherwise
* it is considered as invalid.
*/
int ret = 0;
TCHAR *sections = (TCHAR *)malloc(sizeof(TCHAR) * SIZEOF_SECTION);
if (!sections)
return -1;
GetPrivateProfileSectionNames(sections, SIZEOF_SECTION, cr->lpFileName);
if (NULL_LIST_EMPTY(sections)) {
ret = -1;
goto err_sections;
}
/*
* Check if global section is present and read it.
*/
TCHAR *global = (TCHAR *)malloc(sizeof(TCHAR) * SIZEOF_SECTION);
if (!global)
return -1;
GetPrivateProfileSection(SECTION_GLOBAL, global, SIZEOF_SECTION,
cr->lpFileName);
KV_LIST global_kv = KV_LIST_INIT(global);
int has_global = !KV_LIST_EMPTY(global_kv);
struct scenarios *s = scenarios_alloc();
assert(NULL != s);
if (!s) {
ret = -1;
goto err_gkeys;
}
LPTSTR global_group = NULL;
for (KV_LIST it = global_kv; !KV_LIST_EMPTY(it); KV_LIST_NEXT(it)) {
if (_tcscmp(KV_LIST_KEY(it), KEY_GROUP) == 0) {
global_group = KV_LIST_VALUE(it);
break;
}
}
TCHAR *section;
for (LPTSTR group_name = sections; !NULL_LIST_EMPTY(group_name);
group_name = NULL_LIST_NEXT(group_name)) {
/*
* Check whether a group is a scenario
* or global section.
*/
if (!is_scenario(group_name))
continue;
/*
* Check for KEY_BENCHMARK which contains benchmark name.
* If not present the benchmark name is the same as the
* name of the section.
*/
section = (TCHAR *)malloc(sizeof(TCHAR) * SIZEOF_SECTION);
if (!section)
ret = -1;
GetPrivateProfileSection(group_name, section, SIZEOF_SECTION,
cr->lpFileName);
KV_LIST section_kv = KV_LIST_INIT(section);
struct scenario *scenario = NULL;
LPTSTR name = NULL;
LPTSTR group = NULL;
for (KV_LIST it = section_kv; !KV_LIST_EMPTY(it);
KV_LIST_NEXT(it)) {
if (_tcscmp(KV_LIST_KEY(it), KEY_BENCHMARK) == 0) {
name = KV_LIST_VALUE(it);
}
if (_tcscmp(KV_LIST_KEY(it), KEY_GROUP) == 0) {
group = KV_LIST_VALUE(it);
}
}
if (name == NULL) {
scenario = scenario_alloc((const char *)group_name,
(const char *)group_name);
} else {
scenario = scenario_alloc((const char *)group_name,
(const char *)name);
}
assert(scenario != NULL);
if (has_global) {
/*
* Merge key/values from global section.
*/
for (KV_LIST it = global_kv; !KV_LIST_EMPTY(it);
KV_LIST_NEXT(it)) {
LPTSTR key = KV_LIST_KEY(it);
if (!is_argument(key))
continue;
LPTSTR value = KV_LIST_VALUE(it);
assert(NULL != value);
if (!value) {
ret = -1;
goto err_scenarios;
}
struct kv *kv = kv_alloc((const char *)key,
(const char *)value);
assert(NULL != kv);
if (!kv) {
ret = -1;
goto err_scenarios;
}
TAILQ_INSERT_TAIL(&scenario->head, kv, next);
}
}
/* check for group name */
if (group) {
scenario_set_group(scenario, (const char *)group);
} else if (global_group) {
scenario_set_group(scenario,
(const char *)global_group);
}
for (KV_LIST it = section_kv; !KV_LIST_EMPTY(it);
KV_LIST_NEXT(it)) {
LPTSTR key = KV_LIST_KEY(it);
if (!is_argument(key))
continue;
LPTSTR value = KV_LIST_VALUE(it);
assert(NULL != value);
if (!value) {
ret = -1;
goto err_scenarios;
}
struct kv *kv = kv_alloc((const char *)key,
(const char *)value);
assert(NULL != kv);
if (!kv) {
ret = -1;
goto err_scenarios;
}
TAILQ_INSERT_TAIL(&scenario->head, kv, next);
}
TAILQ_INSERT_TAIL(&s->head, scenario, next);
free(section);
}
*scenarios = s;
free(global);
free(sections);
return 0;
err_scenarios:
free(section);
scenarios_free(s);
err_gkeys:
free(global);
err_sections:
free(sections);
return ret;
}