/*
 * config.c  -  Read configuration file for makerom
 *
 * 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: doconfig.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 "md5.h"



/*
 * Maximum execution size
 */
#define MAX_EXEC_SIZE	(128L * 1024L)



/*
 * Default path and file name definitions
 */
#define DRIVERFILE	"//netboot.drivers"
#define MD5SUMFILE	"//netdrvr/MD5SUMS"
#define BINDIR		"//binaries"
#define NETDRVDIR	"//netdrvr"
#define UTILSDIR	"//utils://binaries"

#define KERNEL32	"kernel.bin"
#define KERNEL16	"kernel86.bin"
#define KERNELM		"kernelm.bin"
#define ROM32		"rom.bin"
#define ROM16		"rom86.bin"
#define FLOPPY32	"floppy.bin"
#define FLOPPY16	"floppy86.bin"

#define PKTDRV32	"netpkt.bin"
#define PKTDRV16	"netpkt86.bin"
#define NDIS32		"netndis.bin"
#define NDIS16		"netndis86.bin"
#define UNDI32		"netundi.bin"
#define UNDI16		"netundi86.bin"



/*
 * Global variables
 */
struct confstruct config;		/* configuration information */



/*
 * Local variables
 */
static char *tmpdrvfname = NULL;	/* temporary drivers file name */
static char *tmpmd5fname = NULL;	/* temporary MD5 sums file name */



/*
 * Definition of makerom:general section in configuration file
 */
static struct paramdef general_params[] = {
  { "driverfile",	par_string,	NULL,	{&tmpdrvfname}},
  { "md5sumfile",	par_string,	NULL,	{&tmpmd5fname}},
  { "bindir",		par_string,	NULL,	{&config.bindir}},
  { "netdrvdir",	par_string,	NULL,	{&config.netdrvdir}},
  { "utilsdir",		par_string,	NULL,	{&config.utilsdir}},
  { "kernel32",		par_string,	NULL,	{&config.files[FILE_KERNEL].filename32}},
  { "kernel16",		par_string,	NULL,	{&config.files[FILE_KERNEL].filename16}},
  { "kernelm32",	par_string,	NULL,	{&config.files[FILE_KERNELM].filename32}},
  { "kernelm16",	par_string,	NULL,	{&config.files[FILE_KERNELM].filename16}},
  { "rom32",		par_string,	NULL,	{&config.files[FILE_ROM].filename32}},
  { "rom16",		par_string,	NULL,	{&config.files[FILE_ROM].filename16}},
  { "floppy32",		par_string,	NULL,	{&config.files[FILE_FLOPPY].filename32}},
  { "floppy16",		par_string,	NULL,	{&config.files[FILE_FLOPPY].filename16}},
  { NULL,		par_null,	NULL,	{NULL}}
};



/*
 * Assign a new string to a string pointer
 */
static void assignstr(old, new)
char **old;
char  *new;
{
  if (new != NULL) {
	if (*old != NULL)
		free(*old);
	*old = new;
  }
}



/*
 * Read network driver interface information from configuration file
 */
static char *dsections[] = {
  "pktdrv", "ndis", "undi"
};

static struct {
	char *filename32;
	char *filename16;
	char *description;
	char *directory;
	char *pattern;
} netdrv_vars;

static struct paramdef netdrv_params[] = {
  { "filename32",	par_string,	NULL,	{&netdrv_vars.filename32}},
  { "filename16",	par_string,	NULL,	{&netdrv_vars.filename16}},
  { "description",	par_string,	NULL,	{&netdrv_vars.description}},
  { "directory",	par_string,	NULL,	{&netdrv_vars.directory}},
  { "pattern",		par_string,	NULL,	{&netdrv_vars.pattern}},
  { NULL,		par_null,	NULL,	{NULL}}
};

static char *handle_netdrv_sect(sectname, cursect)
char *sectname;
struct sectdef *cursect;
{
  struct netdrvinfo *ip;
  int drvtype;
  char *cp;

