/*
 * getdb.c  -  Read system definitions from database file
 *
 * Copyright (C) 1998-2003 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.6 2003/03/09 00:43:08 gkminix Exp $
 */

#include <common.h>
#include <nblib.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 */




/*
 * Table containing option names for "ldoptsX" parameters.
 */
static 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}},
	{ "cardinst",	TRUE, 0, 0,
			{&bootd.loaders[0].cardinst,
			 &bootd.loaders[1].cardinst,
			 &bootd.loaders[2].cardinst}},
	{ "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[0].asktime,
			 &bootd.loaders[0].asktime}},
	{NULL,		FALSE, 0, 0, {NULL, NULL, NULL}}
};


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

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

  /* Find each option in the list */
  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++;
	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 */
		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 (!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 {
		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 char *dtypes[] = {
  "packet driver", "ndis driver", "undi driver", NULL
};



/* Available bus types */
static char *btypes[] = {
  "ISA", "EISA", "MCA", "PCI", NULL
};



/* Available types for output file */
static char *ftypes[] = {
  "binary",
  "intel hex",
  "motorola hex",
  "tektronix hex",
  "flashcard",
  "flash",
  NULL
};



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

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

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

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

  /* Parameters for packet driver interface */
  { "pktdrvidx",   par_int,     NULL,   {(char **)&bootd.netdrv.driverdefs.pd.drvindex}},
  { "pdprog0",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[0]}},
  { "pdprog1",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[1]}},
  { "pdprog2",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[2]}},
  { "pdprog3",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[3]}},
  { "pdprog4",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[4]}},
  { "pdprog5",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[5]}},
  { "pdprog6",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[6]}},
  { "pdprog7",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.prognames[7]}},
  { "pdargs0",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[0]}},
  { "pdargs1",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[1]}},
  { "pdargs2",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[2]}},
  { "pdargs3",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[3]}},
  { "pdargs4",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[4]}},
  { "pdargs5",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[5]}},
  { "pdargs6",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[6]}},
  { "pdargs7",     par_string,	NULL,	{&bootd.netdrv.driverdefs.pd.progs.progargs[7]}},
  { "pdminsize0",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[0]}},
  { "pdminsize1",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[1]}},
  { "pdminsize2",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[2]}},
  { "pdminsize3",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[3]}},
  { "pdminsize4",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[4]}},
  { "pdminsize5",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[5]}},
  { "pdminsize6",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[6]}},
  { "pdminsize7",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.minsizes[7]}},
  { "pdmaxsize0",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[0]}},
  { "pdmaxsize1",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[1]}},
  { "pdmaxsize2",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[2]}},
  { "pdmaxsize3",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[3]}},
  { "pdmaxsize4",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[4]}},
  { "pdmaxsize5",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[5]}},
  { "pdmaxsize6",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[6]}},
  { "pdmaxsize7",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.pd.progs.maxsizes[7]}},

  /* Parameters for NDIS interface */
  { "protini",     par_string,	NULL,	{&protini}},
  { "ndisdrvidx",  par_int,     NULL,   {(char **)&bootd.netdrv.driverdefs.ndis.drvindex}},
  { "ndprog0",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[0]}},
  { "ndprog1",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[1]}},
  { "ndprog2",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[2]}},
  { "ndprog3",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[3]}},
  { "ndprog4",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[4]}},
  { "ndprog5",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[5]}},
  { "ndprog6",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[6]}},
  { "ndprog7",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.prognames[7]}},
  { "ndargs0",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[0]}},
  { "ndargs1",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[1]}},
  { "ndargs2",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[2]}},
  { "ndargs3",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[3]}},
  { "ndargs4",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[4]}},
  { "ndargs5",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[5]}},
  { "ndargs6",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[6]}},
  { "ndargs7",     par_string,	NULL,	{&bootd.netdrv.driverdefs.ndis.progs.progargs[7]}},
  { "ndminsize0",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[0]}},
  { "ndminsize1",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[1]}},
  { "ndminsize2",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[2]}},
  { "ndminsize3",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[3]}},
  { "ndminsize4",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[4]}},
  { "ndminsize5",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[5]}},
  { "ndminsize6",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[6]}},
  { "ndminsize7",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.minsizes[7]}},
  { "ndmaxsize0",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[0]}},
  { "ndmaxsize1",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[1]}},
  { "ndmaxsize2",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[2]}},
  { "ndmaxsize3",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[3]}},
  { "ndmaxsize4",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[4]}},
  { "ndmaxsize5",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[5]}},
  { "ndmaxsize6",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[6]}},
  { "ndmaxsize7",  par_long,	NULL,	{(char **)&bootd.netdrv.driverdefs.ndis.progs.maxsizes[7]}},

  /* Parameters for UNDI interface */
  { "undiprog",    par_string,	NULL,	{&bootd.netdrv.driverdefs.undi.name}},
  { NULL,          par_null,	NULL,	{NULL}}
};



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

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

  if (*drvindex < 0 || *drvindex >= pdp->prognum) {
	prnerr2("network driver index %d invalid in section <%s>",
							*drvindex, namebuf);
	exit(EXIT_DB);
  }

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



/*
 * Read one entry from the database file
 */
