#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <regex.h>
#include "ctlString.h"
#include "ctlError.h"
#include "libctlParameter.h"


/* Internal prototype */
char *convFile2Char(int fd);
char *convParagram2Char(struct paragramAttribute *paragAttr);
enum lineAnalyseResult analyseLine(char *str);
int analyseParagram(char *val, struct configList *cfgList, char *paragName);
int analyseParameter(char *line, struct configList *cfgList);
int extractParameter(char *line, char **result);
int getEnvValue(char *result, char *paragName, char *paramName);
int formatValue(char *result, char *src, int MODE);
int extractValueString(char *result, char *src);
int removeComment(char *result, char *src);
int trimValueString(char *result, char *src);
void setCtlParamErr(enum ctlParamErrCode ecode, char *func, int line, char *msg1, char *msg2);


/* Internal prototype(paragram operations) */
struct paragramAttribute *putParagram(char *paragName, struct paragramList *paragList);
struct paragramAttribute *searchParagram(char *paragName, struct paragramList *paragList);
struct paragramList *addParagram(char *paragName, struct paragramList *paragList);
struct paragramList *createParagramList(char *paragName);
struct paragramAttribute *createParagramAttribute(char *paragName);
void freeParagramList(struct paragramList *paragList);
void freeParagramAttribute(struct paragramAttribute *paragAttr);


/* Internal prototype(parameter operations) */
struct parameterList *putParameter(char *paramName, char *paramValue, struct parameterList *paramList);
struct parameterAttribute *searchParameter(char *paramName, struct parameterList *paramList);
struct parameterList *addParameter(char *paramName, char *paramValue, struct parameterList *paramList);
int modifyParameter(char *paramValue, struct parameterAttribute *paramAttr);
struct parameterList *createParameterList(char *paramName, char *paramValue);
struct parameterAttribute *createParameterAttribute(char *paramName, char *paramValue);
void freeParameterList(struct parameterList *paramList);
void freeParameterAttribute(struct parameterAttribute *paramAttr);


/*
 *
 * Control paragram/parameter functions
 *
 */
/* Initialize structure of ConfigList */
struct configList *initConfigList()
{
	struct configList *cList;

	clearErrMSG();
	cList = (struct configList *)malloc(sizeof(struct configList));
	if(cList == NULL) {
		setCtlParamErr(ERR_ERRNO, "initConfigList", __LINE__, strerror(errno), NULL);
		return NULL;
	}
	cList->paragList = createParagramList(DEFAULT_PARAGRAM_NAME);
	if(cList->paragList == NULL) {
		setCtlParamErr(ERR_CR_PARAG_LST, "initConfigList", __LINE__, NULL, NULL);
		return NULL;
	}
	return cList;
}

/* Free memory for config list*/
void freeConfigList(struct configList *cfgList)
{
	if(cfgList == NULL) {
		return;
	}
	freeParagramList(cfgList->paragList);
	free(cfgList);
}

/* Import paragram/parameter list from file descriptor */
int readConfigFile(int fd, struct configList *cfgList)
{
	char *cfgChar;

	clearErrMSG();
	if(cfgList == NULL) {
		setCtlParamErr(ERR_NULL, "readConfigFile", __LINE__, NULL, NULL);
		return -1;
	}
	cfgChar = convFile2Char(fd);
	if(cfgChar == NULL) {
		setCtlParamErr(ERR_CONV_FILE_TO_CHAR_FAIL, "readConfigFile", __LINE__, NULL, NULL);
		return -1;
	}
	if(readConfigChar(cfgChar, cfgList) == -1) {
		setCtlParamErr(ERR_READ_CONF_FAIL, "readConfigFile", __LINE__, NULL, NULL);
		return -1;
	}
	return 0;
}

/* Import paragram/parameter list from array of char */
int readConfigChar(char *config, struct configList *cfgList)
{
	int offset = 0;
	int n = 0;
	enum lineAnalyseResult ret;
	char *pName;
	char line[CONFIG_FILE_LINE_MAX];
	line[0] = '\0';

	clearErrMSG();
	while((n = getLine(config + offset, line, CONFIG_FILE_LINE_MAX)) > 0) {
		ret = analyseLine(line);
		if(ret == PARAGRAM_LINE) {
			pName = getSubString(line, strlen(line) - 1, PARAGRAM_NAME_MAX);
			if(pName == NULL) {
				setCtlParamErr(ERR_GET_PARAG_NAME_FAIL, "readConfigChar", __LINE__, NULL, NULL);
				return -1;
			}
			offset += n;
			n = analyseParagram(config + offset, cfgList, pName);
			if(n == -1) {
				setCtlParamErr(ERR_PARAG_ANALYSE_FAIL, "readConfigChar", __LINE__, NULL, NULL);
				free(pName);
				return -1;
			}
			free(pName);
		}
		if((ret == NORMAL_PARAMETER_LINE) || (ret == PARAGRAM_PARAMETER_LINE)) {
			if(analyseParameter(line, cfgList) != 0) {
				setCtlParamErr(ERR_PARAM_ANALYSE_FAIL, "readConfigChar", __LINE__, NULL, NULL);
				return -1;
			}
		}
		offset += n;
	}
	if(n == -1) {
		setCtlParamErr(ERR_GET_LINE_FAIL, "readConfigChar", __LINE__, NULL, NULL);
		return -1;
	}
	return 0;
}

