/*
 * Copyright (c) 2004-2006 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: sm-conf-prtcnf.c,v 1.25 2006/04/21 17:52:02 ca Exp $")

#include "sm/string.h"
#include "sm/sm-conf.h"
#include "sm/sm-conf-prt.h"
#include "sm-conf-type.h"
#include "sm/io.h"
#include "sm/str.h"
#include "sm/util.h"
#include "sm/sm-conf-prt.h"
#include "prtcnf.h"

/*
**  Show configuration data.
**  This shows the data that is actually read from a configuration file
**  and stored in a structure.
*/

#define PRT_COMMENT(fp)	\
	do		\
	{		\
		if (def->scd_description != NULL &&	\
		    *(def->scd_description) != '\0')	\
		{					\
			prt_fmtted((fp), indent, "# ", def->scd_description); \
		}					\
	} while (0)

/*
**  SM_CONF_SUB2DEF -- convert a value into its string representation
**
**	Parameters:
**		def -- sentinel-terminated array of subdefinitions.
**		type -- if non-NULL, only entries of this type are
**			taken into account
**		val -- (choice) value to convert
**		prefix -- print before value
**		postfix -- print after value
**		fp -- file for output
**
**	Returns:
**		SM_SUCCESS
*/

int
sm_conf_sub2valbit(sm_conf_definition_T const *def, sm_conf_type_T const *type, uint32_t val, const char *prefix, const char *postfix, int indent, sm_file_T *fp)
{
	SM_IS_CONF_DEF(def);
	while (def->scd_name != NULL)
	{
		if ((type == NULL || def->scd_type == type) &&
		    (val & def->scd_offset) != 0 &&
		    !SM_IS_FLAG(SM_CONF_FLAG_DONTPRINT, def->scd_flags)
		   )
		{
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s%s%s%s", indent, "",
				prefix, def->scd_name, postfix);
		}
		def++;
	}
	return SM_SUCCESS;
}

/*
**  SM_CONF_SUB2DEF -- convert a value into its string representation
**
**	Parameters:
**		def -- sentinel-terminated array of subdefinitions.
**		type -- if non-NULL, only entries of this type are
**			taken into account
**		val -- (choice) value to convert
**		fp -- file for output
**
**	Returns:
**		SM_SUCCESS
*/

int
sm_conf_sub2valeq(sm_conf_definition_T const *def, sm_conf_type_T const *type, uint32_t val, sm_file_T *fp)
{
	SM_IS_CONF_DEF(def);
	while (def->scd_name != NULL)
	{
		if ((type == NULL || def->scd_type == type) &&
		    val == def->scd_offset)
		{
			sm_io_fprintf(fp, "%s", def->scd_name);
			break;
		}
		def++;
	}
	return SM_SUCCESS;
}

#define PRT_TEXT	\
	do		\
	{		\
		if (text != NULL && sm_str_getlen(text) > 0)	\
		{						\
			sm_io_fprintf(fp, "%S", text);		\
			sm_str_clr(text);			\
		}						\
	} while (0)

/*
**  SM_CONF_PRT_UNION -- print content of one node
**
**	Parameters:
**		def -- configuration definition.
**		cnf -- pointer to structure holding the configuration data
**		offset -- current offset in struct (cnf)
**		fp -- file for output
**		indent -- current indentation
**		prefix -- prefix to print (if not NULL)
**			in case of a section: append section name (+ delimiter)
**		text -- if not NULL: print this if something is printed
**			in this invocation
**
**	Returns:
**		0: didn't print anything
**		>0: something has been printed
*/

