/*
 * user.c  -  user interface for makerom bootrom configuration utility
 *
 * Copyright (C) 1995-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: user.c,v 1.8 2003/03/09 00:43:08 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include "makerom.h"
#include "doconfig.h"
#include "protini.h"
#include "console.h"



/*
 * Local declarations
 */
#define DEFAULTINT	0x62		/* default packet driver interrupt */
#define MAXEXECSIZE	(128L * 1024L)	/* maximum size of DOS program */
#define PKTWATCHSIZE	2048L		/* stack size for pktwatch */



/*
 * Variables local to this module
 */
static struct bootdef bootd;
static int is86 = FALSE;




/*
 **************************************************************************
 *
 *		Ask user about parts of bootrom
 *
 **************************************************************************
 */

/*
 * Let the user select a bootrom kernel.
 */
static void getkernel()
{
  int havemenu = FALSE;
  int usemenu = FALSE;
  int useint18 = FALSE;
  int bootask = FALSE;
  int asktime = 0;

  /* Initialize boot definition structure */
  memset(&bootd, 0, sizeof(bootd));

  /* Ask user about bootrom kernel options */
  is86 = getyn("Build bootrom for a processor older than 386", FALSE);
  havemenu = ((is86 ? config.files[FILE_KERNELM].filename16 :
                      config.files[FILE_KERNELM].filename32) != NULL);
  if (havemenu)
	usemenu = getyn("Include support for old-style menus (not recommended)", usemenu);
  useint18 = getyn("Do you want the BIOS to look for boot disks", useint18);
  if (!useint18) {
	bootask = getyn("Do you want the bootrom to ask before booting from network", bootask);
	if (bootask)
		asktime = getsel("How many seconds should the bootrom wait asking (0 = forever)",
									0, 31, asktime);
  }
  printf("\n\n");

  /* Generate bootrom definition */
  if (is86) {
	copystr(&bootd.kernelname, (usemenu ?
					config.files[FILE_KERNELM].filename16 :
					config.files[FILE_KERNEL].filename16));
	copystr(&bootd.loaders[0].name, config.files[FILE_ROM].filename16);
	copystr(&bootd.loaders[1].name, config.files[FILE_FLOPPY].filename16);
  } else {
	copystr(&bootd.kernelname, (usemenu ?
					config.files[FILE_KERNELM].filename32 :
					config.files[FILE_KERNEL].filename32));
	copystr(&bootd.loaders[0].name, config.files[FILE_ROM].filename32);
	copystr(&bootd.loaders[1].name, config.files[FILE_FLOPPY].filename32);
  }
  bootd.loadernum = 2;
  bootd.canflash = FALSE;
  bootd.loaders[0].outname = NULL;		/* set later */
  bootd.loaders[0].outtype = OUT_NONE;		/* set later */
  bootd.loaders[0].outsize = 0;
  bootd.loaders[0].useint18 = useint18;
  bootd.loaders[0].cardinst = TRUE;		/* eventually modified later */
  bootd.loaders[0].bootask = bootask;
  bootd.loaders[0].asktime = asktime;
  bootd.loaders[1].outname = NULL;		/* set later */
  bootd.loaders[1].outtype = OUT_NONE;		/* set later */
  bootd.loaders[1].outsize = 0;
  bootd.loaders[1].useint18 = TRUE;
  bootd.loaders[1].cardinst = FALSE;
  bootd.loaders[1].bootask = FALSE;
  bootd.loaders[1].asktime = 0;

  /* Check that all files are accessible */
  if (bootd.kernelname == NULL) {
	prnerr0("unable to access bootrom kernel file");
	exit(EXIT_ACCESS);
  }
  if (bootd.loaders[0].name == NULL) {
	prnerr0("unable to access rom loader file");
	exit(EXIT_ACCESS);
  }
  if (bootd.loaders[1].name == NULL) {
	prnerr0("unable to access floppy loader file");
	exit(EXIT_ACCESS);
  }
}



/*
 * Get type of output file
 */
