#include "src/include/pmix_config.h"
#include <ctype.h>
#include <string.h>
#include "pmix_common.h"
#include "src/util/keyval/keyval_lex.h"
#include "src/util/pmix_keyval_parse.h"
#include "src/util/pmix_output.h"
#include "src/util/pmix_string_copy.h"
#include "src/threads/pmix_threads.h"
int pmix_util_keyval_parse_lineno = 0;
static char *key_buffer = NULL;
static size_t key_buffer_len = 0;
static pmix_mutex_t keyval_mutex;
static int parse_line(const char *filename, pmix_keyval_parse_fn_t callback);
static int parse_line_new(const char *filename, int first_val,
pmix_keyval_parse_fn_t callback);
static void parse_error(int num, const char *filename);
static char *env_str = NULL;
static int envsize = 1024;
void pmix_util_keyval_parse_finalize(void)
{
free(key_buffer);
key_buffer = NULL;
key_buffer_len = 0;
PMIX_DESTRUCT(&keyval_mutex);
}
int pmix_util_keyval_parse_init(void)
{
PMIX_CONSTRUCT(&keyval_mutex, pmix_mutex_t);
return PMIX_SUCCESS;
}
int pmix_util_keyval_parse(const char *filename, pmix_keyval_parse_fn_t callback)
{
int val;
int ret = PMIX_SUCCESS;
;
pmix_mutex_lock(&keyval_mutex);
pmix_util_keyval_yyin = fopen(filename, "r");
if (NULL == pmix_util_keyval_yyin) {
ret = PMIX_ERR_NOT_FOUND;
goto cleanup;
}
pmix_util_keyval_parse_done = false;
pmix_util_keyval_yynewlines = 1;
pmix_util_keyval_init_buffer(pmix_util_keyval_yyin);
while (!pmix_util_keyval_parse_done) {
val = pmix_util_keyval_yylex();
switch (val) {
case PMIX_UTIL_KEYVAL_PARSE_DONE:
break;
case PMIX_UTIL_KEYVAL_PARSE_NEWLINE:
break;
case PMIX_UTIL_KEYVAL_PARSE_SINGLE_WORD:
parse_line(filename, callback);
break;
case PMIX_UTIL_KEYVAL_PARSE_MCAVAR:
case PMIX_UTIL_KEYVAL_PARSE_ENVVAR:
case PMIX_UTIL_KEYVAL_PARSE_ENVEQL:
parse_line_new(filename, val, callback);
break;
default:
parse_error(1, filename);
break;
}
}
fclose(pmix_util_keyval_yyin);
pmix_util_keyval_yylex_destroy();
cleanup:
pmix_mutex_unlock(&keyval_mutex);
return ret;
}
static int parse_line(const char *filename, pmix_keyval_parse_fn_t callback)
{
int val;
pmix_util_keyval_parse_lineno = pmix_util_keyval_yylineno;
if (key_buffer_len < strlen(pmix_util_keyval_yytext) + 1) {
char *tmp;
key_buffer_len = strlen(pmix_util_keyval_yytext) + 1;
tmp = (char *) realloc(key_buffer, key_buffer_len);
if (NULL == tmp) {
free(key_buffer);
key_buffer_len = 0;
key_buffer = NULL;
return PMIX_ERR_OUT_OF_RESOURCE;
}
key_buffer = tmp;
}
pmix_string_copy(key_buffer, pmix_util_keyval_yytext, key_buffer_len);
val = pmix_util_keyval_yylex();
if (pmix_util_keyval_parse_done || PMIX_UTIL_KEYVAL_PARSE_EQUAL != val) {
parse_error(2, filename);
return PMIX_ERROR;
}
val = pmix_util_keyval_yylex();
if (PMIX_UTIL_KEYVAL_PARSE_SINGLE_WORD == val || PMIX_UTIL_KEYVAL_PARSE_VALUE == val) {
callback(filename, 0, key_buffer, pmix_util_keyval_yytext);
val = pmix_util_keyval_yylex();
if (PMIX_UTIL_KEYVAL_PARSE_NEWLINE == val || PMIX_UTIL_KEYVAL_PARSE_DONE == val) {
return PMIX_SUCCESS;
}
}
else if (PMIX_UTIL_KEYVAL_PARSE_DONE == val || PMIX_UTIL_KEYVAL_PARSE_NEWLINE == val) {
callback(filename, 0, key_buffer, NULL);
return PMIX_SUCCESS;
}
parse_error(3, filename);
return PMIX_ERROR;
}
static void parse_error(int num, const char *filename)
{
pmix_output(0, "keyval parser: error %d reading file %s at line %d:\n %s\n", num, filename,
pmix_util_keyval_yynewlines, pmix_util_keyval_yytext);
}
static void trim_name(char *buffer, const char *prefix, const char *suffix)
{
char *pchr, *echr;
size_t buffer_len;
if (NULL == buffer) {
return;
}
buffer_len = strlen(buffer);
pchr = buffer;
if (NULL != prefix) {
size_t prefix_len = strlen(prefix);
if (0 == strncmp(buffer, prefix, prefix_len)) {
pchr += prefix_len;
}
}
while (isspace(*pchr)) {
pchr++;
}
echr = buffer + buffer_len;
while (echr > buffer && isspace(*(echr - 1))) {
echr--;
}
echr[0] = '\0';
if (NULL != suffix && (uintptr_t)(echr - buffer) > strlen(suffix)) {
size_t suffix_len = strlen(suffix);
echr -= suffix_len;
if (0 == strncmp(echr, suffix, strlen(suffix))) {
do {
echr--;
} while (isspace(*echr));
echr[1] = '\0';
}
}
if (buffer != pchr) {
memmove(buffer, pchr, strlen(pchr) + 1);
}
}
static int save_param_name(void)
{
if (key_buffer_len < strlen(pmix_util_keyval_yytext) + 1) {
char *tmp;
key_buffer_len = strlen(pmix_util_keyval_yytext) + 1;
tmp = (char *) realloc(key_buffer, key_buffer_len);
if (NULL == tmp) {
free(key_buffer);
key_buffer_len = 0;
key_buffer = NULL;
return PMIX_ERR_OUT_OF_RESOURCE;
}
key_buffer = tmp;
}
pmix_string_copy(key_buffer, pmix_util_keyval_yytext, key_buffer_len);
return PMIX_SUCCESS;
}
static int add_to_env_str(char *var, char *val)
{
int sz, varsz = 0, valsz = 0, new_envsize;
void *tmp;
if (NULL == var) {
return PMIX_ERR_BAD_PARAM;
}
varsz = strlen(var);
if (NULL != val) {
valsz = strlen(val);
valsz += 1;
}
sz = 0;
if (NULL != env_str) {
sz = strlen(env_str);
sz += 1;
}
sz += varsz + valsz + 1;
new_envsize = envsize;
while (new_envsize <= sz) {
new_envsize *= 2;
}
if (NULL != env_str) {
if (new_envsize > envsize) {
tmp = realloc(env_str, new_envsize);
if (NULL == tmp) {
return PMIX_ERR_OUT_OF_RESOURCE;
}
env_str = tmp;
}
strcat(env_str, ";");
} else {
env_str = calloc(1, new_envsize);
if (NULL == env_str) {
return PMIX_ERR_OUT_OF_RESOURCE;
}
}
envsize = new_envsize;
strcat(env_str, var);
if (NULL != val) {
strcat(env_str, "=");
strcat(env_str, val);
}
return PMIX_SUCCESS;
}
static int parse_line_new(const char *filename, int first_val,
pmix_keyval_parse_fn_t callback)
{
int val;
char *tmp;
int rc;
val = first_val;
while (PMIX_UTIL_KEYVAL_PARSE_NEWLINE != val && PMIX_UTIL_KEYVAL_PARSE_DONE != val) {
rc = save_param_name();
if (PMIX_SUCCESS != rc) {
return rc;
}
if (PMIX_UTIL_KEYVAL_PARSE_MCAVAR == val) {
trim_name(key_buffer, "-mca", NULL);
trim_name(key_buffer, "--mca", NULL);
val = pmix_util_keyval_yylex();
if (PMIX_UTIL_KEYVAL_PARSE_VALUE == val) {
if (NULL != pmix_util_keyval_yytext) {
tmp = strdup(pmix_util_keyval_yytext);
if ('\'' == tmp[0] || '\"' == tmp[0]) {
trim_name(tmp, "\'", "\'");
trim_name(tmp, "\"", "\"");
}
callback(filename, 0, key_buffer, tmp);
free(tmp);
}
} else {
parse_error(4, filename);
return PMIX_ERROR;
}
} else if (PMIX_UTIL_KEYVAL_PARSE_ENVEQL == val) {
trim_name(key_buffer, "-x", "=");
trim_name(key_buffer, "--x", NULL);
val = pmix_util_keyval_yylex();
if (PMIX_UTIL_KEYVAL_PARSE_VALUE == val) {
add_to_env_str(key_buffer, pmix_util_keyval_yytext);
} else {
parse_error(5, filename);
return PMIX_ERROR;
}
} else if (PMIX_UTIL_KEYVAL_PARSE_ENVVAR == val) {
trim_name(key_buffer, "-x", "=");
trim_name(key_buffer, "--x", NULL);
add_to_env_str(key_buffer, NULL);
} else {
parse_error(6, filename);
return PMIX_ERROR;
}
val = pmix_util_keyval_yylex();
}
return PMIX_SUCCESS;
}
int pmix_util_keyval_save_internal_envars(pmix_keyval_parse_fn_t callback)
{
if (NULL != env_str && 0 < strlen(env_str)) {
callback(NULL, 0, "mca_base_env_list_internal", env_str);
free(env_str);
env_str = NULL;
}
return PMIX_SUCCESS;
}