/* Gate Specific Code -- BEGIN */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)xfs_cmdline_subst.c	1.3	05/04/21 SMI"

/*
 * This file contains the code needed to parse and substitute parameters on
 * the command lines.
 *
 * It has been initially developed part of feature 1144 : problem isolation
 * under GDS.
 */

/* Gate Specific Code -- END */

#include <scha.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>

#include "xfs_cmdline_subst.h"
#include "xfs.h"

/* GDS Specific Code -- BEGIN */
#include "gds_eventlog.h"
/* GDS Specific Code -- END */

/*
 * Here are some type declarations
 */

#define	DEFAULT_MEMORY_TO_FREE_SIZE 8
#define	DEFAULT_OUTPUT_BUFFER_SIZE 32

#define	DEBUG_MACRO(string)

/* #define	DEBUG_MACRO(string) printf string   */

typedef struct {
	scds_handle_t scds_handle;
	scha_resource_t scha_handle;
	char *rname;
	char *rgname;
	int memory_to_free_size;
	int memory_to_free_used;
	void **memory_to_free;
} global_datas_t;

typedef char *(*solver_routine)(const char *variable,
				global_datas_t *handle);

typedef struct {
	const char *name;
	solver_routine solver;
} variable_t;

/*
 * Here are the global declarations
 */

static char *default_solver(const char *variable, global_datas_t *handle);
static char *scha_status_solver(const char *variable, global_datas_t *handle);
static char *scha_int_solver(const char *variable, global_datas_t *handle);
static char *scha_boolean_solver(const char *variable, global_datas_t *handle);
static char *scds_str_array_solver(const char *variable,
    global_datas_t *handle);
static char *names_solver(const char *variable, global_datas_t *handle);
static char *scds_string_solver(const char *variable, global_datas_t *handle);
static char *scds_boolean_solver(const char *variable, global_datas_t *handle);
static char *scds_int_solver(const char *variable, global_datas_t *handle);
static char *compute_hostnames(const char *variable, global_datas_t *handle);

/*
 * The following array contains the list of accepted variables and the
 * solver to be used to substitute them.
 */

static variable_t variable_list[] = {
	{ "RT_NAME", scds_string_solver },
	{ "RT_BASEDIR", scds_string_solver },
	{ "RT_API_VERSION", scds_int_solver },
	{ "RT_VERSION", scds_string_solver },
	{ "RT_SINGLE_INSTANCE", scds_boolean_solver },
	{ "RT_INSTALLED_NODES", scds_str_array_solver },
	{ "RT_FAILOVER", scds_boolean_solver },

	{ "RG_NAME", names_solver },
	{ "RG_DESIRED_PRIMARIES", scds_int_solver },
	{ "RG_GLOBAL_RESOURCES_USED", scds_str_array_solver },
	{ "RG_IMPLICIT_NETWORK_DEPENDANCIES", scds_boolean_solver },
	{ "RG_MAXIMUM_PRIMARIES", scds_int_solver },
	{ "RG_NODELIST", scds_str_array_solver },
	{ "RG_RESOURCE_LIST", scds_str_array_solver },
	{ "RG_PINGPONG_INTERVAL", scds_int_solver },
	{ "RG_PATHPREFIX", scds_string_solver },
	{ "RG_NUM_RESTARTS", scha_int_solver },

	{ "RS_NAME", names_solver },
	{ "RS_CHEAP_PROBE_INTERVAL", scds_int_solver },
	{ "RS_MONITOR_START_TIMEOUT", scha_int_solver },
	{ "RS_MONITOR_STOP_TIMEOUT", scds_int_solver },
	{ "RS_RESOURCE_DEPENDENCIES", scds_str_array_solver },
	{ "RS_RESOURCE_DEPENDENCIES_WEAK", scds_str_array_solver },
	{ "RS_RETRY_COUNT", scds_int_solver },
	{ "RS_RETRY_INTERVAL", scds_int_solver },
	{ "RS_START_TIMEOUT", scds_int_solver },
	{ "RS_STOP_TIMEOUT", scds_int_solver },
	{ "RS_THOROUGH_PROBE_INTERVAL", scds_int_solver },
	{ "RS_SCALABLE", scds_boolean_solver },
	{ "RS_NUM_RESTARTS", scha_int_solver },
	{ "RS_STATUS", scha_status_solver },

	{ "HOSTNAMES", compute_hostnames },
	{ "hostnames", compute_hostnames }
};

