/*
 * getdb.c  -  Read system definitions from database file
 *
 * Copyright (C) 1998-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: getdb.c,v 1.25 2007/02/01 18:42:10 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include <system/bootrom.h>
#include "makerom.h"
#include "doconfig.h"
#include "protini.h"



/* Variables private to this module */
static struct bootdef bootd;		/* boot definition record */
static char *protini = NULL;		/* protocol.ini contents */
static char *undiname = NULL;		/* UNDI driver file name */
static int pddrvidx = -1;		/* packet driver index */
static int ndisdrvidx = -1;		/* NDIS driver index */
static struct progdef progs;		/* program definitions */




/*
 * Table containing flag names for kernel flags parameter
 */
static const struct {
	char *name;
	int   mask;
} flgtbl[] = {
	{ "nouid",	KRNFLG_NOUID },
	{ "opts",	KRNFLG_OPTS },
	{ "nobpext",	KRNFLG_NOBPEXT },
	{ NULL,		0 }
};



/*
 * Write all kernel flags into a string
 */
static char *writeflags __F((flags, val), int flags AND char **val)
{
  int i, j, len, bufsize;
  char *cp, *ret;

  /* Process all flags in turn */
  bufsize = 0;
  ret = NULL;
  for (i = 0; flgtbl[i].name != NULL; i++) {
	if ((flags & flgtbl[i].mask) != 0) {
		j = strlen(flgtbl[i].name);
		len = (ret == NULL ? 0 : strlen(ret));
		bufsize = (len == 0 ? j : (len + j + 1));
		cp = ret;
		ret = (char *)nbmalloc(bufsize + 1);
		if (cp != NULL) {
			strcpy(ret, cp);
			free(cp);
		}
		cp = ret + len;
		sprintf(cp, "%s%s", (len == 0 ? "" : ","), flgtbl[i].name);
	}
  }

  /* Prepare return values */
  *val = ret;
  return(NULL);
}



/*
 * Process kernel flags
 */
static char *procflags __F((name, arg, dowrite),
				const char *name AND
				char **arg AND
				int dowrite)
{
  char *tok, *cp;
  int i;

  /* Check if we have to write the flags into a string */
  if (dowrite)
	return(writeflags(bootd.kernel_flags, arg));

  /* Check if we have something to read */
  if (*arg == NULL)
	return(NULL);

  /* Decode all flag values */
  tok = strtok(*arg, ",");
  while (tok) {
	/* Skip leading blanks */
	while (*tok && (*tok == ' ' || *tok == '\t'))
		tok++;
	cp = tok;
	/* Find end of flag name */
	while (*cp && (*cp != ' ' && *cp != '\t'))
		cp++;
	if (*cp) {
		/* Skip trailing blanks */
		while (*cp && (*cp == ' ' || *cp == '\t'))
			*(cp++) = '\0';
		if (*cp)
			return("invalid characters following kernel flag");
	}
	/* Find flag name in list */
	for (i = 0; flgtbl[i].name != NULL; i++)
		if (!strcmp(tok, flgtbl[i].name))
			break;
	if (flgtbl[i].name == NULL)
		return("invalid kernel flag");
	/* Set flag value */
	bootd.kernel_flags |= flgtbl[i].mask;
	/* Proceed with next option */
	tok = strtok(NULL, ",");
  }
  return(NULL);
}



/*
 * Table containing option names for "ldoptsX" parameters.
 */