  /* Determine name of section, e.g. driver interface type */
  assert((cp = strrchr(sectname, ':')) != NULL);
  cp++;
  for (drvtype = DRVTYPE_MIN; drvtype <= DRVTYPE_MAX; drvtype++)
	if (!strcmp(cp, dsections[drvtype - DRVTYPE_MIN]))
		break;
  if (drvtype > DRVTYPE_MAX) {
	if (netdrv_vars.description != NULL)
		free(netdrv_vars.description);
	if (netdrv_vars.directory != NULL)
		free(netdrv_vars.directory);
	if (netdrv_vars.pattern != NULL)
		free(netdrv_vars.pattern);
	if (netdrv_vars.filename32 != NULL)
		free(netdrv_vars.filename32);
	if (netdrv_vars.filename16 != NULL)
		free(netdrv_vars.filename16);
	memset(&netdrv_vars, 0, sizeof(netdrv_vars));
	return("invalid network driver interface section");
  }

  /* Add new item to info list */
  ip = &config.netdrv[drvtype];
  assignstr(&(ip->descript), netdrv_vars.description);
  assignstr(&(ip->searchdir), netdrv_vars.directory);
  assignstr(&(ip->patlist), netdrv_vars.pattern);
  assignstr(&(ip->filename32), netdrv_vars.filename32);
  assignstr(&(ip->filename16), netdrv_vars.filename16);

  /* Clear out temporary space for next round */
  memset(&netdrv_vars, 0, sizeof(netdrv_vars));
  return(NULL);
}



/*
 * List of recognized options for network driver binary files
 */
static struct {
	char *optname;
	int   optval;
} netoptlist[] = {
	{ "HW_IRQ",	HW_IRQ },
	{ "IO_ADDR",	IO_ADDR },
	{ "BASE_MEM",	BASE_MEM },
	{ "AUI_TYPE",	AUI_TYPE },
	{ "DMA_NUM",	DMA_NUM },
	{ NULL,		0 }
};

static int getoption(optstr)
char *optstr;
{
  char *cp;
  int i, len, optval = 0;

  cp = optstr;
  while (cp != NULL) {
	cp += strspn(cp, " \t");
	len = strcspn(cp, " \t,");
	if (len > 0) {
		for (i = 0; netoptlist[i].optname != NULL; i++)
			if (strlen(netoptlist[i].optname) == len &&
			    !strncmp(netoptlist[i].optname, cp, len))
				break;
		if (netoptlist[i].optname == NULL)
			return(-1);
		optval |= netoptlist[i].optval;
	}
	if ((cp = strchr(cp, ',')) != NULL)
		cp++;
  }
  return(optval);
}



/*
 * Find a network driver description in the configuration file structure. If
 * none is found, a new entry is generated and inserted into the description
 * list.
 */
static struct filedesc *getfiledesc(sectname)
char *sectname;
{
  struct filedesc *fp = config.drvdesc;
  char *cp;

  /* Determine driver name from section name */
  assert((cp = strchr(sectname, ':')) != NULL);
  if (!*(++cp))
	return(NULL);

  /* Find driver description in list */
  while (fp != NULL) {
	if (!strcmp(fp->name, cp))
		break;
	fp = fp->next;
  }

  /* Create new description record if not found */
  if (fp == NULL) {
	fp = (struct filedesc *)nbmalloc(sizeof(struct filedesc));
	copystr(&(fp->name), cp);
	fp->next = config.drvdesc;
	config.drvdesc = fp;
  }
  return(fp);
}



/*
 * Eliminate beginning and ending quotes from a string
 */
static char *delquotes(str)
char *str;
{
  int i;

  assert(str != NULL);
  if (str[0] == '"') {
	i = strlen(str);
	if (str[i - 1] != '"')
		return(NULL);
	str[i - 1] = '\0';
	str++;
  }
  return(str);
}



/*
 * Read description string from config file. This routine gets called
 * everytime a "description" parameter is found in the config file.
 */