/*
 * Private APIs implementation
 */

static variable_t*
retrieve_variable(char *name)
{
	int nb_var, i;
	variable_t *result;

	nb_var = sizeof (variable_list)/sizeof (variable_t);

	for (i = 0; i < nb_var; i++) {
		result = variable_list+i;
		if (strcmp(name, result->name) == 0) {
			break;
		}
	}

	if (i == nb_var) {
		result = NULL;
	}

	return (result);
}

static int
add_memory_to_free(void *p, global_datas_t *handle)
{
	int res = 0;
	void **mem;

	if (handle->memory_to_free_used+1 == handle->memory_to_free_size) {
		mem = realloc(handle->memory_to_free,
		    2*handle->memory_to_free_size*sizeof (void *));
		if (!mem) {
			res = 1;
		}

		if (!res) {
			handle->memory_to_free = mem;
			handle->memory_to_free_size *= 2;
		}
	}

	if (!res) {
		handle->memory_to_free[handle->memory_to_free_used] = p;
		handle->memory_to_free_used++;
	}

	return (res);
}

/*
 * Solver implementations
 */

static char *
default_solver(const char *variable, global_datas_t *handle)
{
	char *res;
	int err;

	res = (char*)malloc(strlen(variable)+3);

	if (res) {
		sprintf(res, ">%s<", variable);
		err = add_memory_to_free(res, handle);
		if (err) {
			free(res);
			res = NULL;
		}
	}

	return (res);
}

static char *
scha_status_solver(const char *variable, global_datas_t *handle)
{
	scha_status_value_t *value;
	char *res = NULL;
	int err;

	DEBUG_MACRO(("scha_status_solver> Find the value of %s\n", variable));

	if (strcmp(variable, "RS_STATUS")) {
		DEBUG_MACRO(("scha_status_solver> The variable is invalid\n"));
		return (NULL);
	}

	err = scha_resource_get(handle->scha_handle,
				SCHA_STATUS,
				&value);
	if (err != SCHA_ERR_NOERR) {
DEBUG_MACRO(("scha_status_solver> scha_resource_get failed [%d]\n", err));
		return (NULL);
	}

	switch (value->status) {
	case SCHA_RSSTATUS_OK:
		res = "SCHA_RSSTATUS_OK";
		break;

	case SCHA_RSSTATUS_OFFLINE:
		res = "SCHA_RSSTATUS_OFFLINE";
		break;

	case SCHA_RSSTATUS_FAULTED:
		res = "SCHA_RSSTATUS_FAULTED";
		break;

	case SCHA_RSSTATUS_DEGRADED:
		res = "SCHA_RSSTATUS_DEGRADED";
		break;

	case SCHA_RSSTATUS_UNKNOWN:
		res = "SCHA_RSSTATUS_UNKNOWN";
		break;

	default:
DEBUG_MACRO(("scha_resource_get return value invalid [%x]\n", value->status));
		res = NULL;
	}

	return (res);
}

static char *
scha_int_solver(const char *variable,
		global_datas_t *handle)
{
	char *tag = NULL;
	int value = 0;
	int err = 0;
	char *res = NULL;

	if ((!tag) && (strcmp(variable, "RS_MONITOR_START_TIMEOUT") == 0)) {
		tag = SCHA_MONITOR_START_TIMEOUT;
	}

	if ((!tag) && (strcmp(variable, "RG_NUM_RESTARTS") == 0)) {
		tag = SCHA_NUM_RG_RESTARTS;
	}

	if ((!tag) && (strcmp(variable, "RS_NUM_RESTARTS") == 0)) {
		tag = SCHA_NUM_RESOURCE_RESTARTS;
	}

	if (!tag) {
		return (NULL);
	}

	res = (char*)malloc(16);
	if (!res) {
		return (NULL);
	}

	err = add_memory_to_free(res, handle);
	if (err) {
		free(res);
		res = NULL;
		return (NULL);
	}

	err = scha_resource_get(handle->scha_handle,
				tag,
				&value);
	if (err != SCHA_ERR_NOERR) {
		free(res);
		res = NULL;
		return (NULL);
	}

	snprintf(res, 16, "%d", value);

	return (res);
}