static int
sm_conf_prt_union(const sm_conf_definition_T *def, const sm_conf_definition_T *union_def, const void *cnf, sm_file_T *fp, int indent, sm_str_P prefix, sm_str_P text)
{
	int ret, choice_type;
	bool printed;

	printed = false;
	ret = 0;
	choice_type = 0;
	if (union_def != NULL
	    && union_def->scd_type == sm_conf_type_union_type)
	{
		bool found;
		ptrdiff_t offtype;
		const sm_conf_definition_T *union_choice_def;

		offtype = union_def->scd_offset;

		/* fixme: how to find the right size for the type?? */
		choice_type = *(int *)(((char *)cnf) + offtype);
		found = false;
		union_choice_def = union_def + 1;
		while (union_choice_def != NULL
		       && union_choice_def->sm_magic == SM_CONF_DEF_MAGIC
		       && union_choice_def->scd_name != NULL
		       && union_choice_def->scd_type
			  == sm_conf_type_union_choice
		       && !found
		      )
		{
		       if ((int) union_choice_def->scd_offset == choice_type)
				found = true;
			else
				++union_choice_def;
		}
		if (found)
		{
			const char *name;

			PRT_TEXT;
			PRT_COMMENT(fp);
			name = NULL;
			if (union_choice_def->scd_contents->scd_type ==
			    sm_conf_type_section_title)
			{
				ptrdiff_t off;

				off = union_choice_def->scd_contents->scd_offset;
				name = (char *)*((char **)(((char *)cnf) + off));
			}

			sm_io_fprintf(fp, "%*s%s %s%s{\n"
				, indent, ""
				, def->scd_name
				, name != NULL ? name : ""
				, name != NULL ? " " : ""
				);
			sm_conf_prt_cnfs(union_choice_def->scd_contents,
				cnf, 0, fp, indent + 2, prefix, text);
			sm_io_fprintf(fp, "%*s}\n"
				, indent, "");
			printed = true;
			ret = 1;
		}
	}
	if (!printed && choice_type != 0)
	{
		sm_io_fprintf(fp,
			"%*s# %s=union-COULD-NOT-BE-PRINTED;\n"
			, indent, ""
			, def->scd_name);
	}
	return ret;
}

/*
**  SM_CONF_PRT_CNF -- print content of one node
**
**	Parameters:
**		def -- configuration definition.
**		cnf -- pointer to structure holding the configuration data
**		offset -- current offset in struct (cnf)
**		fp -- file for output
**		indent -- current indentation
**		prefix -- prefix to print (if not NULL)
**			in case of a section: append section name (+ delimiter)
**		text -- if not NULL: print this if something is printed
**			in this invocation
**
**	Returns:
**		0: didn't print anything
**		>0: something has been printed
*/

#define SM_C_DELIM	'.'
#define SM_S_OPEN	'['
#define SM_S_CLOSE	']'