/* Get paragram name list(include "default" paragram) */
char **getParagramList(struct configList *cfgList)
{
	int num;
	char **ret, **tmp;
	struct paragramList *pList;

	num = countParagram(cfgList);
	if(num  == -1) {
		setCtlParamErr(ERR_CNT_PARAG_FAIL, "getParagramList", __LINE__, NULL, NULL);
		return NULL;
	}
	ret = (char **)malloc(sizeof(char **) * (num + 1));
	if(ret == NULL) {
		setCtlParamErr(ERR_ERRNO, "getParagramList", __LINE__, strerror(errno), NULL);
		return NULL;
	}
	pList = cfgList->paragList;
	tmp = ret;
	do {
		if(strcmp(pList->paragAttr->paragramName, NO_PARAGRAM_PARAMETER) != 0) {
			*tmp++ = strdup(pList->paragAttr->paragramName);
		}
	} while((pList = pList->nextParag) != NULL);
	*tmp = NULL;
	return ret;
}

/* Number of paragram(include "default" paragram) */
int countParagram(struct configList *cfgList)
{
	int cnt = 0;
	struct paragramList *pList;

	clearErrMSG();
	if((cfgList == NULL) || (cfgList->paragList == NULL)) {
		setCtlParamErr(ERR_NULL, "countParagram", __LINE__, NULL, NULL);
		return -1;
	}
	pList = cfgList->paragList;
	do {
		if(strcmp(pList->paragAttr->paragramName, NO_PARAGRAM_PARAMETER) != 0) {
			cnt++;
		}
	} while((pList = pList->nextParag) != NULL);
	return cnt;
}

/* Get parameter value
 * NOTE
 *   sizeof(result) < PARAMETER_VALUE_MAX + 1
 * return
 *   -1 : internal error
 *    0 : success
 *    1 : parameter not found
 */
int getParameterValue(char *result, char *paragName, char *paramName, int MODE, struct configList *cfgList)
{
	struct paragramAttribute *prgAttr;
	struct paragramAttribute *dprgAttr = NULL;
	struct parameterAttribute *prmAttr;
	struct parameterAttribute *dprmAttr = NULL;
	int rc;

	clearErrMSG();
	if((result == NULL) || (paramName == NULL) || (cfgList == NULL)) {
		setCtlParamErr(ERR_NULL, "getParameterValue", __LINE__, NULL, NULL);
		return -1;
	}
	if(paragName == NULL) {
		paragName = NO_PARAGRAM_PARAMETER;
	}
	/* Search parameter in envrionment variable */
	if((MODE & NO_ENV_PARAMETER) == 0) {
		rc = getEnvValue(result, paragName, paramName);
		if(rc == -1) {
			setCtlParamErr(ERR_GET_ENV_FAIL, "getParameterValue", __LINE__, paragName, paramName);
			return -1;
		}
		if(rc == 0) {
			return 0;
		}
	}
	/* Search Paragram */
	if((strcmp(paragName, NO_PARAGRAM_PARAMETER) == 0)) {
		prgAttr = searchParagram(NO_PARAGRAM_PARAMETER, cfgList->paragList);
		// Don't use default paragram
		MODE = MODE | NO_GET_DEF_PARAGRAM;
	} else {
		dprgAttr = searchParagram(DEFAULT_PARAGRAM_NAME, cfgList->paragList);
		prgAttr = searchParagram(paragName, cfgList->paragList);
	}
	if(prgAttr == NULL) {
		if((MODE & NO_GET_DEF_PARAGRAM) == 0) {
			prgAttr = dprgAttr;
		} else {
			return 1;
		}
	}
	/* Search Parameter */
	prmAttr = searchParameter(paramName, prgAttr->paramList);
	if((MODE & NO_GET_DEF_PARAGRAM) == 0) {
		if(strcmp(prgAttr->paragramName, DEFAULT_PARAGRAM_NAME) != 0) {
			dprmAttr = searchParameter(paramName, dprgAttr->paramList);
		}
	}
	if(prmAttr == NULL) {
		if(dprmAttr == NULL) {
			return 1;
		}
		prmAttr = dprmAttr;
	}
	rc = formatValue(result, prmAttr->parameterValue, MODE);
	if(rc == -1) {
		setCtlParamErr(ERR_GET_PARAM_VAL_FAIL, "getParameterValue", __LINE__, paragName, paramName);
	}
	return rc;
}