static char *
scha_boolean_solver(const char *variable,
		    global_datas_t *handle)
{
	char *tag = NULL;
	boolean_t value;
	int err;
	char *res = NULL;

	if (!tag) {
		return (NULL);
	}

	err = scha_resource_get(handle->scha_handle,
				tag,
				&value);
	if (err != SCHA_ERR_NOERR) {
		return (NULL);
	}

	if (value == B_TRUE) {
		res = "true";
	}
	if (value == B_FALSE) {
		res = "false";
	}

	return (res);
}

static char *
scds_str_array_solver(const char *variable, global_datas_t *handle)
{
	const scha_str_array_t *(*scds_call)(scds_handle_t) = NULL;
	const scha_str_array_t *value = NULL;
	int size, i, err;
	char *res = NULL;

	DEBUG_MACRO(("Entering scha_str_array_solver for %s\n", variable));

	if ((!scds_call) && (strcmp(variable, "RT_INSTALLED_NODES") == 0)) {
		scds_call = scds_get_rt_installed_nodes;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RG_GLOBAL_RESOURCES_USED") == 0)) {
		scds_call = scds_get_rg_global_resources_used;
	}

	if ((!scds_call) && (strcmp(variable, "RG_NODELIST") == 0)) {
		scds_call = scds_get_rg_nodelist;
	}

	if ((!scds_call) && (strcmp(variable, "RG_RESOURCE_LIST") == 0)) {
		scds_call = scds_get_rg_resource_list;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RS_RESOURCE_DEPENDENCIES") == 0)) {
		scds_call = scds_get_rs_resource_dependencies;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RS_RESOURCE_DEPENDENCIES_WEAK") == 0)) {
		scds_call = scds_get_rs_resource_dependencies_weak;
	}

	if (!scds_call) {
		DEBUG_MACRO(("scha_str_array_solver> scds_call not found\n"));
		return (NULL);
	}

	value = scds_call(handle->scds_handle);
	if (!value) {
		return (NULL);
	}

	if (value->array_cnt == 0 && value->is_ALL_value == B_FALSE) {
		DEBUG_MACRO(("scha_str_array_solver> array has no element\n"));
		return ("\"\"");
	}

/*
 * We compute the total string size.
 *
 * The final string contains all the strings separated by commas.
 *
 * We should initialize size to -1 not to count the comma for the first
 * string, but we also have to take the ending \0 character. So we
 * initialize it to 0.
 */

	if (value->is_ALL_value == B_TRUE) {
		size = strlen("ALL") + 1;

	} else {
		size = 0;
		for (i = 0; i < value->array_cnt; i++) {
			size += strlen(value->str_array[i])+1;
		}
	}

	res = (char*)malloc(size);
	if (!res) {
		DEBUG_MACRO(("scha_str_array_solver> not enough memory\n"));
		return (NULL);
	}

	err = add_memory_to_free(res, handle);
	if (err) {
	DEBUG_MACRO(("scha_str_array_solver> add_memory_to_free failed\n"));
		free(res);
		res = NULL;
		return (NULL);
	}


	if (value->is_ALL_value == B_TRUE) {
		strcpy(res, "ALL");
	} else {
		strcpy(res, value->str_array[0]);
		for (i = 1; i < value->array_cnt; i++) {
			strcat(res, ",");
			strcat(res, value->str_array[i]);
		}
	}

	return (res);
}

static char *
names_solver(const char *variable, global_datas_t *handle)
{
	char *res = NULL;

	if ((!res) && (strcmp(variable, "RS_NAME") == 0)) {
		res = handle->rname;
	}

	if ((!res) && (strcmp(variable, "RG_NAME") == 0)) {
		res = handle->rgname;
	}

	if (!res) {
		return (NULL);
	}

	return (res);
}