static int
sm_conf_prt_cnf(const sm_conf_definition_T *def, const void *cnf, ptrdiff_t offset, sm_file_T *fp, int indent, sm_str_P prefix, sm_str_P text)
{
	const sm_conf_type_T *scdtype;
	ptrdiff_t off;
	int ret;
	char *s;

	SM_IS_CONF_DEF(def);
	scdtype = def->scd_type;
	if (scdtype == NULL)
		return 0;
	ret = 0;

#define PRT_PREFIX	\
	do		\
	{		\
		if (prefix != NULL)	\
			sm_io_fprintf(fp, "%S", prefix);	\
	} while (0)


	off = offset + def->scd_offset;

#define CNF_VAL	(((char *)cnf) + off)

	if (SM_IS_FLAG(def->scd_flags, SM_CONF_FLAG_DPRCD))
		return 0;
	if (scdtype == sm_conf_type_section)
	{
		if (prefix != NULL)
		{
			sm_ret_T ret;
			size_t oldlen;

			PRT_COMMENT(fp);
			oldlen = sm_str_getlen(prefix);
			ret = sm_str_scat(prefix, def->scd_name);
			if (sm_is_err(ret))
				return ret;

#if 0
			if (def->scn_section.scns_keyword != NULL)
			{
				ret = sm_str_scat0(prefix,
						def->scd_name);
				if (sm_is_err(ret))
					return ret;
			}
#endif /* 0 */

			ret = sm_str_put(prefix, SM_C_DELIM);
			if (sm_is_err(ret))
				return ret;
			sm_conf_prt_cnfs(def->scd_contents, cnf,
				off, fp, indent + 2, prefix, text);
			SM_STR_SETLEN(prefix, oldlen);
		}
		else
		{
			int r;

			if (text != NULL)
			{
				sm_file_T text_fp;

				/* currently doesn't fail; check?? */
				(void) sm_str2file(text, &text_fp);
				PRT_COMMENT(&text_fp);
				sm_strprintf(text, "%*s%s {\n"
					, indent, ""
					, def->scd_name);
			}
			else
			{
				PRT_TEXT;
				PRT_COMMENT(fp);
				sm_io_fprintf(fp, "%*s%s {\n"
					, indent, ""
					, def->scd_name);
			}
			r = sm_conf_prt_cnfs(def->scd_contents, cnf, off,
				fp, indent + 2, prefix, text);
			if (r > 0)
			{
				sm_io_fprintf(fp, "%*s}\n"
					, indent, "");
				ret += r;
			}

			/*
			**  This doesn't work for nested sections!
			**  we "just" need to get back to the old text
			**  (see above how prefix is handled).
			*/

			if (text != NULL)
				sm_str_clr(text);
		}
	}
	else if (scdtype == sm_conf_type_section_title)
	{
#if 0
		/*
		**  this shouldn't be printed here as it belongs
		**  in the section header
		*/

		s = (char *)*((char **)CNF_VAL);
		if (s != NULL)
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s", indent, "");
			PRT_PREFIX;
			sm_io_fprintf(fp, "%s" , s);
			ret = 1;
		}
#endif
	}
	else if (scdtype == sm_conf_type_string)
	{
		s = (char *)*((char **)CNF_VAL);
		if (s != NULL)
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s", indent, "");
			PRT_PREFIX;
			sm_io_fprintf(fp, "%s=\"%s\";\n"
					, def->scd_name
					, s);
			ret = 1;
		}
	}
	else if (scdtype == sm_conf_type_char)
	{
		uchar c;

		c = *((uchar *)CNF_VAL);
		if (c != '\0')
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s", indent, "");
			PRT_PREFIX;
			sm_io_fprintf(fp, "%s=\"%c\";\n"
				, def->scd_name
				, c);
			ret = 1;
		}
	}
	else if (scdtype == sm_conf_type_u32)
	{
		PRT_TEXT;
		PRT_COMMENT(fp);
		sm_io_fprintf(fp, "%*s", indent, "");
		PRT_PREFIX;
		if (def->scd_contents != NULL &&
		    def->scd_contents->scd_type ==
			sm_conf_type_u32_suffix &&
		    def->scd_size == sizeof(uint32_t))
		{
			sm_io_fprintf(fp, "%s=", def->scd_name);
			sm_u32totxtwsuffix(def, *(uint32_t *)CNF_VAL,
				fp);
			sm_io_fprintf(fp, ";\n");
			ret = 1;
		}
		else if (def->scd_size == sizeof(uint32_t))
		{
			sm_io_fprintf(fp,
				((def->scd_flags & SM_CONF_FLAG_HEX) == 0)
				? "%s=%lu;\n" : "%s=%#lx;\n"
				, def->scd_name
				, (unsigned long)*(uint32_t *)CNF_VAL);
			ret = 1;
		}
		else if (def->scd_size == sizeof(uint16_t))
		{
			sm_io_fprintf(fp,
				((def->scd_flags & SM_CONF_FLAG_HEX) == 0)
				? "%s=%u;\n" : "%s=%#x;\n"
				, def->scd_name
				, (unsigned int)*(uint16_t *)CNF_VAL);
			ret = 1;
		}
		else if (def->scd_size == sizeof(uint8_t))
		{
			sm_io_fprintf(fp,
				((def->scd_flags & SM_CONF_FLAG_HEX) == 0)
				? "%s=%hu;\n" : "%s=%#hx;\n"
				, def->scd_name
				, (unsigned short)*(uint8_t *)CNF_VAL);
			ret = 1;
		}
	}
	else if (scdtype == sm_conf_type_bool)
	{
		uint val;

		PRT_TEXT;
		PRT_COMMENT(fp);
		sm_io_fprintf(fp, "%*s", indent, "");
		PRT_PREFIX;
		val = 0;
		if (def->scd_size == sizeof(uint32_t))
			val = (uint)*(uint32_t *)CNF_VAL;
		else if (def->scd_size == sizeof(uint16_t))
			val = (uint)*(uint16_t *)CNF_VAL;
		else if (def->scd_size == sizeof(uint8_t))
			val = (uint)*(uint8_t *)CNF_VAL;
		sm_io_fprintf(fp, "%s=%s;\n"
			, def->scd_name
			, val ? "true" : "false");
		ret = 1;
	}
	else if (scdtype == sm_conf_type_ipv4)
	{
		PRT_TEXT;
		PRT_COMMENT(fp);
		sm_io_fprintf(fp, "%*s", indent, "");
		PRT_PREFIX;
		sm_io_fprintf(fp, "%s=%A;\n"
			, def->scd_name
			, *(uint32_t *)CNF_VAL);
		ret = 1;
	}
	else if (scdtype == sm_conf_type_argv)
	{
		size_t i;
		char **argv;

		argv = (char **)CNF_VAL;
		if (argv != NULL && argv[0] != NULL)
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s%s={"
				, indent, ""
				, def->scd_name);
			for (i = 0; (s = argv[i]) != NULL; i++)
				sm_io_fprintf(fp, "%s, ", s);
			sm_io_fprintf(fp, "}\n");
			ret = i;
		}
	}
	else if (scdtype == sm_conf_type_choice)
	{
		uint32_t u;

		u = *(uint32_t *)CNF_VAL;
		if (u != 0 &&
		    (def->scd_flags & SM_CONF_FLAG_MULTIPLE) != 0)
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s%s={\n"
				, indent, ""
				, def->scd_name);
			(void) sm_conf_sub2valbit(def->scd_contents,
					NULL, u, "", ",\n", indent + 2, fp);
			sm_io_fprintf(fp, "%*s}\n", indent, "");
			ret = 1;
		}
		else if (u != 0 &&
		    (def->scd_flags & SM_CONF_FLAG_OR) != 0)
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s%s="
				, indent, ""
				, def->scd_name);
			(void) sm_conf_sub2valbit(def->scd_contents,
					NULL, u, "", "", indent, fp);
			sm_io_fprintf(fp, ";\n");
			ret = 1;
		}
		else if (u != 0)
		{
			PRT_TEXT;
			PRT_COMMENT(fp);
			sm_io_fprintf(fp, "%*s%s="
				, indent, ""
				, def->scd_name);
			(void) sm_conf_sub2valeq(def->scd_contents,
					NULL, u, fp);
			sm_io_fprintf(fp, ";\n");
			ret = 1;
		}
	}
	else if (scdtype == sm_conf_type_array)
	{
		unsigned int n, i;
		ptrdiff_t offn;
		char *ptr;
		const sm_conf_definition_T *array = def->scd_contents;

		if (array[0].scd_name != NULL &&
		    array[0].scd_type == sm_conf_type_section &&
		    array[1].scd_name != NULL &&
		    array[1].scd_type == sm_conf_type_array_n
		   )
		{
			const char *name;

			name = NULL;

			PRT_TEXT;
			PRT_COMMENT(fp);
			offn = offset + array[1].scd_offset;
			n = *(unsigned int *) (((char *)cnf) + offn);
			ret = n;

			offn = offset + array[0].scd_offset;
			if (def->scd_size > 0)
				ptr = ((char *)cnf) + offn;
			else
				ptr = *(char **) (((char *)cnf) + offn);

			for (i = 0; i < n; i++)
			{
				offn = i * array[0].scd_size;
				if (array[0].scd_contents->scd_type ==
				    sm_conf_type_section_title)
				{
					ptrdiff_t offl;

					offl = array[0].scd_contents->scd_offset
							+ offn;
					name = (char *)*((char **)(((char *)ptr)
							+ offl));
				}
				sm_io_fprintf(fp, "%*s%s %s%s{\n"
					, indent, ""
					, def->scd_name
					, name != NULL ? name : ""
					, name != NULL ? " " : ""
					);

				sm_conf_prt_cnfs(array[0].scd_contents,
					ptr, offn,
					fp, indent + 2, prefix, text);
				sm_io_fprintf(fp, "%*s}\n"
					, indent, "");
			}
		}
		else if (array[0].scd_name != NULL &&
		    array[0].scd_type == sm_conf_type_union &&
		    array[1].scd_name != NULL &&
		    array[1].scd_type == sm_conf_type_array_n
		   )
		{
			PRT_TEXT;
			/* PRT_COMMENT(fp); */

			/* get number of elements in array */
			offn = offset + array[1].scd_offset;
			n = *(unsigned int *) (((char *)cnf) + offn);

			/* get pointer to array */
			offn = offset + array[0].scd_offset;
			if (def->scd_size > 0)
				ptr = ((char *)cnf) + offn;
			else
				ptr = *(char **) (((char *)cnf) + offn);

			ret = n;
			for (i = 0; i < n; i++)
			{
				ret = sm_conf_prt_union(def,
					array[0].scd_contents,
					ptr + i * array[0].scd_size,
					fp, indent + 2, prefix, text);
			}
		}
	}
	else if (scdtype == sm_conf_type_union)
	{
		ret = sm_conf_prt_union(def, def->scd_contents,
				(char *)cnf + off, fp,
				indent, prefix, text);
	}
	else if (scdtype == sm_conf_type_union_choice)
	{
		sm_io_fprintf(fp,
			"%*s# %s=union-choice-COULD-NOT-BE-PRINTED;\n"
			, indent, ""
			, def->scd_name);
	}
	else
	{
		sm_io_fprintf(fp, "%*s# %s=unknown-%p-COULD-NOT-BE-PRINTED;\n"
				, indent, ""
				, def->scd_name, scdtype);
	}
	return ret;
}