static struct descstr *desclist = NULL;

static char *readdesc(name, arg)
char *name;
char *arg;
{
  struct descstr *dp;

  /* Eliminate quotes from argument */
  if ((arg = delquotes(arg)) == NULL)
	return("missing end quote");

  /* Add new item to description string list */
  dp = (struct descstr *)nbmalloc(sizeof(struct descstr));
  copystr(&(dp->descript), arg);
  dp->next = desclist;
  desclist = dp;
  return(NULL);
}



/*
 * Delete a description string list
 */
static void deldesc(sp)
struct descstr *sp;
{
  struct descstr *spt;

  while (sp != NULL) {
	if (sp->descript != NULL)
		free(sp->descript);
	spt = sp;
	sp = sp->next;
	free(spt);
  }
}



/*
 * Read network card information from configuration file
 */
static char *btypes[] = {
  "ISA", "EISA", "MCA", "PCI", NULL
};

static struct {
	char *usedriver;
	char *pnp_devid;
	int   pci_vendid;
	int   pci_devid;
	int   bustype;
} card_vars;

static struct paramdef card_params[] = {
  { "description",	par_proc,	NULL,	{(char **)&readdesc}},
  { "bustype",		par_enum,	btypes,	{(char **)&card_vars.bustype}},
  { "pcivendor",	par_int,	NULL,	{(char **)&card_vars.pci_vendid}},
  { "pcidevice",	par_int,	NULL,	{(char **)&card_vars.pci_devid}},
  { "pnpdevid",		par_string,	NULL,	{&card_vars.pnp_devid}},
  { "usedriver",	par_string,	NULL,	{&card_vars.usedriver}},
  { NULL,		par_null,	NULL,	{NULL}}
};

static char *handle_card_sect(sectname, cursect)
char *sectname;
struct sectdef *cursect;
{
  struct filedesc *fp;
  struct descstr *sp;
  char *cp = NULL;

  /* Analyze section name and create new item in description list */
  if ((fp = getfiledesc(sectname)) == NULL)
	cp = "invalid section name";
  else {
	/* Check that the bus type doesn't change from a previous definition */
	if (card_vars.bustype != BUSTYPE_NONE) {
		if (fp->bustype != BUSTYPE_NONE &&
		    fp->bustype != card_vars.bustype) {
			if (cp == NULL)
				cp = "bus type already defined differently";
		} else
			fp->bustype = card_vars.bustype;
	}

	/* Check that a PCI vendor/device ID doesn't change */
	if (card_vars.pci_vendid != 0) {
		if (fp->pci_vendid != 0 &&
		    fp->pci_vendid != card_vars.pci_vendid) {
			if (cp == NULL)
				cp = "PCI vendor ID already defined differently";
		} else
			fp->pci_vendid = card_vars.pci_vendid;
	}
	if (card_vars.pci_devid != 0) {
		if (fp->pci_devid != 0 &&
		    fp->pci_devid != card_vars.pci_devid) {
			if (cp == NULL)
				cp = "PCI device ID already defined differently";
		} else
			fp->pci_devid  = card_vars.pci_devid;
	}

	/* Check that the PnP device ID is correct and doesn't change */
	if (card_vars.pnp_devid != NULL) {
		if (fp->pnp_devid != NULL &&
		    strcmp(card_vars.pnp_devid, fp->pnp_devid)) {
			if (cp == NULL)
				cp = "PnP device ID already defined differently";
		} else if (strlen(card_vars.pnp_devid) > 0 &&
		           (strlen(card_vars.pnp_devid) != PNPIDLEN ||
		            !isalpha(card_vars.pnp_devid[0]) ||
		            !isalpha(card_vars.pnp_devid[1]) ||
		            !isalpha(card_vars.pnp_devid[2]) ||
		            !isxdigit(card_vars.pnp_devid[3]) ||
		            !isxdigit(card_vars.pnp_devid[4]) ||
		            !isxdigit(card_vars.pnp_devid[5]) ||
		            !isxdigit(card_vars.pnp_devid[6]))) {
			if (cp == NULL)
				cp = "invalid PnP device ID string";
		} else {
			if (strlen(card_vars.pnp_devid) == 0) {
				free(card_vars.pnp_devid);
				card_vars.pnp_devid = NULL;
				if (fp->pnp_devid != NULL)
					free(fp->pnp_devid);
				fp->pnp_devid = NULL;
			} else {
				assignstr(&(fp->pnp_devid), card_vars.pnp_devid);
				card_vars.pnp_devid = NULL;
			}
		}
	}

	/* Check that the bus type is correct */
	if (cp == NULL && fp->bustype != BUSTYPE_PCI &&
	      (fp->pci_vendid != 0 || fp->pci_devid != 0))
		cp = "PCI vendor/device ID specified for non-PCI bus type";
	if (cp == NULL && fp->bustype == BUSTYPE_PCI &&
	      fp->pnp_devid != NULL)
		cp = "PnP device ID string defined for PCI bus type";
	if (cp == NULL && fp->pnp_devid != NULL &&
	      (fp->pci_vendid != 0 || fp->pci_devid != 0))
		cp = "PCI vendor/device ID and PnP device ID defined";

	/* Add driver cross-reference */
	assignstr(&(fp->usedriver), card_vars.usedriver);
	card_vars.usedriver = NULL;

	/* Add list of descriptions to item */
	if ((sp = desclist) != NULL) {
		while (sp->next != NULL)
			sp = sp->next;
		sp->next = fp->descript;
		fp->descript = desclist;
	}
	desclist = NULL;
  }