static const struct {
	char *name;
	int   boolval;
	int   intmin, intmax;
	int  *optptr[MAXLOADERS];
} opttbl[] = {
	{ "useint18",	TRUE, 0, 0,
			{&bootd.loaders[0].useint18,
			 &bootd.loaders[1].useint18,
			 &bootd.loaders[2].useint18}},
	{ "nobbs",	TRUE, 0, 0,
			{&bootd.loaders[0].nobbs,
			 &bootd.loaders[1].nobbs,
			 &bootd.loaders[2].nobbs}},
	{ "bootask",	TRUE, 0, 0,
			{&bootd.loaders[0].bootask,
			 &bootd.loaders[1].bootask,
			 &bootd.loaders[2].bootask}},
	{ "asktime",	FALSE, 0, 31,
			{&bootd.loaders[0].asktime,
			 &bootd.loaders[1].asktime,
			 &bootd.loaders[2].asktime}},
	{ "devnum",	FALSE, 0, 7,
			{&bootd.loaders[0].devnum,
			 &bootd.loaders[1].devnum,
			 &bootd.loaders[2].devnum}},
	{NULL,		FALSE, 0, 0, {NULL, NULL, NULL}}
};



/*
 * Write all options into a string
 */
static char *writeopts __F((num, val), int num AND char **val)
{
  int i, j, len, bufsize;
  char *cp, *ret;

  /* Check that we have a loader definition at all */
  if (num >= bootd.loadernum)
	return(NULL);

  /* Process all options in turn */
  bufsize = 0;
  ret = NULL;
  for (i = 0; opttbl[i].name != NULL; i++) {
	/* Determine necessary space in buffer */
	j = 0;
	if ((opttbl[i].boolval && *opttbl[i].optptr[num]) ||
	    (!opttbl[i].boolval && *opttbl[i].optptr[num] != 0)) {
		j = strlen(opttbl[i].name);
		if (!opttbl[i].boolval) {
			/* Allow for '=' and 6 digits */
			j += 7;
		}
	}

	/* Enlarge buffer as necessary and write option value */
	if (j > 0) {
		len = (ret == NULL ? 0 : strlen(ret));
		bufsize = (len == 0 ? j : (len + j + 1));
		cp = ret;
		ret = (char *)nbmalloc(bufsize + 1);
		if (cp != NULL) {
			strcpy(ret, cp);
			free(cp);
		}
		cp = ret + len;
		sprintf(cp, "%s%s", (len == 0 ? "" : ","), opttbl[i].name);
		if (!opttbl[i].boolval) {
			cp += strlen(opttbl[i].name) + (len == 0 ? 0 : 1);
			sprintf(cp, "=%d", *opttbl[i].optptr[num]);
		}
	}
  }

  /* Prepare return values */
  *val = ret;
  return(NULL);
}



/*
 * Process bootrom loader options. This routine gets called everytime
 * a "ldoptsX" parameter is found in the database file.
 */
static char *procopts __F((name, arg, dowrite),
				const char *name AND
				char **arg AND
				int dowrite)
{
  char *tok, *val, *cp;
  int i, num;
  long l;

  /* Isolate loader number from parameter name */
  assert(strlen(name) == 7 && isdigit((int)(name[6])));
  num = name[6] - '1';
  assert(num >= 0 && num < MAXLOADERS);

  /* Check if we have to write the options into a string */
  if (dowrite)
	return(writeopts(num, arg));

  /* Check if we have something to read */
  if (*arg == NULL)
	return(NULL);

  /*
   * Otherwise we have to read the options string, so find each option
   * in the list, and process its argument.
   */
  tok = strtok(*arg, ",");
  while (tok) {
	/* Skip leading blanks */
	while (*tok && (*tok == ' ' || *tok == '\t'))
		tok++;
	cp = tok;
	/* Find end of option name */
	while (*cp && (*cp != ' ' && *cp != '\t' && *cp != '='))
		cp++;
	if (*cp) {
		/* Skip trailing blanks */
		while (*cp && (*cp == ' ' || *cp == '\t'))
			*(cp++) = '\0';
		if (*cp && *cp != '=')
			return("invalid characters following option name");
	}
	/* Find option value */
	val = NULL;
	if (*cp == '=') {
		/* Skip leading blanks of option value */
		*(cp++) = '\0';
		while (*cp && (*cp == ' ' || *cp == '\t'))
			cp++;
		if (!*cp)
			return("missing option value");
		val = cp;
		/* Find end of option value */
		while (*cp && (*cp != ' ' && *cp != '\t')) {
			*cp = toupper(*cp);
			cp++;
		}
		if (*cp) {
			/* Skip trailing blanks */
			while (*cp && (*cp == ' ' || *cp == '\t'))
				*(cp++) = '\0';
			if (*cp)
				return("invalid characters following option value");
		}
	}
	/* Find option name in list */
	for (i = 0; opttbl[i].name != NULL; i++)
		if (!strcmp(tok, opttbl[i].name))
			break;
	if (opttbl[i].name == NULL)
		return("invalid option");
	/* Set option value */
	if (opttbl[i].boolval) {
		if (val == NULL)
			*opttbl[i].optptr[num] = TRUE;
		else if (!strcmp(val, "TRUE"))
			*opttbl[i].optptr[num] = TRUE;
		else if (!strcmp(val, "FALSE"))
			*opttbl[i].optptr[num] = FALSE;
		else
			return("invalid boolean option value");
	} else {
		if (val == NULL)
			return("missing option value");
		l = strtol(val, &cp, 10);
		if (cp != NULL && *cp)
			return("invalid integer option value");
		if (l < opttbl[i].intmin || l > opttbl[i].intmax)
			return("integer option value out of range");
		*opttbl[i].optptr[num] = l;
	}

	/* Proceed with next option */
	tok = strtok(NULL, ",");
  }
  return(NULL);
}