static char *
scds_string_solver(const char *variable, global_datas_t *handle)
{
	const char *(*scds_call)(scds_handle_t) = NULL;
	char *res = NULL;

	if ((!scds_call) && (strcmp(variable, "RT_NAME") == 0)) {
		scds_call = scds_get_resource_type_name;
	}

	if ((!scds_call) && (strcmp(variable, "RT_BASEDIR") == 0)) {
		scds_call = scds_get_rt_rt_basedir;
	}

	if ((!scds_call) && (strcmp(variable, "RT_VERSION") == 0)) {
		scds_call = scds_get_rt_rt_version;
	}

	if ((!scds_call) && (strcmp(variable, "RG_PATHPREFIX") == 0)) {
		scds_call = scds_get_rg_pathprefix;
	}

	if (!scds_call) {
		return (NULL);
	}

	res = (char*)scds_call(handle->scds_handle);
	if (res[0] == '\0') {
		res = "\"\"";
	}

	return (res);
}

static char *
scds_boolean_solver(const char *variable,
		    global_datas_t *handle)
{
	boolean_t (*scds_call)(scds_handle_t) = NULL;
	boolean_t value;
	char *res = NULL;

	if ((!scds_call) && (strcmp(variable, "RT_FAILOVER") == 0)) {
		scds_call = scds_get_rt_failover;
	}

	if ((!scds_call) && (strcmp(variable, "RT_SINGLE_INSTANCE") == 0)) {
		scds_call = scds_get_rt_single_instance;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RG_IMPLICIT_NETWORK_DEPENDANCIES") == 0)) {
		scds_call = scds_get_rg_implicit_network_dependencies;
	}

	if ((!scds_call) && (strcmp(variable, "RS_SCALABLE") == 0)) {
		scds_call = scds_get_rs_scalable;
	}

	if (!scds_call) {
		return (NULL);
	}

	value = scds_call(handle->scds_handle);

	if (value == B_TRUE) {
		res = "true";
	}
	if (value == B_FALSE) {
		res = "false";
	}

	return (res);
}

static char *
scds_int_solver(const char *variable,
		global_datas_t *handle)
{
	int (*scds_call)(scds_handle_t) = NULL;
	int value = 0;
	int err = 0;
	char *res = NULL;

	if ((!scds_call) && (strcmp(variable, "RT_API_VERSION") == 0)) {
		scds_call = scds_get_rt_api_version;
	}

	if ((!scds_call) && (strcmp(variable, "RG_DESIRED_PRIMARIES") == 0)) {
		scds_call = scds_get_rg_desired_primaries;
	}

	if ((!scds_call) && (strcmp(variable, "RG_MAXIMUM_PRIMARIES") == 0)) {
		scds_call = scds_get_rg_maximum_primaries;
	}

	if ((!scds_call) && (strcmp(variable, "RG_PINGPONG_INTERVAL") == 0)) {
		scds_call = scds_get_rg_pingpong_interval;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RS_CHEAP_PROBE_INTERVAL") == 0)) {
		scds_call = scds_get_rs_cheap_probe_interval;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RS_MONITOR_STOP_TIMEOUT") == 0)) {
		scds_call = scds_get_rs_monitor_stop_timeout;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RS_RETRY_COUNT") == 0)) {
		scds_call = scds_get_rs_retry_count;
	}

	if ((!scds_call) && (strcmp(variable, "RS_RETRY_INTERVAL") == 0)) {
		scds_call = scds_get_rs_retry_interval;
	}

	if ((!scds_call) && (strcmp(variable, "RS_START_TIMEOUT") == 0)) {
		scds_call = scds_get_rs_start_timeout;
	}

	if ((!scds_call) && (strcmp(variable, "RS_STOP_TIMEOUT") == 0)) {
		scds_call = scds_get_rs_stop_timeout;
	}

	if ((!scds_call) &&
	    (strcmp(variable, "RS_THOROUGH_PROBE_INTERVAL") == 0)) {
		scds_call = scds_get_rs_thorough_probe_interval;
	}

	if (!scds_call) {
		return (NULL);
	}

	res = (char*)malloc(16);
	if (!res) {
		return (NULL);
	}

	err = add_memory_to_free(res, handle);
	if (err) {
		free(res);
		res = NULL;
		return (NULL);
	}

	value = scds_call(handle->scds_handle);

	snprintf(res, 16, "%d", value);

	return (res);
}