  /* Clear all dynamic memory which hasn't been saved so far */
  deldesc(desclist);
  desclist = NULL;
  if (card_vars.pnp_devid != NULL)
	free(card_vars.pnp_devid);
  if (card_vars.usedriver != NULL)
	free(card_vars.usedriver);
  memset(&card_vars, 0, sizeof(card_vars));
  return(cp);
}



/*
 * Read network driver information from configuration file
 */
static char *dtypes[] = {
  "packet driver", "ndis driver", "undi driver", NULL
};

static struct {
	char *filename;
	char *parameters;
	char *pd_cmdline;
	char *ndis_protini;
	char *drvdesc;
	char *md5sum;
	int   drvtype;
	long  minsize;
	long  maxsize;
} driver_vars;

static struct paramdef driver_params[] = {
  { "driverdesc",	par_string,	NULL,	{&driver_vars.drvdesc}},
  { "file",		par_string,	NULL,	{&driver_vars.filename}},
  { "params",		par_string,	NULL,	{&driver_vars.parameters}},
  { "cmdline",		par_string,	NULL,	{&driver_vars.pd_cmdline}},
  { "protini",		par_string,	NULL,	{&driver_vars.ndis_protini}},
  { "md5sum",		par_string,	NULL,	{&driver_vars.md5sum}},
  { "drvtype",		par_enum,	dtypes,	{(char **)&driver_vars.drvtype}},
  { "minsize",		par_long,	NULL,	{(char **)&driver_vars.minsize}},
  { "maxsize",		par_long,	NULL,	{(char **)&driver_vars.maxsize}},
  { NULL,		par_null,	NULL,	{NULL}}
};

static char *handle_driver_sect(sectname, cursect)
char *sectname;
struct sectdef *cursect;
{
  struct filedesc *fp;
  struct drvdesc *dp;
  char *drvdesc = NULL;
  char *cp = NULL;
  char *mdp;
  int opt = -1;