static void getouttype()
{
  static char *outdesc[OUT_MAX - OUT_MIN + 1] = {
		"Raw binary",
		"Intel hex",
		"Motorola hex",
		"Tektronix hex",
		"Image for programming a FlashCard across the network",
		"Image for programming a flash EPROM accross the network"
  };

  int outtype, i;

  /* Ask user about output type */
  printf("Available output file types for rom image:\n");
  for (i = OUT_MIN; i <= OUT_MAX; i++) {
	if (!bootd.canflash && i == OUT_FLASH)
		break;
	printf("(%d)\t%s\n", i, outdesc[i - OUT_MIN]);
  }
  assert(i >= OUT_MAX);		/* this requires OUT_MAX == OUT_FLASH */
  outtype = getsel("Select the format you wish to use", OUT_MIN,
							i - 1, OUT_DEF);
  printf("\n\n");

  /* Set values in boot definition structure */
  bootd.loaders[0].outtype = outtype;
  switch (outtype) {
	case OUT_FLASH:
		copystr(&(bootd.loaders[0].outname), "image.flash");
		bootd.loaders[0].cardinst = TRUE;
		break;
	case OUT_FC:
		copystr(&(bootd.loaders[0].outname), "image.flash");
		bootd.loaders[0].cardinst = FALSE;
		break;
	case OUT_IHEX:
	case OUT_MHEX:
	case OUT_THEX:
		copystr(&(bootd.loaders[0].outname), "image.hex");
		bootd.loaders[0].cardinst = TRUE;
		break;
	case OUT_BINARY:
	default:
		copystr(&(bootd.loaders[0].outname), "image.rom");
		bootd.loaders[0].cardinst = TRUE;
		break;
  }
  copystr(&(bootd.loaders[1].outname), "image.flo");
  bootd.loaders[1].cardinst = FALSE;
  bootd.loaders[1].outtype = OUT_BINARY;
}




/*
 **************************************************************************
 *
 *	General routines for sorting description strings
 *
 **************************************************************************
 */

/* Structure holding description information */
struct netdesc {
	struct netdesc	*next;
	union {
		struct filedesc	*fdesc;
		struct drvdesc  *ddesc;
	}		 desc;
	char		*descstr;
};



/*
 * Compare a description table entry for qsort routine
 */
static int netdescmp(entry1, entry2)
const voidstar entry1;
const voidstar entry2;
{
  struct netdesc *dp1 = *((struct netdesc **)entry1);
  struct netdesc *dp2 = *((struct netdesc **)entry2);

  assert(dp1 != NULL && dp2 != NULL &&
         dp1->descstr != NULL && dp2->descstr != NULL);
  return(strcmp(dp1->descstr, dp2->descstr));
}



/*
 * Generate an array of description records and sort it
 */
static struct netdesc **sortdesclist(desclist, descnum)
struct netdesc *desclist;
int descnum;
{
  struct netdesc *dp;
  struct netdesc **descarray;
  int i;

  /* Check that we have anything to process */
  if (desclist == NULL || descnum == 0)
	return(NULL);

  /* Generate array of linked list */
  descarray = (struct netdesc **)nbmalloc((descnum + 1) * sizeof(struct netdesc *));
  i = 0;
  dp = desclist;
  while (dp != NULL) {
	descarray[i] = dp;
	dp = dp->next;
	descarray[i]->next = NULL;
	i++;
  }
  assert(i == descnum);
  descarray[i] = NULL;

  /* Sort the array */
  qsort(descarray, descnum, sizeof(struct netdesc *), &netdescmp);
  return(descarray);
}



/*
 * Delete the description array
 */
static void deldescarray(descarray)
struct netdesc **descarray;
{
  int i = 0;

  while(descarray[i] != NULL)
	free(descarray[i++]);
  free(descarray);
}




/*
 **************************************************************************
 *
 *			Selection of network card
 *
 **************************************************************************
 */

/*
 * Generate sorted array of selectable network card descriptions
 */
static struct netdesc **gencarddescarray()
{
  struct filedesc *fp;
  struct drvdesc *drvp;
  struct descstr *sp;
  struct netdesc *dp;
  struct netdesc *desclist = NULL;
  int havedriver[NETDRV_NUM];
  int i, descnum;