struct bootdef *getdb(name)
char *name;
{
  struct sectdef sect;
  char *namebuf, *cp;
  int i, j;

  /* Generate section record */
  namebuf = (char *)nbmalloc(strlen(name) + 9);
  sprintf(namebuf, "%s:makerom", name);
  sect.name = namebuf;
  sect.params = dbparams;
  sect.startsect = NULL;
  sect.endsect = NULL;

  /* Get entry from database */
  memset(&bootd, 0, sizeof(bootd));
  for (i = 0; i < MAXPROGS; i++) {
	bootd.netdrv.driverdefs.pd.progs.minsizes[i] = -1L;
	bootd.netdrv.driverdefs.pd.progs.maxsizes[i] = -1L;
	bootd.netdrv.driverdefs.ndis.progs.minsizes[i] = -1L;
	bootd.netdrv.driverdefs.ndis.progs.maxsizes[i] = -1L;
  }
  readdb(&sect, dbname);

  /* Check for correct and missing values */
  copystr(&bootd.name, name);
  checkaccess(&bootd.kernelname, config.bindir);
  if (!bootd.kernelname) {
	prnerr1("no or invalid kernel file name given in section <%s>",
								namebuf);
	exit(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) {
		prnerr1("missing PCI vendor/device ID in section <%s>", namebuf);
		exit(EXIT_DB);
	}
	if (bootd.pnp_devid != NULL) {
		prnerr1("PnP device ID for PCI network card in section <%s>",
								namebuf);
		exit(EXIT_DB);
	}
  } else if (bootd.pci_vendid != 0 || bootd.pci_devid != 0) {
	prnerr1("PCI vendor/device ID specified for non-PCI device in section <%s>",
								namebuf);
	exit(EXIT_DB);
  }
  if (bootd.pnp_devid != NULL) {
	if (strlen(bootd.pnp_devid) != PNPIDLEN ||
	    !isalpha(bootd.pnp_devid[0]) ||
	    !isalpha(bootd.pnp_devid[1]) ||
	    !isalpha(bootd.pnp_devid[2]) ||
	    !isxdigit(bootd.pnp_devid[3]) ||
	    !isxdigit(bootd.pnp_devid[4]) ||
	    !isxdigit(bootd.pnp_devid[5]) ||
	    !isxdigit(bootd.pnp_devid[6])) {
		prnerr1("invalid PnP device ID in section <%s>", namebuf);
		exit(EXIT_DB);
	}
  }
  bootd.canflash = checkflash(&bootd);
  checkaccess(&bootd.netdrv.name, config.bindir);
  if (!bootd.netdrv.name) {
	prnerr1("no or invalid network driver name given in section <%s>",
								namebuf);
	exit(EXIT_DB);
  }
  if (bootd.netdrv.drivertype == DRVTYPE_NONE) {
	prnerr1("missing driver type in section <%s>", namebuf);
	exit(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);
		if (bootd.loaders[i].name == NULL) {
			prnerr2("loader %d invalid in section <%s>",
								i, namebuf);
			exit(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) {
			prnerr2("no output file given for loader %d in section <%s>",
								i, namebuf);
			exit(EXIT_DB);
		}
		if (bootd.loaders[i].outtype == OUT_NONE)
			bootd.loaders[i].outtype = OUT_BINARY;
		if (bootd.loaders[i].outtype == OUT_FLASH && !bootd.canflash) {
			prnerr2("flash output type unsupported for loader %d in section <%s>",
								i, namebuf);
			exit(EXIT_DB);
		}
		bootd.loadernum++;
		if (j >= 0) {
			bootd.loaders[j] = bootd.loaders[i];
			bootd.loaders[i].name = NULL;
			i = j;
			j = -1;
		}
	}
  }
  if (bootd.loadernum == 0) {
	prnerr1("no bootrom loader specified in section <%s>", namebuf);
	exit(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(&(pdp->progs), &(pdp->drvindex), namebuf);
	if (pdp->progs.prognum == 0) {
		prnerr1("no or invalid packet driver specified in section <%s>",
								namebuf);
		exit(EXIT_DB);
	}
  } else if (bootd.netdrv.drivertype == DRVTYPE_NDIS) {
	struct ndisdef *np = &(bootd.netdrv.driverdefs.ndis);

	checkprogs(&(np->progs), &(np->drvindex), namebuf);
	if (np->progs.prognum == 0) {
		prnerr1("no or invalid NDIS driver specified in section <%s>",
								namebuf);
		exit(EXIT_DB);
	}

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

	if (protini == NULL) {
		prnerr1("missing \'protini\' entry in section <%s>\n",
								namebuf);
		exit(EXIT_DB);
	}
	if (!strncmp(protini, "file:", 5)) {
		cp = readprotini(&protini[5]);
		parseprotini(cp, &(np->protocolini), &(np->protinisize));
		free(cp);
	} else
		parseprotini(protini, &(np->protocolini), &(np->protinisize));
  } else if (bootd.netdrv.drivertype == DRVTYPE_UNDI) {
	checkaccess(&bootd.netdrv.driverdefs.undi.name, config.netdrvdir);
	if (bootd.netdrv.driverdefs.undi.name == NULL) {
		prnerr1("missing or invalid UNDI driver name in section <%s>",
								namebuf);
		exit(EXIT_DB);
	}
  }
  free(namebuf);
  return(&bootd);
}



syntax highlighted by Code2HTML, v. 0.9.1