/*
 * This solver routine computes the list of hostnames being used by
 * the given "resource".
 */

static char *
compute_hostnames(const char *variable, global_datas_t *handle)
{
	char *res = NULL;
	scds_net_resource_list_t *snrlp = NULL;
	scds_net_resource_t snrp;
	int rs, ip;

	if (strcasecmp(variable, "HOSTNAMES")) {
		return (NULL);
	}

	/* Network aware applications code -- BEGIN */

	res = (char*)malloc(SCDS_CMD_SIZE);
	if (!res) {
		return (NULL);
	}

	res[0] = '\0';

	if (scds_get_rs_hostnames(handle->scds_handle, &snrlp)) {
		scds_syslog(LOG_ERR,
			    "No network address resource in resource group.");
		free(res);
		return (NULL);
	}

	/*
	 * Iterate through all resources to get the hostnames
	 */

	for (rs = 0; rs < snrlp->num_netresources; rs++) {
		snrp = snrlp->netresources[rs];
		for (ip = 0; ip < snrp.num_hostnames; ip++) {
			if (rs != 0 || ip != 0) {
				(void) strlcat(res, ",", SCDS_CMD_SIZE);
			}

			(void) strlcat(res, snrp.hostnames[ip], SCDS_CMD_SIZE);
		}
	}

	scds_free_net_list(snrlp);

	if (add_memory_to_free(res, handle)) {
		free(res);
		return (NULL);
	}

	/* Network aware applications code -- END */

	return (res);
}

/*
 * The following routines implement the command line lexer / parser
 */

typedef enum {
	SPACE_TOKEN = 1,
	VARIABLE_TOKEN,
	QUOTE_TOKEN,
	OTHER_TOKEN,
	INVALID_TOKEN
} token_t;

static int
isspace(char *input, int pos)
{
	return ((input[pos] == ' ') || (input[pos] == '\n') ||
	    (input[pos] == '\0'));
}

static token_t
cmdline_lexer(char *input, int *pos, char **result, int *result_size)
{
	int found = 0;
	int include_current = 0;
	token_t res = INVALID_TOKEN;

	if (input[*pos] == '\0') {
		*result = NULL;
		*result_size = 0;
		return (INVALID_TOKEN);
	}

	*result = input + (*pos);
	*result_size = 0;

	while (!found) {
		include_current = 0;

		if (input[*pos] == '\0') {
			found = 1;
		}

		if (!found) {
			switch (res) {
			case SPACE_TOKEN:
				if (!isspace(input, *pos)) {
					found = 1;
				}
				break;

			case VARIABLE_TOKEN:
			case OTHER_TOKEN:
				if (isspace(input, *pos)) {
					found = 1;
				}
				break;

			case QUOTE_TOKEN:
				if (input[*pos] == '\'') {
					found = 1;
				}
				include_current = 1;
				break;

			case INVALID_TOKEN:
				switch (input[*pos]) {
				case ' ':
			DEBUG_MACRO(("cmdline_lexer> Found a SPACE_TOKEN\n"));
					res = SPACE_TOKEN;
					break;

				case '%':
				case '$':
			DEBUG_MACRO(("cmdline_lexer> Found VARIABLE_TOKEN\n"));
					res = VARIABLE_TOKEN;
					break;

				case '\'':
			DEBUG_MACRO(("cmdline_lexer> Found a QUOTE_TOKEN\n"));
					res = QUOTE_TOKEN;
					break;

				default:
			DEBUG_MACRO(("cmdline_lexer> Found a OTHER_TOKEN\n"));
					res = OTHER_TOKEN;
				}
			}
		}

		if ((!found) || (include_current)) {
			(*pos)++;
			(*result_size)++;
		}
	}

	return (res);
}

static char *
concatenate_strings(char *input, size_t *size, size_t *used, char *s2,
    int s2_size)
{
	char *mem = NULL;
	char *res = NULL;

	if (s2_size == 0) {
		return (input);
	}

	res = input;

	while (s2_size+(*used) > (*size)) {
		mem = (char*)realloc(res, (*size)*2*sizeof (char));
		if (!mem) {
			return (NULL);
		}

		res = mem;
		(*size) *= 2;
	}

	bcopy(s2, res+(*used)-1, s2_size);
	(*used) += s2_size;
	res[(*used)-1] = '\0';

	return (res);
}