  /* Check which network driver interfaces are available */
  for (i = DRVTYPE_MIN; i <= DRVTYPE_MAX; i++) {
	if (is86)
		havedriver[i] = (config.netdrv[i].filename16 != NULL);
	else
		havedriver[i] = (config.netdrv[i].filename32 != NULL);
  }

  /* Scan through list of available network cards */
  descnum = 0;
  fp = config.drvdesc;
  while (fp != NULL) {
	i = 0;
	drvp = fp->drvlist;
	while (drvp != NULL) {
		if (havedriver[drvp->type])
			i++;
		drvp = drvp->next;
	}
	if (i > 0) {
		sp = fp->descript;
		if (sp == NULL) {
			dp = (struct netdesc *)nbmalloc(sizeof(struct netdesc));
			dp->desc.fdesc = fp;
			dp->descstr = fp->name;
			dp->next = desclist;
			desclist = dp;
			descnum++;
		} else while (sp != NULL) {
			dp = (struct netdesc *)nbmalloc(sizeof(struct netdesc));
			dp->desc.fdesc = fp;
			dp->descstr = sp->descript;
			dp->next = desclist;
			desclist = dp;
			sp = sp->next;
			descnum++;
		}
	}
	fp = fp->next;
  }

  /* Sort description list */
  return(sortdesclist(desclist, descnum));
}



/*
 * Print list of network card descriptions
 */
static int printcarddescarray(descarray)
struct netdesc **descarray;
{
  int i = 0;
  int line = 0;

  while (descarray[i] != NULL) {
	printf("(%d)\t%s\n", i + 1, descarray[i]->descstr);
	i++;
	line++;
	if (line >= LISTLINES && descarray[i] != NULL) {
		printf("\nPress <ENTER> to continue listing ");
		(void)getstring(NULL);
		printf("\n");
		line = 0;
	}
  }
  printf("\n");
  return(i);
}



/*
 * Let the user select the bus type
 */
static int getbustype(fp)
struct filedesc *fp;
{
  /* If we have a bus type already, don't need to ask */
  if (fp != NULL && fp->bustype != BUSTYPE_NONE)
	return(fp->bustype);

  /* Otherwise print a list of bus types and let the user select */
  printf("\n\nKnown network card bus types:\n");
  printf("(%d)\tISA bus (including PnP)\n", BUSTYPE_ISA);
  printf("(%d)\tEISA bus\n", BUSTYPE_EISA);
  printf("(%d)\tMCA bus\n", BUSTYPE_MCA);
  printf("(%d)\tPCI bus\n", BUSTYPE_PCI);
  return(getsel("Select the bus type of your network card",
					BUSTYPE_MIN, BUSTYPE_MAX, BUSTYPE_ISA));
}



/*
 * Let the user select a network card
 */
static void getnetcard(fdp)
struct filedesc **fdp;
{
  struct filedesc *fp = NULL;
  struct netdesc **descarray;
  char *pnpdevid;
  int descnum, sel = -1;
  int bustype;

  /* Print list of known network cards and let the user decide */
  if ((descarray = gencarddescarray()) != NULL) {
	do {
		printf("List of known network cards:\n");
		printf("(0)\tunknown card with user supplied driver\n");
		descnum = printcarddescarray(descarray);
		sel = getsel("Select a network card (-1 to review the list)",
							-1, descnum, -1);
	} while (sel < 0);
	if (sel > 0) {
		printf("\nYou selected: %s\n", descarray[sel - 1]->descstr);
		fp = descarray[sel - 1]->desc.fdesc;
	}
	deldescarray(descarray);
  }