/*
**  SM_CONF_PRT_CNFS -- print content of configuration tree
**
**	Parameters:
**		def -- configuration definition.
**		cnf -- pointer to structure holding the configuration data
**		offset -- current offset in struct (cnf)
**		fp -- file for output
**		indent -- current indentation
**		prefix -- prefix to print (if not NULL)
**			in case of a section: append section name (+ delimiter)
**		text -- if not NULL: print this if something is printed
**			in this invocation
**
**	Returns:
**		SM_SUCCESS
*/

int
sm_conf_prt_cnfs(const sm_conf_definition_T *defs, const void *cnf, ptrdiff_t offset, sm_file_T *fp, int indent, sm_str_P prefix, sm_str_P text)
{
	const sm_conf_definition_T *def;
	int r;

	r = 0;
	while ((def = defs++) != NULL && def->scd_name != NULL)
	{
		SM_IS_CONF_DEF(def);
		r += sm_conf_prt_cnf(def, cnf, offset, fp, indent, prefix,
				text);
	}
	return r;
}

/*
**  SM_CONF_PRT_CONF -- print content of configuration tree
**	(wrapper for sm_conf_prt_cnfs())
**
**	Parameters:
**		def -- configuration definition.
**		cnf -- pointer to structure holding the configuration data
**		fp -- file for output
**
**	Returns:
**		SM_SUCCESS or ENOMEM
*/

int
sm_conf_prt_conf(const sm_conf_definition_T *defs, const void *cnf, sm_file_T *fp)
{
	int ret;
	sm_str_P sect_text;

	sect_text = sm_str_new(NULL, 256, 8 * 1024);
	if (sect_text == NULL)
		return sm_err_temp(ENOMEM);
	ret = sm_conf_prt_cnfs(defs, cnf, 0, fp, 0, NULL, sect_text);
	SM_STR_FREE(sect_text);
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1