/* Get parameter value
 * NOTE
 *   sizeof(result) < PARAMETER_VALUE_MAX + 1
 *   You must be NULL terminate
 * return
 *   -1 : internal error
 *    0 : success
 *    1 : parameter not found
 */
int getnParameterValue(char *result, char *paragName, char *paramName, int MODE, ...)
{
	va_list list;
	struct configList *cl;
	int rc;

	va_start(list, MODE);
	while((cl = va_arg(list, struct configList*)) != NULL) {
		rc = getParameterValue(result, paragName, paramName, MODE, cl);
		if(rc != 1) {
			break;
		}
	}
	va_end(list);
	if(rc == -1) {
		setCtlParamErr(ERR_GET_PARAM_VAL_FAIL, "getnParameterValue", __LINE__, paragName, paramName);
	}
	return rc;
}

/* Get parameter value from environment variable
 * return
 *   -1 : internal error
 *    0 : success
 *    1 : parameter not found
 */
int getEnvValue(char *result, char *paragName, char *paramName)
{
	char val_name[6 + PARAGRAM_NAME_MAX + 1 + PARAMETER_NAME_MAX + 1];
	char prgName[PARAGRAM_NAME_MAX + 1];
	char prmName[PARAMETER_NAME_MAX + 1];
	char env_val[PARAMETER_VALUE_MAX + 1];
	char *penv;

	if((result == NULL) || (paramName == NULL)) {
		setCtlParamErr(ERR_NULL, "getEnvValue", __LINE__, NULL, NULL);
		return -1;
	}
	val_name[0] = '\0';
	strcat(val_name, "PARAM_");
	if(strcmp(paragName, NO_PARAGRAM_PARAMETER) != 0) {
		truncateString(prgName, paragName, PARAGRAM_NAME_MAX);
		strcat(val_name, prgName);
		strcat(val_name, "_");
	}
	truncateString(prmName, paramName, PARAMETER_NAME_MAX);
	strcat(val_name, prmName);
	penv = getenv(val_name);
	if(penv == NULL) {
		return 1;
	}
	truncateString(env_val, penv, PARAMETER_VALUE_MAX);
	strncpy(result, env_val, PARAMETER_VALUE_MAX);
	return 0;
}

/* Formatting parameter value string
 * return
 *  -1 : internal error
 *   0 : success
 */
int formatValue(char *result, char *src, int MODE)
{
	int rc;
	char str1[PARAMETER_VALUE_MAX + 1];
	char str2[PARAMETER_VALUE_MAX + 1];

	if((MODE & NO_PROCESSING) != 0) {
		strncpy(result, src, PARAMETER_VALUE_MAX);
		return 0;
	}
	/* Extract processing */
	if((MODE & NO_EXTRACT_STRING) == 0) {
		extractValueString(str1, src);
	} else {
		strncpy(str1, src, PARAMETER_VALUE_MAX);
	}
	/* Remove comment processing */
	if((MODE & NO_DEL_COMMENT) == 0) {
		rc = removeComment(str2, str1);
		if(rc == -1) {
			setCtlParamErr(ERR_RM_COMM_FAIL, "formatValue", __LINE__, NULL, NULL);
			return -1;
		}
	} else {
		strncpy(str2, str1, PARAMETER_VALUE_MAX);
	}
	/* Trimming processing */
	if((MODE & NO_TRIMMING) == 0) {
		rc = trimValueString(result, str2);
		if(rc == -1) {
			setCtlParamErr(ERR_TRIM_VAL_FAIL, "formatValue", __LINE__, NULL, NULL);
			return -1;
		}
	} else {
		strncpy(result, str2, PARAMETER_VALUE_MAX);
	}
	return 0;
}

/* Wrapper of extractString function */
int extractValueString(char *result, char *src)
{
	int rc;

	rc = extractString(result, src, '"', WITHOUT_DELIMITER, PARAMETER_VALUE_MAX);
	if(rc < 1) {
		strncpy(result, src, PARAMETER_VALUE_MAX);
	}
	return 0;
}

/* Get removing comment string */
int removeComment(char *result, char *src)
{
	char *idx;
	int rc;

	idx = index(src, '#');
	if(idx != NULL) {
		rc = truncateString(result, src, idx - src);
		if(rc == -1) {
			setCtlParamErr(ERR_TRUNC_STR_FAIL, "removeComment", __LINE__, NULL, NULL);
			return -1;
		}
	} else {
		strncpy(result, src, PARAMETER_VALUE_MAX);
	}
	return 0;
}

/* Wrapper of trimString function */
int trimValueString(char *result, char *src)
{
	char *str;

	str = trimString(src, PARAMETER_VALUE_MAX);
	if(str == NULL) {
		setCtlParamErr(ERR_TRIM_STR_FAIL, "trimValueString", __LINE__, NULL, NULL);
		return -1;
	}
	strncpy(result, str, PARAMETER_VALUE_MAX);
	free(str);
	return 0;
}

