/*
 * Copyright (c) 2004 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: demo-typezoo.c,v 1.10 2005/09/26 23:26:41 ca Exp $")

#if SM_LIBCONF_ALONE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include "sm-conf.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/string.h"
#include "sm/sm-conf.h"
#include "sm/net.h"
#include <stdio.h>
#endif /* SM_LIBCONF_ALONE */

/* DEMO-TYPEZOO.C -- demo sm_conf_scan() */

#ifndef offsetof
#define offsetof(type, member)	((char *)&((type *)0)->member - (char *)0)
#endif

typedef struct substructure
{
	char const	*sub_string;
	unsigned int	sub_u32;

} substructure;

static sm_conf_definition_T
subdefinitions[] = {

	{ SM_CONF_DEF_MAGIC, "sub_u32",
		sm_conf_type_u32,
		offsetof(substructure, sub_u32),
		sizeof(unsigned int),
		"1"
	},

	{ SM_CONF_DEF_MAGIC, "sub_string",
		sm_conf_type_string,
		offsetof(substructure, sub_string),
		0,
		"Foo!"
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

static sm_conf_definition_T
subdefinitions_lefty[] = {

	{ SM_CONF_DEF_MAGIC, "sub_lefty",
		sm_conf_type_bool,
		0,
		sizeof(unsigned int),
		"yes"
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

typedef union vehicle_U
{
	enum vehicle_type
	{
		AIRPLANE	= 1,
		BOAT		= 2,
		CAR		= 3
	} type;

	struct
	{
		enum vehicle_type	air_type;
		char			*air_flight;
		int			air_passengers;
	} airplane;

	struct
	{
		enum vehicle_type	boat_type;
		int			boat_knots;
		char			*boat_name;
	} boat;

	struct
	{
		enum vehicle_type	car_type;
		int			car_mpg;
		char			*car_plate;
	} car;

} vehicle_T;

sm_conf_definition_T const air_definitions[] = {

	{ SM_CONF_DEF_MAGIC, "flight", sm_conf_type_string,
		offsetof(vehicle_T, airplane.air_flight), 0, NULL
	},

	{ SM_CONF_DEF_MAGIC, "passengers", sm_conf_type_u32,
		offsetof(vehicle_T, airplane.air_passengers),
		sizeof(unsigned int), "1"
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T const boat_definitions[] = {

	{ SM_CONF_DEF_MAGIC, "name", sm_conf_type_string,
		offsetof(vehicle_T, boat.boat_name), 0, NULL
	},

	{ SM_CONF_DEF_MAGIC, "knots", sm_conf_type_u32,
		offsetof(vehicle_T, boat.boat_knots),
		sizeof(unsigned int),  NULL
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T const car_definitions[] = {

	{ SM_CONF_DEF_MAGIC, "plate", sm_conf_type_string,
		offsetof(vehicle_T, car.car_plate), 0, NULL
	},

	{ SM_CONF_DEF_MAGIC, "mpg", sm_conf_type_u32,
		offsetof(vehicle_T, car.car_mpg),
		sizeof(unsigned int),  NULL
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T vehicle_definitions[] = {

	{ SM_CONF_DEF_MAGIC, "airplane", sm_conf_type_union_choice, AIRPLANE,
		sizeof(vehicle_T), /* default */ NULL, /* flags */ 0,
		air_definitions
	},

	{ SM_CONF_DEF_MAGIC, "boat", sm_conf_type_union_choice, BOAT,
		sizeof(vehicle_T), /* default */ NULL, /* flags */ 0,
		boat_definitions
	},

	{ SM_CONF_DEF_MAGIC, "car", sm_conf_type_union_choice, CAR,
		sizeof(vehicle_T), /* default */ NULL, /* flags */ 0,
		car_definitions
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};


typedef struct
{
	unsigned long	long_u32;
	unsigned int	int_u32;
	unsigned short	short_u32;
	unsigned short	dice_roll;	/* between 2 and 12 */
	unsigned long	duration;
	char const	*string;
	char		suffix[4];
	unsigned int	bool_true;
	unsigned int	bool_false;
	sm_conf_bytes_T	bytes;
	unsigned int	color;
	uint32_t	flags;
	ipv4_T		ipv4;

	substructure	sub;
	unsigned int	lefty;

	vehicle_T	vehicle;

	char		*twostrings[2];
	char		**argv;

} structure;

sm_conf_definition_T const dice_roll_constraints[] =
{
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32_minimum, 2	},
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32_maximum, 12	},

	/* Sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T const duration_suffixes[] =
{
	{ SM_CONF_DEF_MAGIC, "s", sm_conf_type_u32_suffix, 1		},
	{ SM_CONF_DEF_MAGIC, "m", sm_conf_type_u32_suffix, 60		},
	{ SM_CONF_DEF_MAGIC, "h", sm_conf_type_u32_suffix, 60 * 60	},
	{ SM_CONF_DEF_MAGIC, "d", sm_conf_type_u32_suffix, 60 * 60 * 24	},
	{ SM_CONF_DEF_MAGIC, "w", sm_conf_type_u32_suffix, 60 * 60 * 24 * 7 },
	{ SM_CONF_DEF_MAGIC, "y", sm_conf_type_u32_suffix, 60 * 60 * 24 * 365 },

	/* Sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T const color_names[] =
{
	{ SM_CONF_DEF_MAGIC, "yellow", sm_conf_type_choice_value, 0xFFFF00},
	{ SM_CONF_DEF_MAGIC, "orange", sm_conf_type_choice_value, 0xCCCC00},
	{ SM_CONF_DEF_MAGIC, "red",    sm_conf_type_choice_value, 0xFF0000},
	{ SM_CONF_DEF_MAGIC, "blue",   sm_conf_type_choice_value, 0x0000FF},
	{ SM_CONF_DEF_MAGIC, "green",  sm_conf_type_choice_value, 0x00FF00},
	{ SM_CONF_DEF_MAGIC, "brown",  sm_conf_type_choice_value, 0x333300},
	{ SM_CONF_DEF_MAGIC, "black",  sm_conf_type_choice_value, 0x000000},
	{ SM_CONF_DEF_MAGIC, "white",  sm_conf_type_choice_value, 0xFFFFFF},

	/* Sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T const flags_names[] =
{
	{ SM_CONF_DEF_MAGIC, "opt1", sm_conf_type_choice_value, 0x000001},
	{ SM_CONF_DEF_MAGIC, "opt2", sm_conf_type_choice_value, 0x000002},
	{ SM_CONF_DEF_MAGIC, "opt3", sm_conf_type_choice_value, 0x000010},

	/* Sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};


sm_conf_definition_T definitions[] = {

	/*
	**	N U M B E R S
	*/

	{ SM_CONF_DEF_MAGIC, "long_u32",
		sm_conf_type_u32,
		offsetof(structure, long_u32),
		sizeof(unsigned long),
		"1"
	},

	{ SM_CONF_DEF_MAGIC, "int_u32",
		sm_conf_type_u32,
		offsetof(structure, int_u32),
		sizeof(unsigned int),
		"2"
	},

	{ SM_CONF_DEF_MAGIC, "short_u32",
		sm_conf_type_u32,
		offsetof(structure, short_u32),
		sizeof(unsigned short),
		"3"
	},

	{ SM_CONF_DEF_MAGIC, "ipv4",
		sm_conf_type_ipv4,
		offsetof(structure, ipv4),
		sizeof(ipv4_T),
		"0"
	},

	/* A number that's constrained. */
	{ SM_CONF_DEF_MAGIC, "dice_roll",
		sm_conf_type_u32,
		offsetof(structure, dice_roll),
		sizeof(unsigned short),
		"7",
		0,
		dice_roll_constraints
	},

	/* A number with suffix multipliers. */
	{ SM_CONF_DEF_MAGIC, "duration",
		sm_conf_type_u32,
		offsetof(structure, duration),
		sizeof(unsigned long),
		"0",
		0,
		duration_suffixes
	},

	/*
	**	S T R I N G S
	*/

	/* Your basic string. */
	{ SM_CONF_DEF_MAGIC, "string",
		sm_conf_type_string,
		offsetof(structure, string),
		0,
		"Hello, World?"
	},

	/* fixed-length string */
	{ SM_CONF_DEF_MAGIC, "suffix",
		sm_conf_type_string,
		offsetof(structure, suffix),
		4,
		"c"
	},

	/* argv-style NULL-terminated array, char ** storage. */
	{ SM_CONF_DEF_MAGIC, "argv",
		sm_conf_type_argv,
		offsetof(structure, argv),
		0
	},

	/* argv-style NULL-terminated array, char *[] storage. */
	{ SM_CONF_DEF_MAGIC, "twostrings",
		sm_conf_type_argv,
		offsetof(structure, twostrings),
		2
	},

	/*
	**	B O O L E A N
	*/

	{ SM_CONF_DEF_MAGIC, "bool_true",
		sm_conf_type_bool,
		offsetof(structure, bool_true),
		sizeof(unsigned int),
		"true"
	},

	{ SM_CONF_DEF_MAGIC, "bool_false",
		sm_conf_type_bool,
		offsetof(structure, bool_false),
		sizeof(unsigned int),
		"false"
	},

	/*
	**	B Y T E S T R I N G
	*/

	{ SM_CONF_DEF_MAGIC, "bytes",
		sm_conf_type_bytes,
		offsetof(structure, bytes)
	},


	/*
	**	E N U M
	*/

	{ SM_CONF_DEF_MAGIC, "color",
		sm_conf_type_choice,
		offsetof(structure, color),
		sizeof(unsigned int),
		"green",
		0,
		color_names
	},

	{ SM_CONF_DEF_MAGIC, "flags",
		sm_conf_type_choice,
		offsetof(structure, flags),
		sizeof(uint32_t),
		"0",
		SM_CONF_FLAG_MULTIPLE | SM_CONF_FLAG_NUMERIC,
		flags_names
	},


	/*
	**	S E C T I O N
	*/

	{ SM_CONF_DEF_MAGIC, "sub",
		sm_conf_type_section,
		offsetof(structure, sub),
		sizeof(struct substructure),
		NULL, 0, subdefinitions
	},

	{ SM_CONF_DEF_MAGIC, "sub{lefty}",
		sm_conf_type_section,
		offsetof(structure, lefty),
		sizeof(unsigned int),
		NULL, 0, subdefinitions_lefty
	},


	/*
	**	U N I O N
	*/

	{ SM_CONF_DEF_MAGIC, "vehicle",
		sm_conf_type_union,
		offsetof(structure, vehicle.type),
		sizeof(enum vehicle_type),
		NULL, 0, vehicle_definitions
	},


	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

static void
print_structure(structure *s)
{
	size_t i;

	printf("long_u32: %lu\n",	s->long_u32);
	printf("int_u32: %u\n",		s->int_u32);
	printf("short_u32: %hu\n",	s->short_u32);
	printf("ipv4: %X\n",		(unsigned int) htonl(s->ipv4));
	printf("dice_roll: %hu\n",	s->dice_roll);
	printf("duration: %lu\n",	s->duration);
	printf("string: '%s'\n",	s->string);
	printf("suffix: '%s'\n",	s->suffix);
	printf("bool_true: '%d'\n",	s->bool_true);
	printf("bool_false: '%d'\n",	s->bool_false);
	printf("bytes: [%d]", (int)s->bytes.scb_size);
	for (i = 0; i < s->bytes.scb_size; i++)
		printf(" %2.2hx", (unsigned char)s->bytes.scb_data[i]);
	putchar('\n');
	printf("color: 0x%6.6x\n",	s->color);
	printf("flags: 0x%6.6x\n",	s->flags);

	printf("sub.sub_u32: %d\n", s->sub.sub_u32);
	printf("sub.sub_string: %s\n", s->sub.sub_string);
	printf("lefty: %d\n", s->lefty);

	printf("argv:");
	if (s->argv == NULL)
		fputs(" NULL", stdout);
	else
		for (i = 0; s->argv[i] != NULL; i++)
			printf(" '%s'", s->argv[i]);
	putchar('\n');

	printf("twostrings:");
	for (i = 0; i < 2 && s->twostrings[i] != NULL; i++)
		printf(" '%s'", s->twostrings[i]);
	putchar('\n');
}

static int
process(char const *name, FILE *fp)
{
	sm_conf_T		*stream;
	int			err;
	structure		s;

	if (((stream = sm_conf_new(name ? name : "*stdin*"))) == NULL)
	{
		fprintf(stderr, "error -- sm_conf_new() returns NULL!\n");
		return 1;
	}
	if ((err = sm_conf_read_FILE(stream, name, fp)) != 0)
	{
		char buf[SM_CONF_ERROR_BUFFER_SIZE];
		char const *e = NULL;

		fprintf(stderr, "%s: %s\n",
			name ? name : "*stdin*",
			sm_conf_strerror(err, buf, sizeof buf));

		while ((e = sm_conf_syntax_error(stream, e)) != NULL)
			fprintf(stderr, "%s\n", e);

		sm_conf_destroy(stream);
		return 2;
	}

	memset(&s, 0, sizeof(s));
	err = sm_conf_scan(stream, definitions, 0, &s);
	if (err != 0)
	{
		char buf[SM_CONF_ERROR_BUFFER_SIZE];
		char const *e = NULL;

		fprintf(stderr, "(while scanning) %s: %s\n",
			name ? name : "*stdin*",
			sm_conf_strerror(err, buf, sizeof buf));

		while ((e = sm_conf_syntax_error(stream, e)) != NULL)
			fprintf(stderr, "%s\n", e);

		sm_conf_destroy(stream);
		return 3;
	}

	print_structure(&s);
	sm_conf_destroy(stream);

	return 0;
}

int
main(int ac, char **av)
{
	int	ai;

	if (ac == 1)
		return process("*stdin*", stdin);

	for (ai = 1; ai < ac; ai++)
	{
		int ret = process(av[ai], NULL);
		if (ret != 0)
			return ret;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1