/* Available network driver interface types */
static struct enumdef dtypes[] = {
  { "packet driver",	DRVTYPE_PD },
  { "ndis driver",	DRVTYPE_NDIS },
  { "undi driver",	DRVTYPE_UNDI },
  { "",			DRVTYPE_NONE },
  { NULL,		DRVTYPE_NONE }
};



/* Available bus types */
static struct enumdef btypes[] = {
  { "ISA",		BUSTYPE_ISA },
  { "EISA",		BUSTYPE_EISA },
  { "MCA",		BUSTYPE_MCA },
  { "PCI",		BUSTYPE_PCI },
  { "",			BUSTYPE_NONE },
  { NULL,		BUSTYPE_NONE }
};



/* Available types for output file */
static struct enumdef ftypes[] = {
  { "binary",		OUT_BINARY },
  { "intel hex",	OUT_IHEX },
  { "motorola hex",	OUT_MHEX },
  { "tektronix hex",	OUT_THEX },
  { "bios",		OUT_BIOS },
  { "flash",		OUT_FLASH },
  { "",			OUT_NONE },
  { NULL,		OUT_NONE }
};



/* Parameters in each bootrom section of database file */
static struct paramdef dbparams[] = {
  /* General bootrom parameters */
  { "kernel",      par_file,   NULL,	{&bootd.kernelname}},
  { "bustype",     par_enum,   btypes,	{&bootd.bus_type}},
  { "pcivendor",   par_int,    NULL,	{&bootd.pci_vendid}},
  { "pcidevice",   par_int,    NULL,	{&bootd.pci_devid}},
  { "pnpdevid",    par_string, NULL,	{&bootd.pnp_devid}},
  { "krnflags",    par_proc,   NULL,	{(voidstar)&procflags}},
  { "netdriver",   par_file,   NULL,	{&bootd.netdrv.name}},
  { "netdrvtype",  par_enum,   dtypes,	{&bootd.netdrv.drivertype}},
  { "netdrvflags", par_int,    NULL,	{&bootd.netdrv.driverflags}},

#if MAXLOADERS < 3
#error Invalid number of loaders
#endif