/* Convert to array of character from configuration information */
char *convConfig2Char(struct configList *cfgList)
{
	char *ret_str = NULL;
	char *tmp_str;
	char *parag_name;
	char *parag_char;
	char **paragList;
	struct paragramAttribute *paragAttr;
	int n = 0;
	int ret_size = 0;

	clearErrMSG();
	paragList = getParagramList(cfgList);	// include NULL check
	if(paragList == NULL) {
		setCtlParamErr(ERR_GET_PARAGRAM_LIST_FAIL, "convConfig2Char", __LINE__, NULL, NULL);
		return NULL;
	}
	parag_name = NO_PARAGRAM_PARAMETER;
	do {
		paragAttr = searchParagram(parag_name, cfgList->paragList);
		if(paragAttr == NULL) {
			continue;
		}
		parag_char = convParagram2Char(paragAttr);
		if(parag_char == NULL) {
			setCtlParamErr(ERR_CONV_PARAGRAM_TO_CHAR_FAIL, "convConfig2Char", __LINE__, NULL, NULL);
			free(ret_str);
			free(paragList);
			return NULL;
		}
		tmp_str = addString(parag_char, ret_str, &ret_size, 0);
		if(tmp_str == NULL) {
			setCtlParamErr(ERR_ADD_STRING_FAIL, "convConfig2Char", __LINE__, NULL, NULL);
			free(ret_str);
			free(paragList);
			return NULL;
		}
		ret_str = tmp_str;
	} while((parag_name = paragList[n++]) != NULL);
	free(paragList);
	return ret_str;
}