  /* Get the bus type and PCI/PnP vendor/device ID for the network card */
  bustype = getbustype(fp);
  if (bustype == BUSTYPE_PCI) {
	if (fp == NULL || fp->pci_vendid == 0)
		bootd.pci_vendid = getnum("Enter PCI vendor ID", 0, 0xffff, TRUE);
	else
		bootd.pci_vendid = fp->pci_vendid;
	if (fp == NULL || fp->pci_devid == 0)
		bootd.pci_devid = getnum("Enter PCI device ID", 0, 0xffff, TRUE);
	else
		bootd.pci_devid = fp->pci_devid;
  } else if (fp == NULL || fp->pnp_devid == NULL) {
	while (TRUE) {
		pnpdevid = getstring("Enter PnP device ID string (maybe empty)");
		if (pnpdevid == NULL || strlen(pnpdevid) == 0) {
			pnpdevid = NULL;
			break;
		}
		if (strlen(pnpdevid) != PNPIDLEN ||
		    !isalpha(pnpdevid[0]) ||
		    !isalpha(pnpdevid[1]) ||
		    !isalpha(pnpdevid[2]) ||
		    !isxdigit(pnpdevid[3]) ||
		    !isxdigit(pnpdevid[4]) ||
		    !isxdigit(pnpdevid[5]) ||
		    !isxdigit(pnpdevid[6]))
			printf("  Invalid ID string, it has to look like: ABC1234\n");
		else
			break;
		pnpdevid[0] = toupper(pnpdevid[0]);
		pnpdevid[1] = toupper(pnpdevid[1]);
		pnpdevid[2] = toupper(pnpdevid[2]);
	}
	if (pnpdevid != NULL)
		copystr(&(bootd.pnp_devid), pnpdevid);
  } else
	copystr(&(bootd.pnp_devid), fp->pnp_devid);
  bootd.bus_type = bustype;

  /* Check if we can offer the flash EPROM option to the user */
  bootd.canflash = checkflash(&bootd);

  /* Prepare return value */
  *fdp = fp;
  printf("\n\n");
}




/*
 **************************************************************************
 *
 *		Selection of network driver
 *
 **************************************************************************
 */

/*
 * Generate sorted array of selectable network drivers
 */
static struct netdesc **gendrvdescarray(fp)
struct filedesc *fp;
{
  struct drvdesc *drvp;
  struct netdesc *dp;
  struct netdesc *desclist = NULL;
  int havedriver[NETDRV_NUM];
  int i, descnum;

  /* Check if we have any drivers at all */
  if (fp == NULL || fp->drvlist == NULL)
	return(NULL);

  /* Check which network driver interfaces are available */
  for (i = DRVTYPE_MIN; i <= DRVTYPE_MAX; i++) {
	if (is86)
		havedriver[i] = (config.netdrv[i].filename16 != NULL);
	else
		havedriver[i] = (config.netdrv[i].filename32 != NULL);
  }

  /* Scan through list of available network drivers */
  descnum = 0;
  drvp = fp->drvlist;
  while (drvp != NULL) {
	if (havedriver[drvp->type]) {
		if (drvp->descript == NULL) {
			if (drvp->filename != NULL) {
				dp = (struct netdesc *)nbmalloc(sizeof(struct netdesc));
				dp->desc.ddesc = drvp;
				dp->descstr = drvp->filename;
				dp->next = desclist;
				desclist = dp;
				descnum++;
			}
		} else {
			dp = (struct netdesc *)nbmalloc(sizeof(struct netdesc));
			dp->desc.ddesc = drvp;
			dp->descstr = drvp->descript;
			dp->next = desclist;
			desclist = dp;
			descnum++;
		}
	}
	drvp = drvp->next;
  }

  /* Sort description list */
  return(sortdesclist(desclist, descnum));
}



/*
 * Print list of network driver descriptions
 */
static int printdrvdescarray(descarray)
struct netdesc **descarray;
{
  static char *dtypes[] = {
	"packet driver", "NDIS driver", "UNDI driver"
  };

  int i = 0;
  int line = 0;

  while (descarray[i] != NULL) {
	printf("(%d)\t%s\t%s\n", i + 1,
				dtypes[descarray[i]->desc.ddesc->type - 1],
				descarray[i]->descstr);
	i++;
	line++;
	if (line >= LISTLINES && descarray[i] != NULL) {
		printf("\nPress <ENTER> to continue listing ");
		(void)getstring(NULL);
		printf("\n");
		line = 0;
	}
  }
  printf("\n");
  return(i);
}