  /* Parameters for defining loaders */
  { "loader1",     par_file,   NULL,	{&bootd.loaders[0].name}},
  { "loader2",     par_file,   NULL,	{&bootd.loaders[1].name}},
  { "loader3",     par_file,   NULL,	{&bootd.loaders[2].name}},
  { "outname1",    par_file,   NULL,	{&bootd.loaders[0].outname}},
  { "outname2",    par_file,   NULL,	{&bootd.loaders[1].outname}},
  { "outname3",    par_file,   NULL,	{&bootd.loaders[2].outname}},
  { "outsize1",    par_int,    NULL,	{&bootd.loaders[0].outsize}},
  { "outsize2",    par_int,    NULL,	{&bootd.loaders[1].outsize}},
  { "outsize3",    par_int,    NULL,	{&bootd.loaders[2].outsize}},
  { "outtype1",	   par_enum,   ftypes,	{&bootd.loaders[0].outtype}},
  { "outtype2",	   par_enum,   ftypes,	{&bootd.loaders[1].outtype}},
  { "outtype3",    par_enum,   ftypes,	{&bootd.loaders[2].outtype}},
  { "ldopts1",     par_proc,   NULL,	{(voidstar)&procopts}},
  { "ldopts2",     par_proc,   NULL,	{(voidstar)&procopts}},
  { "ldopts3",     par_proc,   NULL,	{(voidstar)&procopts}},

#if MAXPROGS < 8
#error Invalid number of drivers
#endif

  /* Parameters for packet driver interface */
  { "pktdrvidx",   par_int,    NULL,	{&pddrvidx}},

  /* Parameters for NDIS interface */
  { "protini",     par_string, NULL,	{&protini}},
  { "ndisdrvidx",  par_int,    NULL,	{&ndisdrvidx}},

  /* Parameters for UNDI interface */
  { "undiprog",    par_file,   NULL,	{&undiname}},

  /* DOS driver programs */
  { "prog0",       par_file,   NULL,	{&progs.prognames[0]}},
  { "prog1",       par_file,   NULL,	{&progs.prognames[1]}},
  { "prog2",       par_file,   NULL,	{&progs.prognames[2]}},
  { "prog3",       par_file,   NULL,	{&progs.prognames[3]}},
  { "prog4",       par_file,   NULL,	{&progs.prognames[4]}},
  { "prog5",       par_file,   NULL,	{&progs.prognames[5]}},
  { "prog6",       par_file,   NULL,	{&progs.prognames[6]}},
  { "prog7",       par_file,   NULL,	{&progs.prognames[7]}},
  { "args0",       par_string, NULL,	{&progs.progargs[0]}},
  { "args1",       par_string, NULL,	{&progs.progargs[1]}},
  { "args2",       par_string, NULL,	{&progs.progargs[2]}},
  { "args3",       par_string, NULL,	{&progs.progargs[3]}},
  { "args4",       par_string, NULL,	{&progs.progargs[4]}},
  { "args5",       par_string, NULL,	{&progs.progargs[5]}},
  { "args6",       par_string, NULL,	{&progs.progargs[6]}},
  { "args7",       par_string, NULL,	{&progs.progargs[7]}},
  { "minsize0",    par_long,   NULL,	{&progs.minsizes[0]}},
  { "minsize1",    par_long,   NULL,	{&progs.minsizes[1]}},
  { "minsize2",    par_long,   NULL,	{&progs.minsizes[2]}},
  { "minsize3",    par_long,   NULL,	{&progs.minsizes[3]}},
  { "minsize4",    par_long,   NULL,	{&progs.minsizes[4]}},
  { "minsize5",    par_long,   NULL,	{&progs.minsizes[5]}},
  { "minsize6",    par_long,   NULL,	{&progs.minsizes[6]}},
  { "minsize7",    par_long,   NULL,	{&progs.minsizes[7]}},
  { "maxsize0",    par_long,   NULL,	{&progs.maxsizes[0]}},
  { "maxsize1",    par_long,   NULL,	{&progs.maxsizes[1]}},
  { "maxsize2",    par_long,   NULL,	{&progs.maxsizes[2]}},
  { "maxsize3",    par_long,   NULL,	{&progs.maxsizes[3]}},
  { "maxsize4",    par_long,   NULL,	{&progs.maxsizes[4]}},
  { "maxsize5",    par_long,   NULL,	{&progs.maxsizes[5]}},
  { "maxsize6",    par_long,   NULL,	{&progs.maxsizes[6]}},
  { "maxsize7",    par_long,   NULL,	{&progs.maxsizes[7]}},
  { NULL,          par_null,   NULL,	{NULL}}
};