/*
 * API routines implementation
 */

int
cmdline_open(scds_handle_t scds_handle, cmdline_handle_t *handle)
{
	global_datas_t *h = NULL;
	scha_err_t err = SCHA_ERR_INTERNAL;
	int res = 0;

	if (!res) {
		h = (global_datas_t*)malloc(sizeof (global_datas_t));
		if (!h) {
			DEBUG_MACRO(("cmdline_open> malloc failed\n"));
			res = 1;
		}
	}

	if (!res) {
		h->scds_handle = scds_handle;
		h->rname = NULL;
		h->rgname = NULL;
		h->memory_to_free_size = DEFAULT_MEMORY_TO_FREE_SIZE;
		h->memory_to_free_used = 0;
		h->memory_to_free = (void **)malloc(
		    h->memory_to_free_size*sizeof (void *));

		if (!h->memory_to_free) {
			DEBUG_MACRO(("cmdline_open> malloc failed\n"));
			res = 1;
		}
	}

	if (!res) {
		h->rname = strdup(scds_get_resource_name(scds_handle));
		if (!h->rname) {
			res = 1;
		}
	}

	if (!res) {
		h->rgname = strdup(scds_get_resource_group_name(scds_handle));
		if (!h->rgname) {
			res = 1;
		}
	}

	if (!res) {
		err = scha_resource_open(h->rname, h->rgname, &h->scha_handle);
		if (err != SCHA_ERR_NOERR) {
			res = 1;
		}
	}

	if (res) {
		if ((h) && (err == SCHA_ERR_NOERR)) {
			(scha_err_t)scha_resource_close(h->scha_handle);
			h->scha_handle = NULL;
		}

		if ((h) && (h->rname)) {
			free(h->rname);
			h->rname = NULL;
		}

		if ((h) && (h->rgname)) {
			free(h->rgname);
			h->rgname = NULL;
		}

		if ((h) && (h->memory_to_free)) {
			free(h->memory_to_free);
			h->memory_to_free = NULL;
		}

		if (h) {
			free(h);
			h = NULL;
		}
	}

	*handle = h;

	return (res);
}

int
cmdline_close(cmdline_handle_t handle)
{
	int res = 0;
	int i;
	global_datas_t *h = (global_datas_t*)handle;

	if (!h) {
		res = 1;
	}

	if (!res) {
		for (i = 0; i < h->memory_to_free_used; i++) {
			if (h->memory_to_free[i]) {
				free(h->memory_to_free[i]);
				h->memory_to_free[i] = NULL;
			}
		}

		(scha_err_t)scha_resource_close(h->scha_handle);
		h->scha_handle = NULL;

		if (h->memory_to_free) {
			free(h->memory_to_free);
			h->memory_to_free = NULL;
		}

		if (h->rname) {
			free(h->rname);
			h->rname = NULL;
		}

		if (h->rgname) {
			free(h->rgname);
			h->rgname = NULL;
		}

		free(h);
		h = NULL;
	}

	return (res);
}

