/*
 * Copyright (c) 2004, 2005 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-array.c,v 1.3 2005/12/26 04:45:09 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 "sm/io.h"
#include "sm/sm-conf-prt.h"

#include <stdio.h>
#endif /* SM_LIBCONF_ALONE */

/* DEMO-ARRAY.C -- demo of arrays and arrays of unions. */

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

static int Verbose = 0;

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;

static 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 }
};

static 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 }
};

static 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 }
};

static sm_conf_definition_T
vehicle_definitions[] =
{

	{ SM_CONF_DEF_MAGIC,
		"",
		sm_conf_type_union_type,
		offsetof(vehicle_T, type),
		sizeof(enum vehicle_type),
		NULL,
		0,
		NULL
	},

	{ SM_CONF_DEF_MAGIC,
		"",
		sm_conf_type_union_choice,
		/* offset = type tag */ AIRPLANE,
		sizeof(vehicle_T),  "airplane", 0,
		air_definitions
	},

	{ SM_CONF_DEF_MAGIC,
		"",
		sm_conf_type_union_choice,
		/* offset = type tag */ BOAT,
		sizeof(vehicle_T), "boat", 0,
		boat_definitions
	},

	{ SM_CONF_DEF_MAGIC,
		"",
		sm_conf_type_union_choice,
		/* offset = type tag */ CAR,
		sizeof(vehicle_T),  "car", 0,
		car_definitions
	},

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

typedef struct
{
	char		*flavor;
	char		*color;
} icecream_T;

static sm_conf_definition_T
icecream_definitions[] =
{

	{ SM_CONF_DEF_MAGIC,
		/* name	*/	"color",
		/* type	*/	sm_conf_type_string,
		/* off	*/	offsetof(icecream_T, color),
		/* size	*/	0,
		/* dflt	*/	NULL,
		/* flag	*/	SM_CONF_FLAG_STRICTLY_REQUIRED,
		/* cont	*/	NULL,
		/* chkf	*/	NULL,
		/* chkd	*/	NULL,
		/* desc	*/	"ice cream color"
	},

	{ SM_CONF_DEF_MAGIC,
		/* name */	"flavor",
		/* type */	sm_conf_type_string,
		/* off  */	offsetof(icecream_T, flavor),
		/* size */	0,
		/* dflt */	NULL,
		/* flag */	SM_CONF_FLAG_STRICTLY_REQUIRED,
		/* cont */	0,
		/* chkf */	NULL,
		/* chkd */	NULL,
		/* desc */	"ice cream flavor"
	},

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

typedef struct
{
	char		name[20];
	int		price;
} candy_T;

static sm_conf_definition_T
candy_definitions[] =
{

	{ SM_CONF_DEF_MAGIC,
		/* name	*/	"name",
		/* type	*/	sm_conf_type_string,
		/* off	*/	offsetof(candy_T, name),
		/* size	*/	20,
		/* dflt	*/	NULL,
		/* flag	*/	  SM_CONF_FLAG_STRICTLY_REQUIRED
				| SM_CONF_FLAG_FLAT,
		/* cont	*/	NULL,
		/* chkf	*/	NULL,
		/* chkd	*/	NULL,
		/* desc	*/	"name of candy"
	},

	{ SM_CONF_DEF_MAGIC,
		/* name */	"price",
		/* type */	sm_conf_type_u32,
		/* off  */	offsetof(candy_T, price),
		/* size */	sizeof(int),
		/* dflt */	NULL,
		/* flag */	SM_CONF_FLAG_STRICTLY_REQUIRED,
		/* cont */	0,
		/* chkf */	NULL,
		/* chkd */	NULL,
		/* desc */	"price of candy"
	},

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

typedef struct
{
	long		*fib;
	unsigned int	fib_n;

	char		pi[13];
	unsigned int	pi_n;

	icecream_T	*icecream;
	unsigned int	icecream_n;

	candy_T		candy[4];
	unsigned int	candy_n;

	vehicle_T	garage[2];
	unsigned int	garage_n;

	vehicle_T	*fleet;
	unsigned int	fleet_n;

} structure;

/*
**  GARAGE_DEFINITIONS -- a two-element fixed array
*/

static sm_conf_definition_T
garage_definitions[] =
{
	{
		SM_CONF_DEF_MAGIC,
		"vehicle",
		sm_conf_type_union,
		offsetof(structure, garage),
		/* size = max # of elements */	2,
		NULL,
		0,
		vehicle_definitions,
		NULL,
		NULL,
		"vehicle parked in a garage"
	},

	{
		SM_CONF_DEF_MAGIC,
		"",
		sm_conf_type_array_n,
		offsetof(structure, garage_n),
		sizeof(unsigned int),
		NULL,
		/* flags */ 0,
		NULL,
		NULL,
		NULL,
		"number of vehicles parked in the garge"
	},

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

static sm_conf_definition_T
garage_outer_definitions[] =
{
	{
		SM_CONF_DEF_MAGIC,
		"vehicle",
		sm_conf_type_array,
		0,
		sizeof(vehicle_T),
		NULL,
		SM_CONF_FLAG_FLAT,
		garage_definitions,
		NULL,
		NULL,
		"garage section"
	},
	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

static sm_conf_definition_T
fleet_definitions[] =
{

	{
		SM_CONF_DEF_MAGIC,
		"fleet vehicle",
		sm_conf_type_union,
		offsetof(structure, fleet),
		sizeof(vehicle_T),
		NULL,
		0,
		vehicle_definitions,
		NULL,
		NULL,
		"fleet of vehicles"
	},

	{
		SM_CONF_DEF_MAGIC,
		"n",
		sm_conf_type_array_n,
		offsetof(structure, fleet_n),
		sizeof(unsigned int),
		NULL,
		0,
		vehicle_definitions,
		NULL,
		NULL,
		"number of vehicles in the fleet (implicit)"
	},

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


static sm_conf_definition_T
fib_definitions[] =
{
	/* numbers: long */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32,
		offsetof(structure, fib), sizeof(long),  NULL, 0, NULL
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(structure, fib_n),
		sizeof(unsigned int),  NULL, 0, NULL
	},

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

static sm_conf_definition_T
pi_definitions[] =
{
	/* numbers: char */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32,
		offsetof(structure, pi), sizeof(char),  NULL, 0, NULL
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(structure, pi_n), sizeof(unsigned int),  NULL, 0, NULL
	},

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

static sm_conf_definition_T
icecreams_definitions[] =
{
	/* flavors: icecream_T */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_section,
		offsetof(structure, icecream),
		sizeof(icecream_T),
		NULL, 0,
		icecream_definitions
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(structure, icecream_n),
		sizeof(unsigned int)
	},

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


static sm_conf_definition_T
candies_definitions[] =
{
	/* candy: candy_T */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_section,
		offsetof(structure, candy),
		sizeof(candy_T),
		NULL, 0,
		candy_definitions
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(structure, candy_n),
		sizeof(unsigned int)
	},

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

static sm_conf_definition_T const
definitions[] =
{

	{ SM_CONF_DEF_MAGIC, "fibonacci",
		sm_conf_type_array,
		0,	/* offset (n/a; see contents)	*/
		0,	/* unlimited size		*/
		NULL,
		0,
		fib_definitions,
		NULL,  NULL, "fibonacci numbers"
	},

	{ SM_CONF_DEF_MAGIC, "pi",
		sm_conf_type_array,
		0,	/* offset (n/a; see contents)	    */
		12,	/* size: maximum number of elements */
		NULL,
		SM_CONF_FLAG_FLAT,
		pi_definitions,
		NULL,  NULL, "digits of pi"
	},

	{ SM_CONF_DEF_MAGIC, "icecream",
		sm_conf_type_array,
		0,	/* offset (n/a; see contents)	    */
		10,	/* size: maximum number of elements */
		NULL,
		SM_CONF_FLAG_MULTIPLE,
		icecreams_definitions,
		NULL,  NULL, "icecream flavor(s)"
	},

	{ SM_CONF_DEF_MAGIC, "candy",
		sm_conf_type_array,
		0,	/* offset (n/a; see contents)	    */
		4,	/* size: maximum number of elements */
		NULL,
		  SM_CONF_FLAG_MULTIPLE
		| SM_CONF_FLAG_FLAT,
		candies_definitions,
		NULL,  NULL, "brands of candy"
	},

	{ SM_CONF_DEF_MAGIC, "garage",
		sm_conf_type_section,
		0,	/* offset */
		2,	/* size (# of elements) */
		NULL,	/* default */
		0,
		garage_outer_definitions,
		NULL,  NULL,
		"A garage with up to two vehicles."
	},

	{ SM_CONF_DEF_MAGIC,
		"vehicle",
		sm_conf_type_array,
		0,
		0,
		NULL,
		SM_CONF_FLAG_MULTIPLE,
		fleet_definitions,
		NULL,
		NULL,
		"A fleet with an arbitrary number of vehicles."
	},

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

static void
print_vehicle(vehicle_T const *v)
{
	switch (v->type)
	{
	  case AIRPLANE:
		printf("airplane { flight=%s passengers=%d }\n",
			v->airplane.air_flight == NULL
				? "(null)" : v->airplane.air_flight,
			v->airplane.air_passengers);
		break;
	  case BOAT:
		printf("boat { knots=%d name=%s }\n",
			v->boat.boat_knots,
			v->boat.boat_name == NULL
				? "(null)" : v->boat.boat_name);
		break;
	  case CAR:
		printf("car { mpg=%d plate=%s }\n",
			v->car.car_mpg,
			v->car.car_plate == NULL ? "(null)" : v->car.car_plate);
		break;

	  case 0:
		printf("(empty)\n");
		break;

	  default:
		printf("<unexpected type %d>\n", (int)v->type);
		break;
	}
}

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

	printf("fibonacci: [%lu]", (unsigned long)s->fib_n);
	for (i = 0; i < s->fib_n; i++)
		printf(" %lu", s->fib[i]);
	putchar('\n');

	printf("pi: [%lu]", (unsigned long)s->pi_n);
	for (i = 0; i < s->pi_n; i++)
		printf(" %d", (int)s->pi[i]);
	putchar('\n');

	printf("icecream: [%lu]\n", (unsigned long)s->icecream_n);
	for (i = 0; i < s->icecream_n; i++)
		printf("[%lu] { flavor=%s; color=%s; }\n",
			(unsigned long)i,
			s->icecream[i].flavor
				? s->icecream[i].flavor
				: "(null)",
			s->icecream[i].color
				? s->icecream[i].color
				: "(null)");

	printf("candy: [%lu]\n", (unsigned long)s->candy_n);
	for (i = 0; i < s->candy_n; i++)
		printf("[%lu] { name=%s; price=%lu; }\n",
			(unsigned long)i,
			s->candy[i].name
				? s->candy[i].name
				: "(null)",
			(unsigned long)s->candy[i].price);

	printf("garage: [%d]\n", s->garage_n);
	for (i = 0; i < 2; i++)
	{
		printf("[%d] ", (int)i);
		print_vehicle(s->garage + i);
	}

	printf("fleet: [%d]\n", s->fleet_n);
	for (i = 0; i < s->fleet_n; i++)
	{
		printf("[%d] ", (int)i);
		print_vehicle(s->fleet + 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;
	}

	if (Verbose > 0)
	{
		sm_io_fprintf(smioout, "configuration\n");
		sm_conf_prt_conf(definitions, &s, smioout);
		sm_io_flush(smioout);
	}

	print_structure(&s);
	sm_conf_destroy(stream);

	return 0;
}

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

	while ((r = getopt(ac, av, "V")) != -1)
	{
		switch (r)
		{
		  case 'V':
			Verbose++;
			break;
		  default:
			/* usage(av[0]); */
			return 1;
		}
	}

	ac -= optind;
	av += optind;

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

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


syntax highlighted by Code2HTML, v. 0.9.1