  /* Analyze section name and create new item in description list */
  if ((fp = getfiledesc(sectname)) == NULL)
	cp = "invalid section name";
  else {
	/* Check that parameter combinations are valid */
	switch (driver_vars.drvtype) {
		case DRVTYPE_NONE:
				if (driver_vars.filename != NULL ||
				    driver_vars.parameters != NULL ||
				    driver_vars.minsize != -1L ||
				    driver_vars.maxsize != -1L ||
				    driver_vars.pd_cmdline != NULL ||
				    driver_vars.ndis_protini != NULL ||
				    driver_vars.md5sum != NULL ||
				    driver_vars.drvdesc != NULL)
					cp = "missing driver type parameter";
				break;
		case DRVTYPE_PD:
				if (driver_vars.ndis_protini != NULL)
					cp = "bogus \'protini\' parameter in packet driver definition";
				break;
		case DRVTYPE_NDIS:
				if (driver_vars.pd_cmdline != NULL)
					cp = "bogus \'cmdline\' parameter in NDIS driver definition";
				break;
		case DRVTYPE_UNDI:
				if (driver_vars.parameters != NULL)
					cp = "bogus \'params\' parameter in UNDI driver definition";
				else if (driver_vars.ndis_protini != NULL)
					cp = "bogus \'protini\' parameter in UNDI driver definition";
				else if (driver_vars.pd_cmdline != NULL)
					cp = "bogus \'cmdline\' parameter in UNDI driver definition";
				else if (driver_vars.minsize != -1L ||
				         driver_vars.maxsize != -1L)
					cp = "bogus \'minsize\' or \'maxsize\' parameter in UNDI driver definition";
				break;
		default:
				assert(driver_vars.drvtype >= DRVTYPE_MIN &&
				       driver_vars.drvtype <= DRVTYPE_MAX);
				break;
	}

	/* Analyze options */
	if (driver_vars.parameters != NULL) {
		if ((opt = getoption(driver_vars.parameters)) < 0 && cp == NULL)
			cp = "invalid \'params\' parameter";
		free(driver_vars.parameters);
		driver_vars.parameters = NULL;
	}

	/* Analyze execution sizes */
	if (driver_vars.minsize < -2L)
		driver_vars.minsize = -2L;
	if (driver_vars.maxsize < -2L)
		driver_vars.maxsize = -2L;
	if (cp == NULL &&
	    ((driver_vars.minsize > 0L &&
	      driver_vars.maxsize > 0L &&
	      driver_vars.minsize > driver_vars.maxsize) ||
	     driver_vars.minsize > MAX_EXEC_SIZE ||
	     driver_vars.maxsize > MAX_EXEC_SIZE))
		cp = "invalid execution size values";

	/* Check for correct driver description string */
	if (driver_vars.drvdesc != NULL &&
 	    (drvdesc = delquotes(driver_vars.drvdesc)) == NULL) {
		if (cp == NULL)
			cp = "missing end quotes in network driver description string";
	}

	/* Check for correct MD5 sum string, and convert it to lower case */
	if (driver_vars.md5sum != NULL) {
		for (mdp = driver_vars.md5sum; *mdp; mdp++) {
			*mdp = tolower(*mdp);
			if ((*mdp < '0' || *mdp > '9') &&
			    (*mdp < 'a' || *mdp > 'f'))
				break;
		}
		if (cp == NULL && (mdp - driver_vars.md5sum) != MD5_SUM_LENGTH)
			cp = "invalid MD5 checksum string";
	}

	/* Add new driver description record */
	if (fp->usedriver != NULL) {
		if (cp == NULL)
			cp = "alternative driver list already defined";
	} else if (driver_vars.drvtype != DRVTYPE_NONE) {
		dp = (struct drvdesc *)nbmalloc(sizeof(struct drvdesc));
		dp->type = driver_vars.drvtype;
		switch (driver_vars.drvtype) {
			case DRVTYPE_PD:
				if (opt >= 0)
					dp->drv.pd.options = opt;
				dp->drv.pd.minsize = driver_vars.minsize;
				dp->drv.pd.maxsize = driver_vars.maxsize;
				assignstr(&(dp->drv.pd.cmdline), driver_vars.pd_cmdline);
				driver_vars.pd_cmdline = NULL;
				break;
			case DRVTYPE_NDIS:
				if (opt >= 0)
					dp->drv.ndis.options = opt;
				dp->drv.ndis.minsize = driver_vars.minsize;
				dp->drv.ndis.maxsize = driver_vars.maxsize;
				assignstr(&(dp->drv.ndis.protini), driver_vars.ndis_protini);
				driver_vars.ndis_protini = NULL;
				break;
			case DRVTYPE_UNDI:
				break;
		}
		if (driver_vars.drvdesc != NULL) {
			if (drvdesc != NULL)
				copystr(&(dp->descript), drvdesc);
			free(driver_vars.drvdesc);
			driver_vars.drvdesc = NULL;
		}
		assignstr(&(dp->filename), driver_vars.filename);
		assignstr(&(dp->md5sum), driver_vars.md5sum);
		driver_vars.filename = NULL;
		driver_vars.md5sum = NULL;
		dp->next = fp->drvlist;
		fp->drvlist = dp;
	}
  }

