/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ /* * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana * University Research and Technology * Corporation. All rights reserved. * Copyright (c) 2004-2005 The University of Tennessee and The University * of Tennessee Research Foundation. All rights * reserved. * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, * University of Stuttgart. All rights reserved. * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * Copyright (c) 2015-2016 Los Alamos National Security, LLC. All rights * reserved. * Copyright (c) 2016-2020 Intel, Inc. All rights reserved. * Copyright (c) 2021-2022 Nanook Consulting. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ #include "src/include/pmix_config.h" #include #include #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); /* Open the pmix */ 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: /* This will also set pmix_util_keyval_parse_done to true, so just break here */ break; case PMIX_UTIL_KEYVAL_PARSE_NEWLINE: /* blank line! ignore it */ 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: /* anything else is an error */ 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; /* Save the name name */ 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); /* The first thing we have to see is an "=" */ val = pmix_util_keyval_yylex(); if (pmix_util_keyval_parse_done || PMIX_UTIL_KEYVAL_PARSE_EQUAL != val) { parse_error(2, filename); return PMIX_ERROR; } /* Next we get the value */ 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); /* Now we need to see the newline */ val = pmix_util_keyval_yylex(); if (PMIX_UTIL_KEYVAL_PARSE_NEWLINE == val || PMIX_UTIL_KEYVAL_PARSE_DONE == val) { return PMIX_SUCCESS; } } /* Did we get an EOL or EOF? */ else if (PMIX_UTIL_KEYVAL_PARSE_DONE == val || PMIX_UTIL_KEYVAL_PARSE_NEWLINE == val) { callback(filename, 0, key_buffer, NULL); return PMIX_SUCCESS; } /* Nope -- we got something unexpected. Bonk! */ parse_error(3, filename); return PMIX_ERROR; } static void parse_error(int num, const char *filename) { /* JMS need better error/warning message here */ 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; } } /* trim spaces at the beginning */ while (isspace(*pchr)) { pchr++; } /* trim spaces at the end */ 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) { /* move the trimmed string to the beginning of the buffer */ 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); /* account for '=' */ valsz += 1; } sz = 0; if (NULL != env_str) { sz = strlen(env_str); /* account for ';' */ sz += 1; } /* add required new size incl NULL byte */ sz += varsz + valsz + 1; /* make sure we have sufficient space */ 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 { /* we got something unexpected. Bonk! */ 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; }