/*
 * parsesect.c  -  Parse the information in one config file section
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: parsesect.c,v 1.9 2007/01/06 18:31:38 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include "privlib.h"
#include "privdb.h"



/*
 * Variables private to this module
 */
static secterr errproc;			/* user-provided error handler */




/*
 * Handle a parse error
 */
static void doerror __F((msg, recname, item),
				const char *msg AND
				const char *recname AND
				const struct dbitem *item)
{
  /* Check if we have to print an error message at all */
  if (msg == NULL || !*msg)
	return;

  /* Call user-provided error handler */
  if (errproc != NULL) {
	(errproc)(msg, recname, item);
	return;
  }

  /* Print error message */
  if (!quiet) {
	fprintf(stderr, "[%s] ", recname);
	if (item == NULL)
		fprintf(stderr, "item \"%s\"", item->name);
	fprintf(stderr, ": %s\n", msg);
  }
}



/*
 * Check if record name matches section
 */
static int findsect __F((sect, name),
				const struct sectdef *sect AND
				const char *name)
{
  int len;

  len = strlen(sect->name);
  if (len > 2 && sect->name[len - 2] == ':' &&
                 sect->name[len - 1] == '*' &&
                 !strncmp(sect->name, name, len - 1)) {
	if (!name[len - 1])
		doerror("missing subrecord name", name, NULL);
	else if (strchr(&(name[len]), ':') != NULL)
		doerror("too many subrecord names", name, NULL);
	else
		return(TRUE);
  } else if (!strcmp(sect->name, name))
	return(TRUE);

  return(FALSE);
}



/*
 * Decode one item
 */
static char *decode __F((params, item),
				struct paramdef *params AND
				struct dbitem *item)
{
  struct enumdef *ep;
  char *arg;
  char *cp;
  int i;

  /*
   * Find item name in parameter list. If the name could not be found,
   * 'i' points to the last entry in the parameter list. In this case,
   * we return with an error message, if the type of this last parameter
   * is par_null. Otherwise, we process the unknown item just as any
   * other item.
   */
  for (i = 0; params[i].name != NULL; i++)
	if (!strcmp(params[i].name, item->name))
		break;

  /*
   * Decode the item
   */
  assert(params[i].type == par_null || params[i].valptr.dummyptr != NULL);
  switch (params[i].type) {

	case par_string:
		/* String parameter */
		if (item->type == item_none)
			break;
		else if (item->type != item_string)
			return("string parameter expected");
		else
			cp = item->val.s;
		copystr(params[i].valptr.strptr, cp);
		break;

	case par_file:
		/* File name parameter */
		if (item->type == item_none)
			break;
		else if (item->type != item_string)
			return("string parameter expected");
		else
			cp = item->val.s;
		if (cp != NULL && strchr(cp, ':') != NULL)
			return("invalid colon in file name");
		copystr(params[i].valptr.fnamptr, cp);
		break;

	case par_bool:
		/* Boolean parameter */
		if (item->type == item_none)
			break;
		else if (item->type != item_boolean)
			return("boolean parameter expected");
		*(params[i].valptr.boolptr) = item->val.b;
		break;

	case par_int:
		/* Short integer parameter */
		if (item->type == item_none)
			break;
		else if (item->type != item_integer ||
		         item->val.i > INT_MAX || item->val.i < INT_MIN)
			return("short integer parameter expected");
		*(params[i].valptr.intptr) = (int)(item->val.i);
		break;

	case par_long:
		/* Long integer parameter */
		if (item->type == item_none)
			break;
		else if (item->type != item_integer)
			return("integer parameter expected");
		*(params[i].valptr.longptr) = item->val.i;
		break;

	case par_enum:
		/* Enumeration parameter */
		if (item->type == item_none)
			break;
		else if (item->type != item_string)
			return("enumeration parameter string expected");
		else
			cp = item->val.s;

		/* Find item value in enumeration list */
		if (cp == NULL)
			cp = "";
		for (ep = params[i].enumlist; ep->enumstr != NULL; ep++)
			if (!strcmp(ep->enumstr, cp))
				break;

		/* Check if item found */
		if (ep->enumstr == NULL)
			return("invalid enumeration value");

		/* Set enumeration value */
		*(params[i].valptr.enumptr) = ep->val;
		break;

	case par_proc:
		/* Call procedure to handle item value */
		arg = NULL;
		if (item->type == item_boolean)
			copystr(&arg, (item->val.b ? "TRUE" : "FALSE"));
		else if (item->type == item_string)
			copystr(&arg, item->val.s);
		else if (item->type == item_integer) {
			arg = (char *)nbmalloc(24);
			sprintf(arg, "%ld", item->val.i);
		}
		cp = (params[i].valptr.procptr)(item->name, &arg, FALSE);
		if (arg != NULL)
			free(arg);
		if (cp != NULL)
			return(cp);
		break;

	case par_null:
	default:
		/* Invalid parameter */
		return("invalid parameter");
  }
  return(NULL);
}



/*
 * Parse a config file section
 */
int nblib_parse_sect __F((recname, itemlist, sects, errhandler),
				const char *recname AND
				struct dbitem *itemlist AND
				struct sectdef *sects AND
				secterr errhandler)
{
  struct sectdef *cursect, *procsect;
  struct dbitem *curitem;
  char *cp;
  int count;

  /* Set global variables */
  errproc = errhandler;

  /* Scan through all section definitions */
  count = 0;
  for (cursect = sects; cursect->name != NULL; cursect++) {

	/* Check if we need to process this section */
	if (!findsect(cursect, recname))
		continue;

	/* Startup new section */
	procsect = cursect;
	if (procsect != NULL && procsect->startsect != NULL) {
		cp = (procsect->startsect)(recname, &procsect);
		if (cp != NULL)
			doerror(cp, recname, NULL);
	}

	/* Decode all section items */
	if (procsect != NULL && procsect->params != NULL) {
		count++;
		curitem = itemlist;
		while (curitem != NULL) {
			cp = decode(procsect->params, curitem);
			if (cp != NULL)
				doerror(cp, recname, curitem);
			curitem = curitem->next;
		}
	}

	/* Terminate current section */
	if (procsect != NULL && procsect->endsect != NULL) {
		cp = (procsect->endsect)(recname, &procsect);
		if (cp != NULL)
			doerror(cp, recname, NULL);
	}
  }

  /* Return number of processed sections */
  return(count);
}