/*
 * Scan through the network driver array and determine the number of
 * bits in the options field. Then select the interface with the least
 * number of bits, e.g. that one with the least number of options to
 * be entered by the user.
 */
static int checkdefdrv(descarray)
struct netdesc **descarray;
{
  struct drvdesc *dp;
  int def, bitnum;
  int i, j, k;

  i = 0;
  def = 0;
  bitnum = 16;
  while (descarray[i] != NULL) {
	dp = descarray[i]->desc.ddesc;
	k = 0;
	switch (dp->type) {
		case DRVTYPE_PD:
			k = dp->drv.pd.options;
			break;
		case DRVTYPE_NDIS:
			k = dp->drv.ndis.options;
			break;
		case DRVTYPE_UNDI:
			/* there is no options value for UNDI drivers */
			k = 0;
			break;
		default:
			/* should never happen */
			k = 16;
			break;
	}
	j = 0;
	while (k > 0) {
		if ((k & 1) != 0)
			j++;
		k >>= 1;
	}
	if (j <= bitnum) {
		bitnum = j;
		def = i;
	}
	i++;
  }
  return(def);
}



/*
 * Let the user select a network driver for the interface card selected. If
 * none has been selected just ask for the network driver interface type.
 */
static struct drvdesc *getnetdrv(fp)
struct filedesc *fp;
{
  struct netdesc **descarray;
  struct drvdesc *dp;
  static struct drvdesc ddesc;
  int drvtypelist[NETDRV_NUM];
  int descnum, sel;
  int i, j, def;
  char *cp;

  /* If we have preselected a network card, print a list of available drivers */
  if (fp != NULL) {
	if ((descarray = gendrvdescarray(fp)) != NULL) {
		assert(descarray[0] != NULL);
		if (descarray[1] == NULL) {
			/* We have just one network driver available */
			dp = descarray[0]->desc.ddesc;
		} else {
			/* There is more than one driver available */
			printf("List of drivers for the selected network card:\n");
			descnum = printdrvdescarray(descarray);
			def = checkdefdrv(descarray);
			sel = getsel("Select a driver", 1, descnum, def + 1);
			dp = descarray[sel - 1]->desc.ddesc;
		}
		deldescarray(descarray);
		return(dp);
	}
  }

  /*
   * If we got here a network card has not been selected or there are no
   * suitable network drivers available. We therefore ask about the type of
   * network driver interface and then return an empty static driver description
   * record.
   */

  /* Determine the interface types which are available */
  j = 0;
  for (i = DRVTYPE_MIN; i <= DRVTYPE_MAX; i++) {
	if (is86)
		cp = config.netdrv[i].filename16;
	else
		cp = config.netdrv[i].filename32;
	if (cp != NULL)
		drvtypelist[j++] = i;
  }

  /* There should be at least one interface available */
  if (j == 0) {
	prnerr0("no network driver interfaces available");
	exit(EXIT_MAKEROM_IFFND);
  }

  /* Let the user select the network driver interface type */
  sel = 1;
  if (j > 1) {
	printf("The following network driver interfaces are available:\n");
	for (i = 0; i < j; i++) {
		cp = config.netdrv[drvtypelist[i]].descript;
		if (cp == NULL) switch(drvtypelist[i]) {
			case DRVTYPE_PD:
				cp = "packet driver interface";
				break;
			case DRVTYPE_NDIS:
				cp = "NDIS driver interface";
				break;
			case DRVTYPE_UNDI:
				cp = "generic UNDI driver interface";
				break;
		}
		assert(cp != NULL);
		printf("(%d)\t%s\n", i + 1, cp);
	}
	sel = getsel("Select the interface you wish to use", 1, j,
								drvtypelist[0]);
  }
  memset(&ddesc, 0, sizeof(ddesc));
  ddesc.type = drvtypelist[sel - 1];
  switch (ddesc.type) {
	case DRVTYPE_PD:
		ddesc.drv.pd.minsize = -2L;
		ddesc.drv.pd.maxsize = -2L;
		break;
	case DRVTYPE_NDIS:
		ddesc.drv.ndis.minsize = -2L;
		ddesc.drv.ndis.maxsize = -2L;
		break;
	case DRVTYPE_UNDI:
	default:
		break;
  }
  return(&ddesc);
}