  /* Clear all dynamic memory before returning */
  if (driver_vars.filename != NULL)
	free(driver_vars.filename);
  if (driver_vars.parameters != NULL)
	free(driver_vars.parameters);
  if (driver_vars.pd_cmdline != NULL)
	free(driver_vars.pd_cmdline);
  if (driver_vars.ndis_protini != NULL)
	free(driver_vars.ndis_protini);
  if (driver_vars.md5sum != NULL)
	free(driver_vars.md5sum);
  if (driver_vars.drvdesc != NULL)
	free(driver_vars.drvdesc);
  memset(&driver_vars, 0, sizeof(driver_vars));
  driver_vars.minsize = -1L;
  driver_vars.maxsize = -1L;
  return(cp);
}



/*
 * List of sections to read from configuration files at startup
 */
static struct sectdef confsects[] = {
	{ "makerom:general",	general_params,	NULL,	NULL },
	{ NULL,			NULL,		NULL,	NULL}
};

static struct sectdef driversects[] = {
	{ "netdrv:*",		netdrv_params,	NULL,	&handle_netdrv_sect },
	{ "card:*",		card_params,	NULL,	&handle_card_sect },
	{ "driver:*",		driver_params,	NULL,	&handle_driver_sect },
	{ NULL,			NULL,		NULL,	NULL}
};



/*
 * Set configuration structure with default values
 */
static struct {
	char **cnfptr;
	char  *defstr;
} filedefaults[] = {
	{ &config.bindir,				BINDIR },
	{ &config.netdrvdir,				NETDRVDIR },
	{ &config.utilsdir,				UTILSDIR },
	{ &config.files[FILE_KERNEL].filename32,	KERNEL32 },
	{ &config.files[FILE_KERNEL].filename16,	KERNEL16 },
	{ &config.files[FILE_KERNELM].filename32,	KERNELM },
	{ &config.files[FILE_ROM].filename32,		ROM32 },
	{ &config.files[FILE_ROM].filename16,		ROM16 },
	{ &config.files[FILE_FLOPPY].filename32,	FLOPPY32 },
	{ &config.files[FILE_FLOPPY].filename16,	FLOPPY16 },
	{ &config.netdrv[DRVTYPE_PD].filename32,	PKTDRV32 },
	{ &config.netdrv[DRVTYPE_PD].filename16,	PKTDRV16 },
	{ &config.netdrv[DRVTYPE_NDIS].filename32,	NDIS32 },
	{ &config.netdrv[DRVTYPE_NDIS].filename16,	NDIS16 },
	{ &config.netdrv[DRVTYPE_UNDI].filename32,	UNDI32 },
	{ &config.netdrv[DRVTYPE_UNDI].filename16,	UNDI16 },
	{ NULL,						NULL }
};

static char *netdescstr[] = {
	"Packet Driver interface (PD)",
	"Network Driver Interface Specification (NDIS)",
	"Universal Network Driver Interface (UNDI)"
};

static char *netpathstr[] = {
	"pktdrvr:pktdrvr/drivers",
	"ndis2:ndis2/drivers",
	"undi:undi/drivers"
};