/*
 * Check and reorder a list of DOS programs
 */
static void checkprogs __F((drvindex, namebuf), int *drvindex AND char *namebuf)
{
  int i, j;
  char *configdir;

  progs.prognum = 0;
  for (i = 0, j = -1; i < MAXPROGS; i++) {
	if (progs.prognames[i] != NULL) {
		if (drvindex != NULL && i == *drvindex)
			configdir = config.netdrvdir;
		else
			configdir = config.utilsdir;
		checkaccess(&(progs.prognames[i]), configdir,
							ACCESS_FILE_READ);
		if (progs.prognames[i] == NULL) {
			prnerr("program %d invalid in section <%s>",
								i, namebuf);
			nbexit(EXIT_DB);
		}
	}
	if (progs.prognames[i] == NULL) {
		if (progs.progargs[i] != NULL) {
			free(progs.progargs[i]);
			progs.progargs[i] = NULL;
		}
		if (j < 0)
			j = i;
	} else if (j >= 0) {
		if (drvindex != NULL && i == *drvindex)
			*drvindex = j;
		progs.prognames[j] = progs.prognames[i];
		progs.progargs[j] = progs.progargs[i];
		progs.minsizes[j] = progs.minsizes[i];
		progs.maxsizes[j] = progs.maxsizes[i];
		progs.prognames[i] = NULL;
		progs.progargs[i] = NULL;
		progs.minsizes[i] = -1L;
		progs.maxsizes[i] = -1L;
		progs.prognum++;
		i = j;
		j = -1;
	} else
		progs.prognum++;
  }

  if (drvindex != NULL && (*drvindex < 0 || *drvindex >= progs.prognum)) {
	prnerr("network driver index %d invalid in section <%s>",
							*drvindex, namebuf);
	nbexit(EXIT_DB);
  }

  for (i = 0; i < progs.prognum; i++) {
	if (progs.minsizes[i] < 0L)
		progs.minsizes[i] = -1L;
	if (progs.maxsizes[i] < 0L)
		progs.maxsizes[i] = -1L;
	if (progs.maxsizes[i] == 0L ||
	    (i == 0 && progs.minsizes[i] == 0L) ||
	    (progs.minsizes[i] > 0L && progs.maxsizes[i] > 0L &&
	     progs.minsizes[i] > progs.maxsizes[i])) {
		prnerr("invalid execution size for program %d in section <%s>",
								i, namebuf);
		nbexit(EXIT_DB);
	}
  }
}



/*
 * Read one entry from the database file
 */
struct bootdef *getdb __F((name), const char *name)
{
  char *sectname;
  int i, j;

  /* Get entry from database */
  sectname = (char *)nbmalloc(strlen(name) + strlen(DBSECTNAME) + 2);
  sprintf(sectname, "%s:%s", DBSECTNAME, name);
  memzero(&bootd, sizeof(bootd));
  memzero(&progs, sizeof(progs));
  for (i = 0; i < MAXPROGS; i++) {
	progs.minsizes[i] = -1L;
	progs.maxsizes[i] = -1L;
  }
  if (!opensysdb(TRUE))
	nbexit(-1);
  if (!readsysdb(sectname, dbparams))
	nbexit(-1);
  closesysdb();