/*
 * Get network driver options
 */
static char *getnetopt(str, opt)
char *str;
int opt;
{
  long hwirq = 0L;
  long ioaddr = 0L;
  long basemem = 0L;
  long dmanum = 0L;
  long auitype = 0L;
  int retbuflen, retbufsize;
  char *cp, *retbuf, buf[128], c;
  size_t len;

  /* Ask the user for required driver values */
  if (opt) {
	printf("Enter network driver options:\n");
	if (opt & HW_IRQ)
		hwirq = getnum("  Hardware IRQ number", 2, 15, FALSE);
	if (opt & IO_ADDR)
		ioaddr = getnum("  I/O address", 0, 0xffff, TRUE);
	if (opt & BASE_MEM)
		basemem = getnum("  Shared memory address", 0, 0xffff, TRUE);
	if (opt & DMA_NUM)
		dmanum = getnum("  DMA number", 0, 7, FALSE);
	if (opt & AUI_TYPE)
		auitype = getnum("  AUI type (-1 for default)", -1, 1, FALSE);
  }
  if (auitype < 0L)
	auitype = 65535L;

  /* Generate return string */
  retbuf =  NULL;
  retbuflen = retbufsize = 0;
  while (*str) {
	if (*str != '%') {
		buf[0] = *str;
		buf[1] = '\0';
	} else {
		if (!(c = *(++str)))
			break;
		switch (c) {
			case 'S':
				sprintf(buf, "%x", DEFAULTINT);
				break;
			case 's':
				sprintf(buf, "%d", DEFAULTINT);
				break;
			case 'H':
				sprintf(buf, "%lx", hwirq);
				break;
			case 'h':
				sprintf(buf, "%ld", hwirq);
				break;
			case 'A':
				sprintf(buf, "%lx", ioaddr);
				break;
			case 'a':
				sprintf(buf, "%ld", ioaddr);
				break;
			case 'M':
				sprintf(buf, "%lx", basemem);
				break;
			case 'm':
				sprintf(buf, "%ld", basemem);
				break;
			case 'D':
				sprintf(buf, "%lx", dmanum);
				break;
			case 'd':
				sprintf(buf, "%ld", dmanum);
				break;
			case 't':
				if (auitype > 0)
					sprintf(buf, "%ld", auitype);
				break;
			default:
				buf[0] = c;
				buf[1] = '\0';
				break;
		}
	}
	str++;
	len = (((retbuflen + strlen(buf) + 1) / 32) + 1) * 32;
	if (retbufsize < len) {
		cp = (char *)nbmalloc(len);
		if (retbuf != NULL) {
			strcpy(cp, retbuf);
			free(retbuf);
		}
		retbuf = cp;
		retbufsize = len;
	}
	cp = &retbuf[retbuflen];
	strcpy(cp, buf);
	retbuflen += strlen(buf);
  }
  return(retbuf);
}



/*
 * Ask the user about DOS program execution sizes
 */
static void getsizes(minsize, maxsize, needmin)
long *minsize;
long *maxsize;
int needmin;
{
  long smin, smax;

  while (TRUE) {
	/*
	 * The sizes are defined as follows:
	 *
	 *   minimum size  -  Size of the program after becoming resident.
	 *                    If the program doesn't get resident this
	 *                    number should be zero. The default is that
	 *                    the program becomes resident, and it's size
	 *                    resident size is the same as the load size.
	 *
	 *   maximum size  -  Size of the program when it gets loaded and
	 *                    before it gets started. The default is deter-
	 *                    mined lateron by the binary patch routines.
	 *                    For EXE programs, the size is in the EXE
	 *                    header. COM programs always get 64kB.
	 */
	smin = getnum("Enter minimum execution size in bytes (-1 = default)",
						-1L, MAXEXECSIZE, FALSE);
	smax = getnum("Enter maximum execution size in bytes (-1 = default)",
						-1L, MAXEXECSIZE, FALSE);
	if (smax == 0L)
		printf("Maximum size is not allowed to be zero\n");
	else if (needmin && smin == 0L)
		printf("Minimum size is not allowed to be zero\n");
	else if (smin > 0L && smax > 0L && smin > smax)
		printf("Minimum size is larger than maximum size\n");
	else
		break;
  }
  *minsize = smin;
  *maxsize = smax;
}