static char *patternstr[] = {
	"*.com:*.exe",
	"*.sys:*.dos",
	"*.undi"
};

static void setdefaults()
{
  int i;

  /* Clearout configuration structure */
  memset(&config, 0, sizeof(config));

  /* Copy default strings into structure */
  for (i = 0; filedefaults[i].defstr != NULL; i++)
	copystr(filedefaults[i].cnfptr, filedefaults[i].defstr);

  /* Set defaults for network driver interface definitions */
  for (i = DRVTYPE_MIN; i <= DRVTYPE_MAX; i++) {
	copystr(&config.netdrv[i].descript, netdescstr[i - DRVTYPE_MIN]);
	copystr(&config.netdrv[i].searchdir, netpathstr[i - DRVTYPE_MIN]);
	copystr(&config.netdrv[i].patlist, patternstr[i - DRVTYPE_MIN]);
  }
}



/*
 * Normalize directory and file names and cleanup the network driver
 * description list.
 */
static void donormalize()
{
  struct filedesc *fp;
  struct drvdesc *dp, *olddp, *dpt;
  char *sdir;
  int i;

  /* Normalize main directory names */
  setpath(&config.bindir, NULL);
  setpath(&config.netdrvdir, NULL);
  setpath(&config.utilsdir, NULL);

  /* Normalize binary file names */
  for (i = 0; i < FILE_NUMBER; i++) {
	checkaccess(&config.files[i].filename32, config.bindir);
	checkaccess(&config.files[i].filename16, config.bindir);
  }
  for (i = DRVTYPE_MIN; i <= DRVTYPE_MAX; i++) {
	setpath(&config.netdrv[i].searchdir, config.netdrvdir);
	checkaccess(&config.netdrv[i].filename32, config.bindir);
	checkaccess(&config.netdrv[i].filename16, config.bindir);
  }

  /* Scan through all network cards and process their drivers */
  fp = config.drvdesc;
  while (fp != NULL) {
	/* Normalize network driver file names and check MD5 checksums */
	dp = fp->drvlist;
	while (dp != NULL) {
		assert(dp->type >= DRVTYPE_MIN &&
		       dp->type <= DRVTYPE_MAX);
		sdir = NULL;
		switch (dp->type) {
			case DRVTYPE_PD:
				sdir = config.netdrv[DRVTYPE_PD].searchdir;
				break;
			case DRVTYPE_NDIS:
				sdir = config.netdrv[DRVTYPE_NDIS].searchdir;
				break;
			case DRVTYPE_UNDI:
				sdir = config.netdrv[DRVTYPE_UNDI].searchdir;
				break;
		}
		checkaccess(&(dp->filename), sdir);
		checkmd5sum(&(dp->filename), dp->md5sum);
		dp = dp->next;
	}

	/*
	 * Remove all drivers with the same file name but without an MD5
	 * checksum, if at least one driver has a checksum. We have to do
	 * this in a seperate loop because all filenames have to be
	 * normalized first.
	 */
	dp = fp->drvlist;
	while (dp != NULL) {
		if (dp->filename != NULL && dp->md5sum != NULL) {
			dpt = fp->drvlist;
			while (dpt != NULL) {
				if (dpt != dp &&
				    dpt->md5sum == NULL &&
				    dpt->filename != NULL &&
				    !strcmp(dpt->filename, dp->filename)) {
					free(dpt->filename);
					dpt->filename = NULL;
				}
				dpt = dpt->next;
			}
		}
		dp = dp->next;
	}

	/* Cleanup driver list by deleting all drivers with empty filename */
	dp = fp->drvlist;
	olddp = NULL;
	while (dp != NULL) {
		if (dp->filename == NULL) {
			/* Delete item from list */
			switch (dp->type) {
				case DRVTYPE_PD:
					if (dp->drv.pd.cmdline != NULL)
						free(dp->drv.pd.cmdline);
					break;
				case DRVTYPE_NDIS:
					if (dp->drv.ndis.protini != NULL)
						free(dp->drv.ndis.protini);
					break;
				case DRVTYPE_UNDI:
					break;
			}
			if (dp->md5sum != NULL)
				free(dp->md5sum);
			if (dp->descript != NULL)
				free(dp->descript);
			if (olddp == NULL)
				fp->drvlist = dp->next;
			else
				olddp->next = dp->next;
			dpt = dp;
			dp = dp->next;
			free(dpt);
		} else {
			/* Continue with next item */
			olddp = dp;
			dp = dp->next;
		}
	}
	fp = fp->next;
  }
}