char *
cmdline_substitute_parameters(cmdline_handle_t handle, char *input)
{
	global_datas_t *h = (global_datas_t*)handle;
	char *res = NULL;
	char *tmp_res = NULL;
	size_t res_size = DEFAULT_OUTPUT_BUFFER_SIZE;
	size_t res_used = 1;
	token_t cur_token = INVALID_TOKEN;
	int pos = 0;
	char *result = NULL;
	int result_size = 0;

	if (!h) {
		DEBUG_MACRO(("handle is NULL\n"));
/* GDS Specific Code -- BEGIN */
		(int)gds_publish_eventlog_with_names(GDS_OTHER, GDS_LOG_ERR,
		    h->rname, h->rgname, "handle is NULL");
/* GDS Specific Code -- END */
		return (NULL);
	}

	res = (char*)malloc(res_size*sizeof (char));
	if (!res) {
		DEBUG_MACRO(("malloc failed\n"));
/* GDS Specific Code -- BEGIN */
		(int)gds_publish_eventlog_with_names(GDS_OTHER, GDS_LOG_ERR,
		    h->rname, h->rgname, "malloc failed");
/* GDS Specific Code -- END */
		return (NULL);
	}
	res[0] = '\0';

	do {
		cur_token = cmdline_lexer(input, &pos, &result, &result_size);
		switch (cur_token) {
		case SPACE_TOKEN:
			DEBUG_MACRO(("Entering SPACE_TOKEN\n"));
			tmp_res = concatenate_strings(res, &res_size, &res_used,
			    " ", 1);
			break;

		case VARIABLE_TOKEN: {
			char *substituted = NULL;
			char tmp_char = '\0';
			variable_t *var = NULL;

			DEBUG_MACRO(("Entering VARIABLE_TOKEN\n"));
			/*
			 * Check for '$hostnames'. If present replace with
			 * hostnames otherwise just append it to the res
			 */
			if (((result[0]) == '$') &&
				(strcmp(result+1, "hostnames") != 0)) {
					tmp_res = concatenate_strings(res,
						&res_size, &res_used,
						result, result_size);
					break;
			}
			tmp_char = result[result_size];
			result[result_size] = '\0';

			var = retrieve_variable(result+1);
			DEBUG_MACRO(("Substituting %s\n", result+1));

			if (var) {
				substituted = var->solver(result+1, handle);
				if (!substituted) {
					DEBUG_MACRO(("The solver failed\n"));
/* GDS Specific Code -- BEGIN */
					(int)gds_publish_eventlog_with_names(
					    GDS_OTHER, GDS_LOG_ERR, h->rname,
					    h->rgname,
			"The solver failed to substitue the variable %s",
					    result);
/* GDS Specific Code -- END */
					result[result_size] = tmp_char;
					if (res) {
						free(res);
						res = NULL;
					}
				} else {
					result[result_size] = tmp_char;
					tmp_res = concatenate_strings(res,
					    &res_size, &res_used, substituted,
					    strlen(substituted));
				}
			} else {
		DEBUG_MACRO(("Failed to retrieve the variable in the list\n"));
/* GDS Specific Code -- BEGIN */
				(int)gds_publish_eventlog_with_names(GDS_OTHER,
				    GDS_LOG_ERR, h->rname, h->rgname,
			"Failed to retrieve the variable %s in the list",
				    result);
/* GDS Specific Code -- END */
				result[result_size] = tmp_char;
				if (res) {
					free(res);
					res = NULL;
				}
			}
		}
			break;

		case QUOTE_TOKEN:
			DEBUG_MACRO(("Entering QUOTE_TOKEN\n"));
		case OTHER_TOKEN:
			DEBUG_MACRO(("Entering OTHER_TOKEN\n"));
			tmp_res = concatenate_strings(res, &res_size, &res_used,
			    result, result_size);
			break;

		case INVALID_TOKEN:
			DEBUG_MACRO(("Entering INVALID_TOKEN\n"));
			/* EOL reached */
			break;
		}

		if ((cur_token != INVALID_TOKEN) && (res)) {
			if (tmp_res) {
				res = tmp_res;
			} else {
				DEBUG_MACRO(("tmp_res is NULL. Aborting\n"));
/* GDS Specific Code -- BEGIN */
				(int)gds_publish_eventlog_with_names(GDS_OTHER,
				    GDS_LOG_ERR, h->rname, h->rgname,
				    "tmp_res is NULL. Aborting");
/* GDS Specific Code -- END */
				free(res);
				res = NULL;
			}
		}
	} while ((res) && (cur_token != INVALID_TOKEN));

	if (res) {
		int err;

		err = add_memory_to_free(res, h);
		if (err) {
			DEBUG_MACRO(("add_memory_to_free failed\n"));
/* GDS Specific Code -- BEGIN */
			(int)gds_publish_eventlog_with_names(GDS_OTHER,
			    GDS_LOG_ERR, h->rname, h->rgname,
			    "add_memory_to_free failed");
/* GDS Specific Code -- END */
			free(res);
			res = NULL;
		}
	}

	return (res);
}