/*
 * Ask the user about additional DOS programs to execute
 */
static int getuserprog(pdp)
struct progdef *pdp;
{
  int i;
  int drvindex = 0;
  char *pathname = NULL;
  char *cmdline = NULL;
  char *pattern = NULL;
  char *buf;
  long minsize, maxsize;

  copystr(&pattern, "*.com:*.exe");
  while(getyn("Do you want to specify an additional program", FALSE)) {
	pathname = getfilename("Enter path name of the program",
						config.utilsdir, pattern);
	buf = getstring("Enter command line for the program");
	if (*buf)
		copystr(&cmdline, buf);
	getsizes(&minsize, &maxsize, FALSE);
	if (getyn("Should the program run before the network driver", FALSE)) {
		for (i = pdp->prognum; i > drvindex; i--) {
			pdp->prognames[i] = pdp->prognames[i - 1];
			pdp->progargs[i] = pdp->progargs[i - 1];
			pdp->minsizes[i] = pdp->minsizes[i - 1];
			pdp->maxsizes[i] = pdp->maxsizes[i - 1];
		}
		drvindex++;
	} else
		i = pdp->prognum;
	pdp->prognames[i] = pathname;
	pdp->progargs[i] = cmdline;
	pdp->minsizes[i] = minsize;
	pdp->maxsizes[i] = maxsize;
	pdp->prognum++;
  }
  free(pattern);
  return(drvindex);
}



/*
 * Ask the user about relevant information for packet drivers
 */
static void getpktdrvinfo(dp)
struct drvdesc *dp;
{
  struct pktdrvdef *defp = &bootd.netdrv.driverdefs.pd;
  int options = 0;
  long minsize = -2L;
  long maxsize = -2L;
  char *filename = NULL;
  char *cmdline = NULL;

  /* Get info from description record */
  assert(dp != NULL);
  filename = dp->filename;
  cmdline = dp->drv.pd.cmdline;
  options = dp->drv.pd.options;
  minsize = dp->drv.pd.minsize;
  maxsize = dp->drv.pd.maxsize;

  /* Get filename of packet driver binary */
  if (filename == NULL)
	defp->progs.prognames[0] = getfilename("Enter path of packet driver",
					config.netdrv[DRVTYPE_PD].searchdir,
					config.netdrv[DRVTYPE_PD].patlist);
  else
	defp->progs.prognames[0] = filename;
  defp->progs.prognum = 1;

  /* Ask user for command line options */
  if (cmdline == NULL) {
	cmdline = getstring("Enter command line for packet driver [0x%S]");
	if (!*cmdline)
		cmdline = "0x%S";
  }
  defp->progs.progargs[0] = getnetopt(cmdline, options);

  /* Ask user about execution sizes */
  if (minsize < -1L || maxsize < -1L || maxsize == 0)
	getsizes(&minsize, &maxsize, TRUE);
  defp->progs.minsizes[0] = minsize;
  defp->progs.maxsizes[0] = maxsize;

  /* Ask the user if to include the packet driver debugger program */
  filename = NULL;
  copystr(&filename, "pktwatch.com");
  checkaccess(&filename, config.utilsdir);
  if (filename != NULL &&
      getyn("Do you want to use the packet driver debugger", FALSE)) {
	defp->progs.prognames[defp->progs.prognum] = filename;
	defp->progs.progargs[defp->progs.prognum] = NULL;
	defp->progs.minsizes[defp->progs.prognum] = filesize(filename);
	defp->progs.maxsizes[defp->progs.prognum] =
		defp->progs.minsizes[defp->progs.prognum] + PKTWATCHSIZE;
	defp->progs.prognum++;
  }

  /* Ask the user to include additional programs */
  defp->drvindex = getuserprog(&(defp->progs));
}



/*
 * Ask the user about relevant information for NDIS drivers
 */