/*
 * Resolve cross-reference driver lists and cleanup network card list
 */
static void doresolve()
{
  struct filedesc *fp, *oldfp, *fpt;
  char *cp;

  fp = config.drvdesc;
  oldfp = NULL;
  while (fp != NULL) {
	fpt = NULL;
	if (fp->drvlist == NULL && (cp = fp->usedriver) != NULL) {
		fpt = config.drvdesc;
		while (fpt != NULL) {
			if (fpt != fp && !strcmp(fpt->name, cp))
				break;
			fpt = fpt->next;
		}
	}
	if (fpt != NULL)
		fp->drvlist = fpt->drvlist;
	if (fp->drvlist == NULL) {
		/* Delete item from list */
		deldesc(fp->descript);
		if (fp->name != NULL)
			free(fp->name);
		if (fp->pnp_devid != NULL)
			free(fp->pnp_devid);
		if (fp->usedriver != NULL)
			free(fp->usedriver);
		if (oldfp == NULL)
			config.drvdesc = fp->next;
		else
			oldfp->next = fp->next;
		fpt = fp;
		fp = fp->next;
		free(fpt);
	} else {
		oldfp = fp;
		fp = fp->next;
	}
  }
}



/*
 * Parse options and read configuration file
 */
void doconfig(argc, argv, opts)
int argc;
char **argv;
struct cmdopt *opts;
{
  /* Clear config file read structures */
  memset(&netdrv_vars, 0, sizeof(netdrv_vars));
  memset(&card_vars, 0, sizeof(card_vars));
  memset(&driver_vars, 0, sizeof(driver_vars));
  driver_vars.minsize = -1L;
  driver_vars.maxsize = -1L;

  /* Set defaults */
  setdefaults();

  /* Parse options and read configuration file */
  nbsetup(argc, argv, opts, confsects);

  /* Let the command line file name override the one from the config file */
  if (tmpmd5fname != NULL) {
	if (config.md5fname == NULL)
		config.md5fname = tmpmd5fname;
	else
		free(tmpmd5fname);
	tmpmd5fname = NULL;
  } else if (config.md5fname == NULL)
	copystr(&config.md5fname, MD5SUMFILE);

  /* Normalize name of MD5 sums file and read it */
  checkaccess(&config.md5fname, NULL);
  if (config.md5fname != NULL)
	readmd5(config.md5fname);
  else
	prnerr0("WARNING: No or invalid MD5 checksum file specified!");

  /* Let the command line file name override the one from the config file */
  if (tmpdrvfname != NULL) {
	if (config.drvfname == NULL)
		config.drvfname = tmpdrvfname;
	else
		free(tmpdrvfname);
	tmpdrvfname = NULL;
  } else if (config.drvfname == NULL)
	copystr(&config.drvfname, DRIVERFILE);

  /* Normalize file name of network driver configuration file and read it */
  checkaccess(&config.drvfname, NULL);
  if (config.drvfname != NULL)
	readconfig(driversects, config.drvfname);
  else
	prnerr0("WARNING: No or invalid network driver config file specified!");

  /* Normalize directory and file names and resolve cross-references */
  donormalize();
  doresolve();

  /* Cleanup MD5 checksum list, which is no longer needed */
  clearmd5();
}



syntax highlighted by Code2HTML, v. 0.9.1