  /* Check for correct and missing values */
  copystr(&bootd.name, name);
  checkaccess(&bootd.kernelname, config.bindir, ACCESS_FILE_READ);
  if (!bootd.kernelname) {
	prnerr("no or invalid kernel file name given in section <%s>",
								sectname);
	nbexit(EXIT_DB);
  }
  if (bootd.pnp_devid != NULL && strlen(bootd.pnp_devid) == 0) {
	free(bootd.pnp_devid);
	bootd.pnp_devid = NULL;
  }
  if (bootd.bus_type == BUSTYPE_NONE)
	bootd.bus_type = BUSTYPE_ISA;
  if (bootd.bus_type == BUSTYPE_PCI) {
	if (bootd.pci_vendid == 0 || bootd.pci_devid == 0) {
		prnerr("missing PCI vendor/device ID in section <%s>", sectname);
		nbexit(EXIT_DB);
	}
	if (bootd.pnp_devid != NULL) {
		prnerr("PnP device ID for PCI network card in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
  } else if (bootd.pci_vendid != 0 || bootd.pci_devid != 0) {
	prnerr("PCI vendor/device ID specified for non-PCI device in section <%s>",
								sectname);
	nbexit(EXIT_DB);
  }
  if (bootd.pnp_devid != NULL) {
	if (strlen(bootd.pnp_devid) != PNPIDLEN  ||
	    !isalpha((int)(bootd.pnp_devid[0]))  ||
	    !isalpha((int)(bootd.pnp_devid[1]))  ||
	    !isalpha((int)(bootd.pnp_devid[2]))  ||
	    !isxdigit((int)(bootd.pnp_devid[3])) ||
	    !isxdigit((int)(bootd.pnp_devid[4])) ||
	    !isxdigit((int)(bootd.pnp_devid[5])) ||
	    !isxdigit((int)(bootd.pnp_devid[6]))) {
		prnerr("invalid PnP device ID in section <%s>", sectname);
		nbexit(EXIT_DB);
	}
  }
  bootd.canflash = checkflash(&bootd);
  checkaccess(&bootd.netdrv.name, config.bindir, ACCESS_FILE_READ);
  if (!bootd.netdrv.name) {
	prnerr("no or invalid network driver name given in section <%s>",
								sectname);
	nbexit(EXIT_DB);
  }
  if (bootd.netdrv.drivertype == DRVTYPE_NONE) {
	prnerr("missing driver type in section <%s>", sectname);
	nbexit(EXIT_DB);
  }

  /* Reorder the loader list and check for correct values */
  bootd.loadernum = 0;
  for (i = 0, j = -1; i < MAXLOADERS; i++) {
	if (bootd.loaders[i].name != NULL) {
		checkaccess(&bootd.loaders[i].name, config.bindir,
							ACCESS_FILE_READ);
		if (bootd.loaders[i].name == NULL) {
			prnerr("loader %d invalid in section <%s>",
								i, sectname);
			nbexit(EXIT_DB);
		}
	}
	if (bootd.loaders[i].name == NULL) {
		if (bootd.loaders[i].outname != NULL) {
			free(bootd.loaders[i].outname);
			bootd.loaders[i].outname = NULL;
		}
		if (j < 0)
			j = i;
	} else {
		if (bootd.loaders[i].outname == NULL) {
			prnerr("no output file given for loader %d in section <%s>",
								i, sectname);
			nbexit(EXIT_DB);
		}
		if (bootd.loaders[i].outtype == OUT_NONE)
			bootd.loaders[i].outtype = OUT_BINARY;
		bootd.loadernum++;
		if (j >= 0) {
			bootd.loaders[j] = bootd.loaders[i];
			bootd.loaders[i].name = NULL;
			i = j;
			j = -1;
		}
	}
  }
  if (bootd.loadernum == 0) {
	prnerr("no bootrom loader specified in section <%s>", sectname);
	nbexit(EXIT_DB);
  }

  /* Check for correct values for the different network driver types */
  if (bootd.netdrv.drivertype == DRVTYPE_PD) {
	struct pktdrvdef *pdp = &(bootd.netdrv.driverdefs.pd);

	checkprogs(&pddrvidx, sectname);
	if (progs.prognum == 0) {
		prnerr("no or invalid packet driver specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (ndisdrvidx >= 0) {
		prnerr("invalid ndisdrvidx specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (protini != NULL) {
		prnerr("invalid protini specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (undiname != NULL) {
		prnerr("invalid undiprog specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	pdp->drvindex = pddrvidx;
	pdp->progs = progs;
  } else if (bootd.netdrv.drivertype == DRVTYPE_NDIS) {
	struct ndisdef *np = &(bootd.netdrv.driverdefs.ndis);

	checkprogs(&ndisdrvidx, sectname);
	if (progs.prognum == 0) {
		prnerr("no or invalid NDIS driver specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (pddrvidx >= 0) {
		prnerr("invalid pddrvidx specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (undiname != NULL) {
		prnerr("invalid undiprog specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	np->drvindex = ndisdrvidx;
	np->progs = progs;

	if (np->progs.progargs[np->drvindex] != NULL) {
		free(np->progs.progargs[np->drvindex]);
		np->progs.progargs[np->drvindex] = NULL;
	}

	if (protini == NULL) {
		prnerr("missing \'protini\' entry in section <%s>\n",
								sectname);
		nbexit(EXIT_DB);
	}
	if (!strncmp(protini, "file:", 5)) {
		np->protocolini = readprotini(&protini[5]);
		free(protini);
	} else
		np->protocolini = protini;
  } else if (bootd.netdrv.drivertype == DRVTYPE_UNDI) {
	checkprogs(NULL, sectname);
	if (progs.prognum > 0) {
		prnerr("programs not allowed with UNDI driver in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (pddrvidx >= 0) {
		prnerr("invalid pddrvidx specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (ndisdrvidx >= 0) {
		prnerr("invalid ndisdrvidx specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	if (protini != NULL) {
		prnerr("invalid protini specified in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	checkaccess(&undiname, config.netdrvdir, ACCESS_FILE_READ);
	if (undiname == NULL) {
		prnerr("missing or invalid UNDI driver name in section <%s>",
								sectname);
		nbexit(EXIT_DB);
	}
	bootd.netdrv.driverdefs.undi.name = undiname;
  }
  free(sectname);
  return(&bootd);
}



/*
 * Write one entry into the database file
 */
void putdb __F((bp), const struct bootdef *bp)
{
  char *sectname;
  int len, i;

  /* Check if we have something to write at all */
  if (bp->name == NULL || (len = strlen(bp->name)) == 0)
	return;

  /* Copy the boot definition into our own static data area */
  if (bp != &bootd)
	bootd = *bp;

  /* Copy certain fixed values into the static data area */
  if (bootd.netdrv.drivertype == DRVTYPE_PD) {
	struct pktdrvdef *pdp = &(bootd.netdrv.driverdefs.pd);

	pddrvidx = pdp->drvindex;
	ndisdrvidx = -1;
	protini = NULL;
	progs = pdp->progs;
	undiname = NULL;
  } else if (bootd.netdrv.drivertype == DRVTYPE_NDIS) {
	struct ndisdef *np = &(bootd.netdrv.driverdefs.ndis);

	pddrvidx = -1;
	ndisdrvidx = np->drvindex;
	protini = np->protocolini;
	progs = np->progs;
	undiname = NULL;
  } else if (bootd.netdrv.drivertype == DRVTYPE_UNDI) {
	pddrvidx = -1;
	ndisdrvidx = -1;
	protini = NULL;
	undiname = bootd.netdrv.driverdefs.undi.name;
	progs.prognum = 0;
  }

  /* Clear DOS program array */
  for (i = progs.prognum; i < MAXPROGS; i++) {
	progs.prognames[i] = NULL;
	progs.progargs[i] = NULL;
	progs.minsizes[i] = -1L;
	progs.maxsizes[i] = -1L;
  }

  /* Generate section name */
  sectname = (char *)nbmalloc(len + strlen(DBSECTNAME) + 3);
  sprintf(sectname, "%s:%s", DBSECTNAME, bootd.name);

  /* Write section into database */
  if (!opensysdb(FALSE))
	nbexit(-1);
  if (!writesysdb(sectname, dbparams))
	nbexit(-1);
  closesysdb();
  free(sectname);
}