static void getndisinfo(dp)
struct drvdesc *dp;
{
  struct ndisdef *defp = &bootd.netdrv.driverdefs.ndis;
  int options = 0;
  long minsize = -2L;
  long maxsize = -2L;
  char *filename = NULL;
  char *protini = NULL;
  char *cp;

  /* Get info from description record */
  assert(dp != NULL);
  filename = dp->filename;
  protini = dp->drv.ndis.protini;
  options = dp->drv.ndis.options;
  minsize = dp->drv.ndis.minsize;
  maxsize = dp->drv.ndis.maxsize;

  /* Get filename of NDIS driver binary */
  if (filename == NULL)
	defp->progs.prognames[0] = getfilename("Enter path of NDIS driver",
					config.netdrv[DRVTYPE_NDIS].searchdir,
					config.netdrv[DRVTYPE_NDIS].patlist);
  else
	defp->progs.prognames[0] = filename;
  defp->progs.progargs[0] = NULL;
  defp->progs.prognum = 1;

  /* Ask user about execution sizes */
  if (minsize < -1L || maxsize < -1L || maxsize == 0)
	getsizes(&minsize, &maxsize, TRUE);
  defp->progs.minsizes[0] = minsize;
  defp->progs.maxsizes[0] = maxsize;

  /* Ask for protocol.ini file and driver options and normalize protocol.ini */
  filename = NULL;
  if (protini != NULL && !strncmp(protini, "file:", 5))
	copystr(&filename, &(protini[5]));
  if (protini == NULL || (filename != NULL && !*filename))
	filename = getfilename("Enter path name of PROTOCOL.INI file",
						config.netdrvdir, "*.ini");
  if (filename != NULL) {
	cp = readprotini(filename);
	protini = getnetopt(cp, options);
	free(filename);
	free(cp);
  } else {
	protini = getnetopt(protini, options);
  }
  parseprotini(protini, &(defp->protocolini), &(defp->protinisize));
  free(protini);

  /* Ask the user to include additional program */
  defp->drvindex = getuserprog(&(defp->progs));
}



/*
 * Ask the user about relevant information for UNDI drivers
 */
static void getundiinfo(dp)
struct drvdesc *dp;
{
  struct undidef *defp = &bootd.netdrv.driverdefs.undi;
  char *filename = NULL;

  /* Get info from description record */
  assert(dp != NULL);
  filename = dp->filename;

  /* Get filename of UNDI driver binary */
  if (filename == NULL)
	defp->name = getfilename("Enter path name of UNDI driver",
					config.netdrv[DRVTYPE_UNDI].searchdir,
					config.netdrv[DRVTYPE_UNDI].patlist);
  else
	defp->name = filename;
}



/*
 * Let the user select a network driver and get all relevant information for
 * the network driver.
 */
static void getdrvinfo(fp)
struct filedesc *fp;
{
  struct drvdesc *dp;

  /* Let the user select the network driver */
  dp = getnetdrv(fp);
  assert(dp != NULL && dp->type >= DRVTYPE_MIN && dp->type <= DRVTYPE_MAX);
  if (is86)
	copystr(&bootd.netdrv.name, config.netdrv[dp->type].filename16);
  else
	copystr(&bootd.netdrv.name, config.netdrv[dp->type].filename32);
  bootd.netdrv.drivertype = dp->type;

  /* Let the user enter all relevant information for the network driver */
  switch (dp->type) {
	case DRVTYPE_PD:
		getpktdrvinfo(dp);
		break;
	case DRVTYPE_NDIS:
		getndisinfo(dp);
		break;
	case DRVTYPE_UNDI:
		getundiinfo(dp);
		break;
  }
  printf("\n\n");
}




/*
 **************************************************************************
 *
 *			Main routine
 *
 **************************************************************************
 */

/*
 * Main routine which handles all user input
 */
struct bootdef *getuser()
{
  struct filedesc *fp;

  getkernel();
  getnetcard(&fp);
  getdrvinfo(fp);
  getouttype();
  return(&bootd);
}



syntax highlighted by Code2HTML, v. 0.9.1