#include "src/include/pmix_config.h"
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <ctype.h>
#include "include/pmix.h"
#include "pmix_common.h"
#include "src/class/pmix_list.h"
#include "src/client/pmix_client_ops.h"
#include "src/include/pmix_globals.h"
#include "src/include/pmix_socket_errno.h"
#include "src/mca/bfrops/base/base.h"
#include "src/mca/gds/gds.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_output.h"
#include "src/util/pmix_printf.h"
#include "preg_native.h"
#include "src/mca/preg/base/base.h"
static pmix_status_t generate_node_regex(const char *input, char **regex);
static pmix_status_t generate_ppn(const char *input, char **ppn);
static pmix_status_t parse_nodes(const char *regexp, char ***names);
static pmix_status_t parse_procs(const char *regexp, char ***procs);
static pmix_status_t copy(char **dest, size_t *len, const char *input);
static pmix_status_t pack(pmix_buffer_t *buffer, const char *input);
static pmix_status_t unpack(pmix_buffer_t *buffer, char **regex);
static pmix_status_t release(char *regexp);
pmix_preg_module_t pmix_preg_native_module = {
.name = "pmix",
.generate_node_regex = generate_node_regex,
.generate_ppn = generate_ppn,
.parse_nodes = parse_nodes,
.parse_procs = parse_procs,
.copy = copy,
.pack = pack,
.unpack = unpack,
.release = release
};
static pmix_status_t regex_parse_value_ranges(char *base, char *ranges, int num_digits,
char *suffix, char ***names);
static pmix_status_t regex_parse_value_range(char *base, char *range, int num_digits, char *suffix,
char ***names);
static pmix_status_t pmix_regex_extract_nodes(char *regexp, char ***names);
static pmix_status_t pmix_regex_extract_ppn(char *regexp, char ***procs);
static pmix_status_t generate_node_regex(const char *input, char **regexp)
{
char *vptr, *vsave;
char prefix[PMIX_MAX_NODE_PREFIX];
int i, j, len, startnum, vnum, numdigits;
bool found, fullval;
char *suffix, *sfx;
pmix_regex_value_t *vreg;
pmix_regex_range_t *range;
pmix_list_t vids;
char **regexargs = NULL, *tmp, *tmp2;
char *cptr;
pmix_status_t rc;
*regexp = NULL;
PMIX_CONSTRUCT(&vids, pmix_list_t);
vsave = strdup(input);
vptr = vsave;
while (NULL != (cptr = strchr(vptr, ',')) || 0 < strlen(vptr)) {
if (NULL != cptr) {
*cptr = '\0';
}
fullval = false;
len = strlen(vptr);
startnum = -1;
memset(prefix, 0, PMIX_MAX_NODE_PREFIX);
for (i = 0, j = 0; i < len && j < (PMIX_MAX_NODE_PREFIX-1); i++) {
if (!isalpha(vptr[i])) {
if (!isdigit(vptr[i])) {
fullval = true;
break;
}
if (startnum < 0) {
startnum = i;
}
continue;
}
if (startnum < 0) {
prefix[j++] = vptr[i];
}
}
if (fullval || startnum < 0) {
vreg = PMIX_NEW(pmix_regex_value_t);
vreg->prefix = strdup(vptr);
pmix_list_append(&vids, &vreg->super);
if (NULL == cptr) {
break;
}
vptr = cptr + 1;
continue;
}
vnum = strtol(&vptr[startnum], &sfx, 10);
if (NULL != sfx) {
suffix = strdup(sfx);
numdigits = (int) (sfx - &vptr[startnum]);
} else {
suffix = NULL;
numdigits = (int) strlen(&vptr[startnum]);
}
found = false;
PMIX_LIST_FOREACH (vreg, &vids, pmix_regex_value_t) {
if (vreg->skip) {
continue;
}
if (0 < strlen(prefix) && NULL == vreg->prefix) {
continue;
}
if (0 == strlen(prefix) && NULL != vreg->prefix) {
continue;
}
if (0 < strlen(prefix) && NULL != vreg->prefix && 0 != strcmp(prefix, vreg->prefix)) {
vreg->skip = true;
continue;
}
if (NULL == suffix && NULL != vreg->suffix) {
continue;
}
if (NULL != suffix && NULL == vreg->suffix) {
continue;
}
if (NULL != suffix && NULL != vreg->suffix && 0 != strcmp(suffix, vreg->suffix)) {
vreg->skip = true;
continue;
}
if (numdigits != vreg->num_digits) {
vreg->skip = true;
continue;
}
found = true;
range = (pmix_regex_range_t *) pmix_list_get_last(&vreg->ranges);
if (NULL == range) {
range = PMIX_NEW(pmix_regex_range_t);
range->start = vnum;
range->cnt = 1;
pmix_list_append(&vreg->ranges, &range->super);
break;
}
if (vnum != (range->start + range->cnt)) {
range = PMIX_NEW(pmix_regex_range_t);
range->start = vnum;
range->cnt = 1;
pmix_list_append(&vreg->ranges, &range->super);
break;
}
range->cnt++;
break;
}
if (!found) {
vreg = PMIX_NEW(pmix_regex_value_t);
if (0 < strlen(prefix)) {
vreg->prefix = strdup(prefix);
}
if (NULL != suffix) {
vreg->suffix = strdup(suffix);
}
vreg->num_digits = numdigits;
pmix_list_append(&vids, &vreg->super);
range = PMIX_NEW(pmix_regex_range_t);
range->start = vnum;
range->cnt = 1;
pmix_list_append(&vreg->ranges, &range->super);
}
if (NULL != suffix) {
free(suffix);
}
if (NULL == cptr) {
break;
}
vptr = cptr + 1;
}
free(vsave);
while (NULL != (vreg = (pmix_regex_value_t *) pmix_list_remove_first(&vids))) {
if (0 == pmix_list_get_size(&vreg->ranges)) {
if (NULL != vreg->prefix) {
PMIx_Argv_append_nosize(®exargs, vreg->prefix);
}
PMIX_RELEASE(vreg);
continue;
}
if (NULL != vreg->prefix) {
if (0 > asprintf(&tmp, "%s[%d:", vreg->prefix, vreg->num_digits)) {
return PMIX_ERR_NOMEM;
}
} else {
if (0 > asprintf(&tmp, "[%d:", vreg->num_digits)) {
return PMIX_ERR_NOMEM;
}
}
while (NULL != (range = (pmix_regex_range_t *) pmix_list_remove_first(&vreg->ranges))) {
if (1 == range->cnt) {
if (0 > asprintf(&tmp2, "%s%d,", tmp, range->start)) {
return PMIX_ERR_NOMEM;
}
} else {
if (0 > asprintf(&tmp2, "%s%d-%d,", tmp, range->start,
range->start + range->cnt - 1)) {
return PMIX_ERR_NOMEM;
}
}
free(tmp);
tmp = tmp2;
PMIX_RELEASE(range);
}
tmp[strlen(tmp) - 1] = ']';
if (NULL != vreg->suffix) {
if (0 > asprintf(&tmp2, "%s%s", tmp, vreg->suffix)) {
return PMIX_ERR_NOMEM;
}
free(tmp);
tmp = tmp2;
}
PMIx_Argv_append_nosize(®exargs, tmp);
free(tmp);
PMIX_RELEASE(vreg);
}
if (NULL != regexargs) {
tmp = PMIx_Argv_join(regexargs, ',');
if (0 > asprintf(regexp, "pmix[%s]", tmp)) {
return PMIX_ERR_NOMEM;
}
free(tmp);
PMIx_Argv_free(regexargs);
rc = PMIX_SUCCESS;
} else {
rc = PMIX_ERR_TAKE_NEXT_OPTION;
}
PMIX_DESTRUCT(&vids);
return rc;
}
static pmix_status_t generate_ppn(const char *input, char **regexp)
{
char **ppn, **npn;
int i, j, start, end;
pmix_regex_value_t *vreg;
pmix_regex_range_t *rng;
pmix_list_t nodes;
char *tmp, *tmp2;
char *cptr;
*regexp = NULL;
PMIX_CONSTRUCT(&nodes, pmix_list_t);
ppn = PMIx_Argv_split(input, ';');
for (i = 0; NULL != ppn[i]; i++) {
rng = NULL;
vreg = PMIX_NEW(pmix_regex_value_t);
pmix_list_append(&nodes, &vreg->super);
npn = PMIx_Argv_split(ppn[i], ',');
for (j = 0; NULL != npn[j]; j++) {
if (NULL != (cptr = strchr(npn[j], '-'))) {
*cptr = '\0';
++cptr;
start = strtol(npn[j], NULL, 10);
end = strtol(cptr, NULL, 10);
if (NULL == rng) {
rng = PMIX_NEW(pmix_regex_range_t);
rng->start = start;
rng->cnt = end - start + 1;
pmix_list_append(&vreg->ranges, &rng->super);
} else {
if (start == (rng->start + rng->cnt)) {
rng->cnt++;
} else {
rng = PMIX_NEW(pmix_regex_range_t);
rng->start = start;
rng->cnt = end - start + 1;
pmix_list_append(&vreg->ranges, &rng->super);
}
}
} else {
start = strtol(npn[j], NULL, 10);
if (NULL == rng) {
rng = PMIX_NEW(pmix_regex_range_t);
rng->start = start;
rng->cnt = 1;
pmix_list_append(&vreg->ranges, &rng->super);
} else {
if (start == (rng->start + rng->cnt)) {
rng->cnt++;
} else {
rng = PMIX_NEW(pmix_regex_range_t);
rng->start = start;
rng->cnt = 1;
pmix_list_append(&vreg->ranges, &rng->super);
}
}
}
}
PMIx_Argv_free(npn);
}
PMIx_Argv_free(ppn);
tmp = strdup("pmix[");
PMIX_LIST_FOREACH (vreg, &nodes, pmix_regex_value_t) {
while (NULL != (rng = (pmix_regex_range_t *) pmix_list_remove_first(&vreg->ranges))) {
if (1 == rng->cnt) {
if (0 > asprintf(&tmp2, "%s%d,", tmp, rng->start)) {
free(tmp);
return PMIX_ERR_NOMEM;
}
} else {
if (0 > asprintf(&tmp2, "%s%d-%d,", tmp, rng->start, rng->start + rng->cnt - 1)) {
free(tmp);
return PMIX_ERR_NOMEM;
}
}
free(tmp);
tmp = tmp2;
PMIX_RELEASE(rng);
}
tmp[strlen(tmp) - 1] = ';';
}
tmp[strlen(tmp) - 1] = ']';
if (strlen(tmp) > strlen(input)) {
free(tmp);
PMIX_LIST_DESTRUCT(&nodes);
return PMIX_ERR_TAKE_NEXT_OPTION;
}
*regexp = tmp;
PMIX_LIST_DESTRUCT(&nodes);
return PMIX_SUCCESS;
}
static pmix_status_t parse_nodes(const char *regexp, char ***names)
{
char *tmp, *ptr;
pmix_status_t rc;
*names = NULL;
if (NULL == regexp) {
return PMIX_SUCCESS;
}
tmp = strdup(regexp);
tmp[strlen(tmp) - 1] = '\0';
if (NULL == (ptr = strchr(tmp, '['))) {
free(tmp);
return PMIX_ERR_BAD_PARAM;
}
*ptr = '\0';
++ptr;
if (0 == strcmp(tmp, "pmix")) {
if (PMIX_SUCCESS != (rc = pmix_regex_extract_nodes(ptr, names))) {
PMIX_ERROR_LOG(rc);
}
} else {
rc = PMIX_ERR_TAKE_NEXT_OPTION;
}
free(tmp);
return rc;
}
static pmix_status_t parse_procs(const char *regexp, char ***procs)
{
char *tmp, *ptr;
pmix_status_t rc;
*procs = NULL;
if (NULL == regexp) {
return PMIX_SUCCESS;
}
tmp = strdup(regexp);
tmp[strlen(tmp) - 1] = '\0';
if (NULL == (ptr = strchr(tmp, '['))) {
free(tmp);
return PMIX_ERR_BAD_PARAM;
}
*ptr = '\0';
++ptr;
if (0 == strcmp(tmp, "pmix")) {
if (PMIX_SUCCESS != (rc = pmix_regex_extract_ppn(ptr, procs))) {
PMIX_ERROR_LOG(rc);
}
} else {
rc = PMIX_ERR_TAKE_NEXT_OPTION;
}
free(tmp);
return rc;
}
static pmix_status_t copy(char **dest, size_t *len, const char *input)
{
if (0 != strncmp(input, "pmix", 4)) {
return PMIX_ERR_TAKE_NEXT_OPTION;
}
*dest = strdup(input);
*len = strlen(input) + 1;
return PMIX_SUCCESS;
}
static pmix_status_t pack(pmix_buffer_t *buffer, const char *input)
{
size_t slen;
char *ptr;
if (0 != strncmp(input, "pmix", 4)) {
return PMIX_ERR_TAKE_NEXT_OPTION;
}
slen = strlen(input) + 1;
ptr = pmix_bfrop_buffer_extend(buffer, slen);
if (NULL == ptr) {
return PMIX_ERR_NOMEM;
}
memcpy(ptr, input, slen);
buffer->bytes_used += slen;
buffer->pack_ptr += slen;
return PMIX_SUCCESS;
}
static pmix_status_t unpack(pmix_buffer_t *buffer, char **regex)
{
char *ptr;
ptr = buffer->unpack_ptr;
if (0 != strncmp(ptr, "pmix", 4)) {
return PMIX_ERR_TAKE_NEXT_OPTION;
}
*regex = strdup(ptr);
buffer->unpack_ptr += strlen(ptr) + 1;
if (NULL == *regex) {
return PMIX_ERR_NOMEM;
}
return PMIX_SUCCESS;
}
static pmix_status_t pmix_regex_extract_nodes(char *regexp, char ***names)
{
int i, j, k, len;
pmix_status_t ret;
char *base;
char *orig, *suffix;
bool found_range = false;
bool more_to_come = false;
int num_digits;
*names = NULL;
if (NULL == regexp) {
return PMIX_SUCCESS;
}
orig = base = strdup(regexp);
if (NULL == base) {
PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
return PMIX_ERR_OUT_OF_RESOURCE;
}
pmix_output_verbose(1, pmix_preg_base_framework.framework_output,
"pmix:extract:nodes: checking list: %s", regexp);
do {
len = strlen(base);
for (i = 0; i <= len; ++i) {
if (base[i] == '[') {
base[i] = '\0';
found_range = true;
break;
}
if (base[i] == ',') {
base[i] = '\0';
found_range = false;
more_to_come = true;
break;
}
if (base[i] == '\0') {
found_range = false;
more_to_come = false;
break;
}
}
if (i == 0 && !found_range) {
free(orig);
return PMIX_ERR_BAD_PARAM;
}
if (found_range) {
i++;
for (j = i; j < len; j++) {
if (base[j] == ':') {
base[j] = '\0';
break;
}
}
if (j >= len) {
free(orig);
return PMIX_ERR_BAD_PARAM;
}
num_digits = strtol(&base[i], NULL, 10);
i = j + 1;
for (j = i; j < len; ++j) {
if (base[j] == ']') {
base[j] = '\0';
break;
}
}
if (j >= len) {
free(orig);
return PMIX_ERR_BAD_PARAM;
}
if (j + 1 < len && base[j + 1] != ',') {
for (k = j + 1; k < len && base[k] != ','; k++)
;
if (k < len) {
base[k] = '\0';
}
suffix = strdup(&base[j + 1]);
if (k < len) {
base[k] = ',';
}
j = k - 1;
} else {
suffix = NULL;
}
pmix_output_verbose(1, pmix_preg_base_framework.framework_output,
"regex:extract:nodes: parsing range %s %s %s", base, base + i,
suffix);
ret = regex_parse_value_ranges(base, base + i, num_digits, suffix, names);
if (NULL != suffix) {
free(suffix);
}
if (PMIX_SUCCESS != ret) {
free(orig);
return ret;
}
if (j + 1 < len && base[j + 1] == ',') {
more_to_come = true;
base = &base[j + 2];
} else {
more_to_come = false;
}
} else {
if (PMIX_SUCCESS != (ret = PMIx_Argv_append_nosize(names, base))) {
PMIX_ERROR_LOG(ret);
free(orig);
return ret;
}
i++;
base = &base[i];
}
} while (more_to_come);
free(orig);
return ret;
}
static pmix_status_t regex_parse_value_ranges(char *base, char *ranges, int num_digits,
char *suffix, char ***names)
{
int i, len;
pmix_status_t ret;
char *start, *orig;
len = strlen(ranges);
for (orig = start = ranges, i = 0; i < len; ++i) {
if (',' == ranges[i]) {
ranges[i] = '\0';
ret = regex_parse_value_range(base, start, num_digits, suffix, names);
if (PMIX_SUCCESS != ret) {
PMIX_ERROR_LOG(ret);
return ret;
}
start = ranges + i + 1;
}
}
if (start < orig + len) {
pmix_output_verbose(1, pmix_preg_base_framework.framework_output,
"regex:parse:ranges: parse range %s (2)", start);
ret = regex_parse_value_range(base, start, num_digits, suffix, names);
if (PMIX_SUCCESS != ret) {
PMIX_ERROR_LOG(ret);
return ret;
}
}
return PMIX_SUCCESS;
}
static pmix_status_t regex_parse_value_range(char *base, char *range, int num_digits, char *suffix,
char ***names)
{
char *str, tmp[132];
size_t i, k, start, end;
size_t base_len, len;
bool found;
pmix_status_t ret;
if (NULL == base || NULL == range) {
return PMIX_ERROR;
}
len = strlen(range);
base_len = strlen(base);
start = end = 0;
for (found = false, i = 0; i < len; ++i) {
if (isdigit((int) range[i])) {
if (!found) {
start = strtol(range + i, NULL, 10);
found = true;
break;
}
}
}
if (!found) {
PMIX_ERROR_LOG(PMIX_ERR_NOT_FOUND);
return PMIX_ERR_NOT_FOUND;
}
for (found = false; i < len; ++i) {
if (!isdigit(range[i])) {
break;
}
}
if (i >= len) {
end = start;
found = true;
} else {
for (; i < len; ++i) {
if (isdigit(range[i])) {
end = strtol(range + i, NULL, 10);
found = true;
break;
}
}
}
if (!found) {
PMIX_ERROR_LOG(PMIX_ERR_NOT_FOUND);
return PMIX_ERR_NOT_FOUND;
}
len = base_len + num_digits + 32;
if (NULL != suffix) {
len += strlen(suffix);
}
str = (char *) malloc(len);
if (NULL == str) {
PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
return PMIX_ERR_OUT_OF_RESOURCE;
}
for (i = start; i <= end; ++i) {
memset(str, 0, len);
strcpy(str, base);
for (k = 0; k < (size_t) num_digits; k++) {
str[k + base_len] = '0';
}
memset(tmp, 0, 132);
pmix_snprintf(tmp, 132, "%lu", (unsigned long) i);
for (k = 0; k < strlen(tmp); k++) {
str[base_len + num_digits - k - 1] = tmp[strlen(tmp) - k - 1];
}
if (NULL != suffix) {
strcat(str, suffix);
}
ret = PMIx_Argv_append_nosize(names, str);
if (PMIX_SUCCESS != ret) {
PMIX_ERROR_LOG(ret);
free(str);
return ret;
}
}
free(str);
return PMIX_SUCCESS;
}
static pmix_status_t pmix_regex_extract_ppn(char *regexp, char ***procs)
{
char **rngs, **nds, *t, **ps = NULL;
int i, j, k, start, end;
nds = PMIx_Argv_split(regexp, ';');
for (j = 0; NULL != nds[j]; j++) {
rngs = PMIx_Argv_split(nds[j], ',');
for (i = 0; NULL != rngs[i]; i++) {
if (NULL == (t = strchr(rngs[i], '-'))) {
PMIx_Argv_append_nosize(&ps, rngs[i]);
} else {
*t = '\0';
start = strtol(rngs[i], NULL, 10);
++t;
end = strtol(t, NULL, 10);
for (k = start; k <= end; k++) {
if (0 > asprintf(&t, "%d", k)) {
PMIx_Argv_free(nds);
PMIx_Argv_free(rngs);
return PMIX_ERR_NOMEM;
}
PMIx_Argv_append_nosize(&ps, t);
free(t);
}
}
}
PMIx_Argv_free(rngs);
t = PMIx_Argv_join(ps, ',');
PMIx_Argv_append_nosize(procs, t);
free(t);
PMIx_Argv_free(ps);
ps = NULL;
}
PMIx_Argv_free(nds);
return PMIX_SUCCESS;
}
static pmix_status_t release(char *regexp)
{
if (NULL == regexp) {
return PMIX_SUCCESS;
}
if (0 != strncmp(regexp, "pmix", 4)) {
return PMIX_ERR_TAKE_NEXT_OPTION;
}
free(regexp);
return PMIX_SUCCESS;
}