/* Generate error message */
void setCtlParamErr(enum ctlParamErrCode ecode, char *func, int line, char *msg1, char *msg2)
{
	char str[CTL_ERROR_MESSAGE_MAX + 1];

	switch(ecode) {
		case ERR_ERRNO:
			strncpy(str, msg1, CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_NULL:
			strncpy(str, "NULL pointer exception", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_CR_PARAG_LST:
			strncpy(str, "Failed to creating paragram list", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_CONV_FILE_TO_CHAR_FAIL:
			strncpy(str, "Failed to convert character array from file failed", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_READ_CONF_FAIL:
			strncpy(str, "Failed to read from configuration information", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_PARAM_NAME_FAIL:
			strncpy(str, "Failed to get the parameter name", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_PARAG_NAME_FAIL:
			strncpy(str, "Failed to get the paragram name", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_PARAM_VAL_FAIL:
			snprintf(str, CTL_ERROR_MESSAGE_MAX, "Failed to get the parameter value(paragram name: %s, parameter name: %s)", msg1, msg2);
			break;
		case ERR_PARAG_ANALYSE_FAIL:
			strncpy(str, "Failed to analyse paragram", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_PARAM_ANALYSE_FAIL:
			strncpy(str, "Failed to analyse parameter", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_LINE_ANALYSE_FAIL:
			strncpy(str, "Failed to analyse a line", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_ADD_STRING_FAIL:
			strncpy(str, "Failed to add strings", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_LINE_FAIL:
			strncpy(str, "Failed to get the line", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_CNT_PARAG_FAIL:
			strncpy(str, "Failed to get the number of paragram", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_ENV_FAIL:
			snprintf(str, CTL_ERROR_MESSAGE_MAX, "Failed to get the environment variable(paragram name: %s, parameter name: %s)", msg1, msg2);
			break;
		case ERR_RM_COMM_FAIL:
			strncpy(str, "Failed to remove the comment", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_TRIM_VAL_FAIL:
			strncpy(str, "Failed to trim the value", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_TRUNC_STR_FAIL:
			strncpy(str, "Failed to truncate the value", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_TRIM_STR_FAIL:
			strncpy(str, "Failed to trim process", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_REGEX_FAIL:
			snprintf(str, CTL_ERROR_MESSAGE_MAX, "Failed to matching process(format template : %s)", msg1);
			break;
		case ERR_PUT_PARAG_FAIL:
			strncpy(str, "Failed to search/add paragram", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_PUT_PARAM_FAIL:
			strncpy(str, "Failed to search/add parameter", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_EXTRACT_PARAM_FAIL:
			strncpy(str, "Failed to extract parameter name and value", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_SUBSTR_FAIL:
			strncpy(str, "Failed to get sub-string process", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_ADD_PARAG_FAIL:
			strncpy(str, "Failed to add new paragram", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_ADD_PARAM_FAIL:
			strncpy(str, "Failed to add new parameter", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_CR_PARAG_ATTR_FAIL:
			strncpy(str, "Failed to create new paragram attribute", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_MOD_PARAM_FAIL:
			strncpy(str, "Failed to modify parameter value", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_CR_PARAM_ATTR_FAIL:
			strncpy(str, "Failed to create new parameter attribute", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_GET_PARAGRAM_LIST_FAIL:
			strncpy(str, "Failed to get paragram list", CTL_ERROR_MESSAGE_MAX);
			break;
		case ERR_CONV_PARAGRAM_TO_CHAR_FAIL:
			strncpy(str, "Failed to convert character array from paragram information failed", CTL_ERROR_MESSAGE_MAX);
			break;
		default:
			strncpy(str, "Unexplained error", CTL_ERROR_MESSAGE_MAX);
			break;
	}
	addErrMSG(__FILE__, func, line, str);
}

/*
 *
 * Analyse functions
 *
 */
/* Convert to array of character from file */
char *convFile2Char(int fd)
{
	int rc, n = 0;
	int strsize = 0, arrsize = 0;
	char *ret = NULL, *tmp, ch;
	char str[CONFIG_FILE_LINE_MAX + 2];
	enum lineAnalyseResult mr;

	while((rc = read(fd, &ch, 1)) >= 0) {
		*(str + n) = ch;
		if((ch == '\n') || (n == CONFIG_FILE_LINE_MAX) || (rc == 0)) {
			*(str + n) = '\0';
			mr = analyseLine(str);
			if(mr == ANALYSE_ERROR) {
				setCtlParamErr(ERR_LINE_ANALYSE_FAIL, "convFile2Char", __LINE__, NULL, NULL);
				rc = -2;
				break;
			}
			if(mr != IGNORE_LINE) {
				strcat(str, "\n");
				n++;
				tmp = addString(str, ret, &arrsize, CONFIG_FILE_LINE_MAX);
				if(tmp == NULL) {
					setCtlParamErr(ERR_ADD_STRING_FAIL, "convFile2Char", __LINE__, strerror(errno), NULL);
					rc = -2;
					break;
				}
				ret = tmp;
				strsize += n;
			}
			if(n == CONFIG_FILE_LINE_MAX) {
				while(read(fd, &ch, 1) > 0) {
					if(ch == '\n') {
						break;
					}
				}
			}
			if(rc == 0) {		// read end
				break;
			}
			n = -1;
		}
		n++;
	}
	if(rc == -1) {
		setCtlParamErr(ERR_ERRNO, "convFile2Char", __LINE__, strerror(errno), NULL);
		free(ret);
		return NULL;
	} else if(rc == -2) {
		free(ret);
		return NULL;
	}
	return ret;
}

/* Convert to array of character from paragram information */
char *convParagram2Char(struct paragramAttribute *paragAttr)
{
	char pre_str[2];
	char line[CONFIG_FILE_LINE_MAX + 1];
	char *tmp_str;
	char *ret_str = NULL;
	size_t ret_size = 0;
	struct parameterList *paramList;
	struct parameterAttribute *paramAttr;

	if(paragAttr == NULL) {
		setCtlParamErr(ERR_NULL, "convParagram2Char", __LINE__, NULL, NULL);
		return NULL;
	}
	if(strcmp(paragAttr->paragramName, NO_PARAGRAM_PARAMETER) == 0) {
		strcpy(pre_str, "");
	} else {
		strcpy(pre_str, "\t");
		sprintf(line, "%s:\n", paragAttr->paragramName);
		tmp_str = addString(line, ret_str, &ret_size, CONFIG_FILE_LINE_MAX);
		if(tmp_str == NULL) {
			setCtlParamErr(ERR_ADD_STRING_FAIL, "convParagram2Char", __LINE__, NULL, NULL);
			free(ret_str);
			return NULL;
		}
		ret_str = tmp_str;
	}
	paramList = paragAttr->paramList;
	while(paramList != NULL) {
		paramAttr = paramList->paramAttr;
		sprintf(line, "%s%s = %s\n", pre_str, paramAttr->parameterName, paramAttr->parameterValue);
		tmp_str = addString(line, ret_str, &ret_size, CONFIG_FILE_LINE_MAX);
		if(tmp_str == NULL) {
			setCtlParamErr(ERR_ADD_STRING_FAIL, "convParagram2Char", __LINE__, NULL, NULL);
			free(ret_str);
			return NULL;
		}
		ret_str = tmp_str;
		paramList = paramList->nextParam;
	}
	tmp_str = addString("\n", ret_str, &ret_size, 0);
	if(tmp_str == NULL) {
		setCtlParamErr(ERR_ADD_STRING_FAIL, "convParagram2Char", __LINE__, NULL, NULL);
		free(ret_str);
		return NULL;
	}
	ret_str = tmp_str;
	return ret_str;
}

/* Analyse type of line */
enum lineAnalyseResult analyseLine(char *str)
{
	int ret;

	ret = matchRegex(str, PARAGRAM_REGEX_FORMAT, REG_EXTENDED);
	if(ret == REGEX_RESULT_MATCH) {
		return PARAGRAM_LINE;
	} else if(ret == REGEX_RESULT_ERROR) {
		setCtlParamErr(ERR_REGEX_FAIL, "lineAnalyseResult", __LINE__, "PARAGRAM_REGEX", NULL);
		return ANALYSE_ERROR;
	}
	ret = matchRegex(str, PARAGRAM_PARAMETER_REGEX_FORMAT, REG_EXTENDED);
	if(ret == REGEX_RESULT_MATCH) {
		return PARAGRAM_PARAMETER_LINE;
	} else if(ret == REGEX_RESULT_ERROR) {
		setCtlParamErr(ERR_REGEX_FAIL, "lineAnalyseResult", __LINE__, "PARAGRAM_PARAMETER_REGEX", NULL);
		return ANALYSE_ERROR;
	}
	ret = matchRegex(str, NORMAL_PARAMETER_REGEX_FORMAT, REG_EXTENDED);
	if(ret == REGEX_RESULT_MATCH) {
		return NORMAL_PARAMETER_LINE;
	} else if(ret == REGEX_RESULT_ERROR) {
		setCtlParamErr(ERR_REGEX_FAIL, "lineAnalyseResult", __LINE__, "NORMAL_PARAMETER_REGEX", NULL);
		return ANALYSE_ERROR;
	}
	if((*str == '\n') || (*str == '\0')) {
		return NEWLINE_ONLY;
	}
	return IGNORE_LINE;
}

/* Analyse for paragram */
int analyseParagram(char *val, struct configList *cfgList, char *paragName)
{
	int offset = 0;
	int n = 0;
	enum lineAnalyseResult rc;
	char *ret[2];
	char line[CONFIG_FILE_LINE_MAX + 1];
	struct paragramAttribute *pAttr;

	if(cfgList == NULL) {
		setCtlParamErr(ERR_NULL, "analyseParagram", __LINE__, NULL, NULL);
		return -1;
	}
	pAttr = putParagram(paragName, cfgList->paragList);
	if(pAttr == NULL) {
		setCtlParamErr(ERR_PUT_PARAG_FAIL, "analyseParagram", __LINE__, NULL, NULL);
		return -1;
	}
	while((n = getLine(val + offset, line, CONFIG_FILE_LINE_MAX)) != -1) {
		rc = analyseLine(line);
		if((rc == PARAGRAM_LINE) || (rc == NEWLINE_ONLY)) {
			return offset;
		}
		if(rc == PARAGRAM_PARAMETER_LINE) {
			ret[0] = NULL;
			ret[1] = NULL;
			if(extractParameter(line, ret) != 0) {
				setCtlParamErr(ERR_EXTRACT_PARAM_FAIL, "analyseParagram", __LINE__, NULL, NULL);
				return -1;
			}
			pAttr->paramList = putParameter(ret[0], ret[1], pAttr->paramList);
			free(ret[0]);
			free(ret[1]);
			if(pAttr->paramList == NULL) {
				setCtlParamErr(ERR_PUT_PARAM_FAIL, "analyseParagram", __LINE__, NULL, NULL);
				return -1;
			}
		}
		offset += n;
	}
	return offset;
}

/* Analyse parameter line */
int analyseParameter(char *line, struct configList *cfgList)
{
	char *result[2];
	struct paragramAttribute *pAttr;

	if(cfgList == NULL) {
		setCtlParamErr(ERR_NULL, "analyseParameter", __LINE__, NULL, NULL);
		return -1;
	}
	if(extractParameter(line, result) != 0) {
		setCtlParamErr(ERR_EXTRACT_PARAM_FAIL, "analyseParameter", __LINE__, NULL, NULL);
		return -1;
	}
	pAttr = putParagram(NO_PARAGRAM_PARAMETER, cfgList->paragList);
	if(pAttr == NULL) {
		setCtlParamErr(ERR_PUT_PARAG_FAIL, "analyseParameter", __LINE__, NULL, NULL);
		free(result[0]);
		free(result[1]);
		return -1;
	}
	pAttr->paramList = putParameter(result[0], result[1], pAttr->paramList);
	if(pAttr->paramList == NULL) {
		setCtlParamErr(ERR_PUT_PARAM_FAIL, "analyseParameter", __LINE__, NULL, NULL);
		free(result[0]);
		free(result[1]);
		return -1;
	}
	free(result[0]);
	free(result[1]);
	return 0;
}

/* Get parameter name and parameter value from line */
int extractParameter(char *line, char **result)
{
	int cnt = 0, offset;
	char *pName, *pVal, c;

	if((line == NULL) || (result == NULL)) {
		setCtlParamErr(ERR_NULL, "extractParameter", __LINE__, NULL, NULL);
		return -1;
	}
	offset = skipSpaceTab(line);
	cnt = offset;
	/* Search the end of parameter name */
	while((c = *(line + cnt)) != '=') {
		if((c == ' ') || (c == '\t')) {
			break;
		}
		cnt++;
	}
	pName = getSubString(line + offset, cnt - offset, PARAMETER_NAME_MAX);
	if(pName == NULL) {
		setCtlParamErr(ERR_GET_SUBSTR_FAIL, "extractParameter", __LINE__, NULL, NULL);
		return -1;
	}
	/* The cnt variable seek the "=" character */
	while((c = *(line + cnt)) != '=') {
		cnt++;
	}
	offset = ++cnt;
	pVal = getSubString(line + offset, strlen(line) - offset, PARAMETER_VALUE_MAX);
	if(pVal == NULL) {
		setCtlParamErr(ERR_GET_SUBSTR_FAIL, "extractParameter", __LINE__, NULL, NULL);
		free(pName);
		return -1;
	}
	result[0] = pName;
	result[1] = pVal;
	return 0;
}

/*
 *
 * Paragram operation functions
 *
 */
/* The paragram/parameter list display function(for Debug)
 * mode = 0 : only paragram list
 * mode = 1 : include parameter list
 */
void showParagram(struct paragramList *paragList, int mode)
{
	if(paragList == NULL) {
		printf("paragram List is NULL\n");
		return;
	}
	do {
		printf("%s\n", paragList->paragAttr->paragramName);
		if(mode == 1) {
			showParameter(paragList->paragAttr->paramList);
			printf("\n");
		}
	} while((paragList = paragList->nextParag) != NULL);
	return;
}

/* Create paragram attribute and add paragram list */
struct paragramAttribute *putParagram(char *paragName, struct paragramList *paragList)
{
	struct paragramAttribute *pAttr;
	struct paragramList *pList;

	pAttr = searchParagram(paragName, paragList);
	if(pAttr != NULL) {
		/* The paragram already exists */
		return pAttr;
	} else {
		/* Add new paragram */
		pList = addParagram(paragName, paragList);
		if(pList == NULL) {
			setCtlParamErr(ERR_ADD_PARAG_FAIL, "putParagram", __LINE__, NULL, NULL);
			return NULL;
		}
	}
	return pList->paragAttr;
}

/* Search paragram attribute at paragram list */
struct paragramAttribute *searchParagram(char *paragName, struct paragramList *paragList)
{
	struct paragramAttribute *pAttr;

	if(paragList == NULL) {
		return NULL;
	}
	do {
		pAttr = paragList->paragAttr;
		if(strcmp(pAttr->paragramName, paragName) == 0) {
			return pAttr;
		}
	} while((paragList = paragList->nextParag) != NULL);
	return NULL;
}

/* Add paragram list */
struct paragramList *addParagram(char *paragName, struct paragramList *paragList)
{
	struct paragramList *pList = paragList;

	if(paragList == NULL) {
		return createParagramList(paragName);
	}
	while(pList->nextParag != NULL) {
		pList = pList->nextParag;
	}
	pList->nextParag = createParagramList(paragName);
	if(pList->nextParag == NULL) {
		return NULL;
	}
	return pList->nextParag;
}

/* Create paragram list */
struct paragramList *createParagramList(char *paragName)
{
	struct paragramList *pList;

	pList = (struct paragramList *)malloc(sizeof(struct paragramList));
	if(pList == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParagramList", __LINE__, strerror(errno), NULL);
		return NULL;
	}
	pList->paragAttr = createParagramAttribute(paragName);
	if(pList->paragAttr == NULL) {
		setCtlParamErr(ERR_CR_PARAG_ATTR_FAIL, "createParagramList", __LINE__, strerror(errno), NULL);
		free(pList);
		return NULL;
	}
	pList->nextParag = NULL;
	return pList;
}

/* Create paragram attribute */
struct paragramAttribute *createParagramAttribute(char *paragName)
{
	struct paragramAttribute *pAttr;

	pAttr = (struct paragramAttribute *)malloc(sizeof(struct paragramAttribute));
	if(pAttr == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParagramAttribute", __LINE__, strerror(errno), NULL);
		return NULL;
	}
	pAttr->paragramName = strndup(paragName, PARAGRAM_NAME_MAX);
	if(pAttr->paragramName == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParagramAttribute", __LINE__, strerror(errno), NULL);
		free(pAttr);
		return NULL;
	}
	pAttr->paramList = NULL;
	return pAttr;
}

/* Free memory for paragram list */
void freeParagramList(struct paragramList *paragList)
{
	if(paragList == NULL) {
		return;
	}
	freeParagramList(paragList->nextParag);

	freeParagramAttribute(paragList->paragAttr);
	free(paragList);
}

/* Free memory for paragram Attribute */
void freeParagramAttribute(struct paragramAttribute *paragAttr)
{
	free(paragAttr->paragramName);
	freeParameterList(paragAttr->paramList);
	free(paragAttr);
}

/*
 *
 * Parameter operation functions
 *
 */
/* The parameter list display function(for Debug) */
void showParameter(struct parameterList *paramList)
{
	struct parameterAttribute *pAttr;

	if(paramList == NULL) {
		printf("\tparameter List is NULL\n");
		return;
	}
	do {
		pAttr = paramList->paramAttr;
		printf("\t%-32s = %s\n", pAttr->parameterName, pAttr->parameterValue);
	} while((paramList = paramList->nextParam) != NULL);
	return;
}


/* Create/Modify parameter attribute and add parameter list */
struct parameterList *putParameter(char *paramName, char *paramValue, struct parameterList *paramList)
{
	struct parameterAttribute *pAttr;
	struct parameterList *pList;

	pAttr = searchParameter(paramName, paramList);
	if(pAttr != NULL) {
		/* Modify the parameter value */
  		if(modifyParameter(paramValue, pAttr) != 0) {
			setCtlParamErr(ERR_MOD_PARAM_FAIL, "putParameter", __LINE__, NULL, NULL);
  			pList = NULL;
  		} else {
  			pList = paramList;
		}
	} else {
		/* Add new parameter */
		pList = addParameter(paramName, paramValue, paramList);
	}
	return pList;
}


/* search parameter name at parameter list */
struct parameterAttribute *searchParameter(char *paramName, struct parameterList *paramList)
{
	struct parameterAttribute *pAttr;

	if(paramList == NULL) {
		return NULL;
	}
	do {
		pAttr = paramList->paramAttr;
		if(strcmp(pAttr->parameterName, paramName) == 0) {
			return pAttr;
		}
	} while((paramList = paramList->nextParam) != NULL);
	return NULL;
}


/* Create parameter attribute and add parameter list */
struct parameterList *addParameter(char *paramName, char *paramValue, struct parameterList *paramList)
{
	struct parameterList *pList = paramList;

	if(pList == NULL) {
		return createParameterList(paramName, paramValue);
	}
	while(pList->nextParam != NULL) {
		pList = pList->nextParam;
	}
	pList->nextParam = createParameterList(paramName, paramValue);
	if(pList->nextParam == NULL) {
		return NULL;
	}
	return paramList;
}


/* Modify parameter value */
int modifyParameter(char *paramValue, struct parameterAttribute *paramAttr)
{
	char *pVal;

	if(paramAttr == NULL) {
		setCtlParamErr(ERR_NULL, "modifyParameter", __LINE__, NULL, NULL);
		return -1;
	}
	pVal = strndup(paramValue, PARAMETER_VALUE_MAX);
	if(pVal == NULL) {
		setCtlParamErr(ERR_ERRNO, "modifyParameter", __LINE__, strerror(errno), NULL);
		return -1;
	}
	free(paramAttr->parameterValue);
	paramAttr->parameterValue = pVal;
	return 0;
}

/* Create struct of parameter list */
struct parameterList *createParameterList(char *paramName, char *paramValue)
{
	struct parameterList *pList;

	pList = (struct parameterList *)malloc(sizeof(struct parameterList));
	if(pList == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParameterList", __LINE__, strerror(errno), NULL);
		return NULL;
	}
	pList->paramAttr = createParameterAttribute(paramName, paramValue);
	if(pList->paramAttr == NULL) {
		setCtlParamErr(ERR_CR_PARAM_ATTR_FAIL, "createParameterList", __LINE__, NULL, NULL);
		free(pList);
		return NULL;
	}
	pList->nextParam = NULL;
	return pList;
}

/* Create parameter attribute */
struct parameterAttribute *createParameterAttribute(char *paramName, char *paramValue)
{
	struct parameterAttribute *pAttr;

	pAttr = (struct parameterAttribute *)malloc(sizeof(struct parameterAttribute));
	if(pAttr == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParameterAttribute", __LINE__, strerror(errno), NULL);
		return NULL;
	}
	pAttr->parameterName = strndup(paramName, PARAMETER_NAME_MAX);
	if(pAttr->parameterName == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParameterAttribute", __LINE__, strerror(errno), NULL);
		free(pAttr);
		return NULL;
	}
	pAttr->parameterValue = strndup(paramValue, PARAMETER_VALUE_MAX);
	if(pAttr->parameterValue == NULL) {
		setCtlParamErr(ERR_ERRNO, "createParameterAttribute", __LINE__, strerror(errno), NULL);
		free(pAttr->parameterName);
		free(pAttr);
		return NULL;
	}
	return pAttr;
}

/* Free memory for parameter list */
void freeParameterList(struct parameterList *paramList)
{
	if(paramList == NULL) {
		return;
	}
	freeParameterList(paramList->nextParam);

	freeParameterAttribute(paramList->paramAttr);
	free(paramList);
}


/* Free memory for parameter attribute */
void freeParameterAttribute(struct parameterAttribute *paramAttr)
{
	free(paramAttr->parameterName);
	free(paramAttr->parameterValue);
	free(paramAttr);
}



