static char SCCS_ID [] = "@(#)mkconf.c	2.30";
/*
 *		Perkin-Elmer UNIX Configuration Utility
 *
 *
 *		(C) 1980, Richard Miller
 *		    University of Wollongong
 *
 *   There is code here to make a shell file to do the mknods
 *   required for the devices, but it is not fully implemented,
 *   and for that matter, it would be rather hard to handle all
 *   the cases well.   -- D Yost 11/22/81
 */

#include "../h/local.h"
#include "../h/dios.h"
#include <stdio.h>
#include <ctype.h>

/*
 * Table describing device characteristics
 */

#define CDEV		01	/* is a character device */
#define BDEV		02	/* is a block device */
#define B_AND_CDEV (BDEV|CDEV)
#define DEVAD		04	/* device addresses required */
#define CNTLAD		010	/* controller address required */

#define SELCHAD		020	/* selector channel address required */
#define SIZE            040     /* size specification required */
#define MAP		0100	/* logical device map required */
#define SPEEDS		0200	/* baud rate table required */

#define CONSDEV		0400	/* can be console device */
#define FSDEV		01000	/* can be file system device */
#define DREQ            02000   /* must be defined -- see struct req below */
#define DIOSAD          04000   /* DIOS addresses optional - 0 if not used */

#define TYPE           010000   /* type specification required */
#define LAYOUT         020000   /* layout specification required */
#define NAME           040000   /* name specification required */
#define PICT           0100000  /* picture specification required */

#define NMAP    8               /* max number of maps if DMAP */

/* There are two classes of required devices:
 * 1. Devices that appear in a list in the reqdev structure.
 * 2. A device that is required for some special device, such as swap,
 *    in which case its DREQ flag is set by the occurence of the
 *    special device.
 */
struct reqdev {
	char *rq_names;         /* names of devices in this class */
	int rq_def;             /* 1 if any were defined */
};
struct reqdev reqdev[] = {
	{ 
		"msm d40 dsk" 	}
	,
	{ 
		"vdu" 	}
	,
	{ 
		"clock" 	}
	,
	{ 
		0 	}
};
#define RQ_DISK  0
#define RQ_TTY   1
#define RQ_CLOCK 2

char	null[]	= "nulldev";	/* name of no-op fill-in routine */
char	no[]	= "nodev";	/* name of error fill-in routine */
char	zero[]	= "0";		/* null fill-in entry */
int	zeromap[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };

struct keyparm *findkey();

struct devconf {
	/* Static data */
	char	*d_name;	/* external name */
	int	d_flags;	/* device attributes - see #defines above */
	int	d_maxdev;	/* maximum number of devices permitted
						(or 0 for no limit) */
	int	d_nccb;		/* number of channel control blocks per dev */
	char	*d_bsw[6];	/* names of routines for bdevsw
						(if block device) */
	char	*d_csw[7];	/* names of routines for cdevsw
						(if character device) */
	char	*d_int[2];	/* names of interrupt handlers for even & odd
						address (character device), or for
						device & controller (block device) */
	struct reqdev *d_req;   /* required device class, if any */
	/* be sure dev name occurs in reqdev list */
	/* Input data */
#ifdef  MAKEDEV
	char    *d_devname;     /* name in /dev */
#endif  MAKEDEV
	char   *d_reqby;        /* if DREQ, what special device requires it */
	int	d_major;	/* major device number (if both BDEV and CDEV,
						this is block device number) */
	int	d_defined;	/* d_flags bits which have been defined */
	int	d_ndev;		/* number of devices to be configured */
	int	*d_dev;		/* list of device addresses */
	int     *d_cntl;         /* array controller addresses */
	union {                 /* channel address(es) */
		int *d_selch;    /*  array of selch address */
		int *d_dios;    /*   array of dios addresses */
	} 
	d_channel;
	union {
		int     d_size;         /* capacity for disks (ordinal ) */
		int     *d_type;        /* array of disc types */
	} 
	d_disc;
	int     *d_map;         /* logical device map index for disks */

} 
devconf[] = {

	/***  The order is important here.
	/***  Change it, and you will have to
	/***  redo the nodes in /dev
	/***  because the major device numbers will be off
	/***/

	/* Comms Mux, PALS or PASLA */

	{       
		"vdu", CDEV+DEVAD+DIOSAD+SPEEDS+CONSDEV, MAXVDU, 1,
		{ 
			0 		}
		,
		{ 
			"vduopen", "vduclose", "vduread", "vduwrite", "vduioctl",
			"vdustop", "vdu" 		}
		,
		{ 
			"vdurint", "vduxint" 		}
		,
		&reqdev[RQ_TTY]         /* vdu must also be in this list */
	}
	,

	/* Memory pseudo device */

	{       
		"mem", CDEV, 0, 0,
		{ 
			0 		}
		,
		{ 
			null, null, "mmread", "mmwrite", no, null, zero 		}
		,
		{ 
			0 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* Cartridge disk - 2.5 or 10 Megabyte */

	{       
		"dsk", B_AND_CDEV+DEVAD+CNTLAD+SELCHAD+SIZE+FSDEV, 8, 0,
		{ 
			null, null, "dskstrategy", "dsktab", "BD_NORMAL" }
		,
		{ 
			null, null, "dskread", "dskwrite", no, null, zero 		}
		,
		{ 
			"dskintr", "cntlintr" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* Controlling terminal pseudo device */

	{       
		"tty", CDEV, 0, 0,
		{ 
			0 		}
		,
		{ 
			"syopen", null, "syread", "sywrite", "sysioctl", null,
			zero 		}
		,
		{ 
			0 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* 1st Magnetic tape - 800 or 1600 bpi */

	{       
		"mt", B_AND_CDEV+DEVAD+SELCHAD, 4, 0,
		{ 
			"mtopen", "mtclose", "mtstrategy", "mtab", "BD_NORMAL" }
		,
		{ 
			"mtopen", "mtclose", "mtread", "mtwrite",
			"mtioctl", null, zero 		}
		,
		{ 
			"mtintr" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* Current loop interface */

	{       
		"cli", CDEV+DEVAD, 0, 0,
		{ 
			0 		}
		,
		{ 
			"cliopen", "cliclose", "cliread", "cliwrite",
			"cliioctl", null, "cli" 		}
		,
		{ 
			"cliint" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* 1st Line printer */

	{       
		"lp", CDEV+DEVAD, 1, 0,
		{ 
			0 		}
		,
		{ 
			"lpopen", "lpclose", no, "lpwrite", "lpioctl", null, zero 		}
		,
		{ 
			"lpint" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* MSM disk - 67 or 256 megabyte */

	{       
		"msm", B_AND_CDEV+DEVAD+CNTLAD+SELCHAD+NAME+TYPE+LAYOUT+PICT+FSDEV, 0, 0,
		{ 
			null, null, "msmstrategy", "msmtab", "BD_NORMAL" }
		,
		{ 
			null, null, "msmread", "msmwrite", no, null, zero 		}
		,
		{ 
			"msmintr" 		}
		,
		&reqdev[RQ_DISK]        /* msm must also be in this list */
	}
	,

	/* Synchronous data set adapter */

	{       
		"dsa", CDEV+DEVAD, 0, 2,
		{ 
			0 		}
		,
		{ 
			"dsaopen", "dsaclose", "dsaread", "dsawrite", "dsaioctl",
			"dsastop", "dsa" 		}
		,
		{ 
			"dsarint", "dsaxint" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* Multiplexed file pseudo device */

	{       
		"mx", CDEV, 0, 0,
		{ 
			0 		}
		,
		{ 
			"mxopen", "mxclose", "mxread", "mxwrite", "mxioctl", null,
			zero 		}
		,
		{ 
			0 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* 40 megabyte disk */

	{       
		"d40", B_AND_CDEV+DEVAD+CNTLAD+SELCHAD+MAP+FSDEV, 4, 0,
		{ 
			null, null, "d40strategy", "d40tab", "BD_NORMAL" }
		,
		{ 
			null, null, "d40read", "d40write", no, null, zero 		}
		,
		{ 
			"d40intr", "d40cintr" 		}
		,
		&reqdev[RQ_DISK]        /* d40 must also be in this list */
	}
	,

	/* Precision clock */

	{       
		"clock", DEVAD, 1, 0,
		{ 
			0 		}
		,
		{ 
			0 		}
		,
		{ 
			"clock" 		}
		,
		&reqdev[RQ_CLOCK]       /* clock must also be in this list */
	}
	,

	/* 2nd Magnetic tape - 800 or 1600 bpi */

	{       
		"mta", B_AND_CDEV+DEVAD+SELCHAD, 4, 0,
		{ 
			"mtaopen", "mtaclose", "mtastrategy", "mtaab", "BD_NORMAL" }
		,
		{ 
			"mtaopen", "mtaclose", "mtaread", "mtawrite",
			"mtaioctl", null, zero 		}
		,
		{ 
			"mtaintr" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* 3rd Magnetic tape - 800 or 1600 bpi */

	{       
		"mtb", B_AND_CDEV+DEVAD+SELCHAD, 4, 0,
		{ 
			"mtbopen", "mtbclose", "mtbstrategy", "mtbab", "BD_NORMAL" }
		,
		{ 
			"mtbopen", "mtbclose", "mtbread", "mtbwrite",
			"mtbioctl", null, zero 		}
		,
		{ 
			"mtbintr" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* Undefined device #1, used for devices defined by users. */

	{       
		"ud1", B_AND_CDEV, 1, 0,
		{ 
			0 }
		,
		{ 
			0 }
		,
		{ 
			0 }
		,
		
	}
	,

	/* Comms Mux, PALS or PASLA for simplex input */

	{       
		"sin", CDEV+DEVAD+DIOSAD+SPEEDS, 0, 1,
		{ 
			0 		}
		,
		{ 
			"sinopen", "sinclose", "sinread", null, "sinioctl", null,
			"sin" 		}
		,
		{ 
			"sinrint" 		}
		,  /* no transmit interrupt */
		{ 
			0 		}
		,
	}
	,

 /* megatek 7200 driver */

       {       "meg", CDEV+DEVAD+SELCHAD, 1, 0,
	       { 0 },
	       { "megopen", "megclose", "megread", "megwrite",
		       "megioctl", null, zero },
	       { "megintr" },
	       { 0 },
       },
	/* ptc Unet */

	{       
		"ptc",CDEV,0,0,
		{ 
			0 		}
		,
		{ 
			"ptcopen", "ptcclose", "ptcread", "ptcwrite", "ptyioctl",
			null, "pt_tty"                }
		,
		{ 
			0         }
		,
		{
			0
				}
		,
	}
	,
	/* pts Unet */

	{       
		"pts",CDEV,0,0,
		{ 
			0 		}
		,
		{ 
			"ptsopen", "ptsclose", "ptsread", "ptswrite", "ptyioctl",
			null, "pt_tty"                }
		,
		{ 
			0         }
		,
		{
			0
				}
		,
	}
	,
	/* up_ Unet */

	{       
		"up",CDEV,0,0,
		{ 
			0 		}
		,
		{ 
			"up_open", "up_close", "up_read", "up_write", "up_ioctl",
			null, zero                }
		,
		{ 
			0         }
		,
		{
			0
				}
		,
	}
	,
	/* uu_ Unet */

	{       
		"uu",CDEV,0,0,
		{ 
			0 		}
		,
		{ 
			"uu_open", "uu_close", "uu_read", "uu_write", "uu_ioctl",
			null, zero                }
		,
		{ 
			0         }
		,
		{
			0
				}
		,
	}
	,
	/* ud_ Unet */

	{       
		"ud",CDEV,0,0,
		{ 
			0 		}
		,
		{ 
			"ud_open", "ud_close", "ud_read", "ud_write", "ud_ioctl",
			null, zero                }
		,
		{ 
			0         }
		,
		{
			0
				}
		,
	}
	,
	/* Block Memory Device */

	{
		"mm",BDEV+MAP+FSDEV,1,0,
		{
			"mmopen", "mmclose", "mmstrategy", "mmtab", "BD_NOCACHE" }
		,
		{
			0 }
		,
		{
			0 }
		,
		{
			0 }
		,
	}
	,
	/* Line Interrupt */

	{
		"li",CDEV+DEVAD,8,0,
		{
			0 }
		,
		{
			"liopen", "liclose", "liread", no, "liioctl", null, zero }
		,
		{
			"liint" }
		,
		{
			0 }
		,
	}
	,

	/* Digital I/O Module */

	{
		"dio",CDEV+DEVAD,2,0,
		{
			0 }
		,
		{
			"dioopen", "dioclose", no, "diowrite", "dioioctl", null, zero }
		,
		{
			"dioint" }
		
		,
		{
			0 }
		,
	}
	,

	/* 2nd Line printer */

	{       
		"lpa", CDEV+DEVAD, 1, 0,
		{ 
			0 		}
		,
		{ 
			"lpaopen", "lpaclose", no, "lpawrite", "lpaioctl", null, zero 		}
		,
		{ 
			"lpaint" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* 3rd Line printer */

	{       
		"lpb", CDEV+DEVAD, 1, 0,
		{ 
			0 		}
		,
		{ 
			"lpbopen", "lpbclose", no, "lpbwrite", "lpbioctl", null, zero 		}
		,
		{ 
			"lpbint" 		}
		,
		{ 
			0 		}
		,
	}
	,

	/* Null name marks end of table */

	{	
		0		}
	,
};

/*
 * Table describing line disciplines
 */

struct lineconf {
	char	*l_name;		/* external name */
	int	l_include;		/* include this discipline if not 0 */
	int	l_select;		/* selectable via ioctl */
	/* N.B. All entries with select=1 must
						   precede those with select=0 */
	char	*l_lsw[10];		/* names of linesw entries */
} 
lineconf[] = {

	/* Standard tty driver */

	{	
		"tty", 1, 1,
		"ttyopen", null, "ttread", "ttwrite", no,
		"ttyinput", "ttyrend", null, "ttstart", null,
	}
	,

	/* Packet switch */

	{	
		"pack", 0, 1,
		"pkopen", "pkclose", "pkread", "pkwrite", "pkioctl",
		"pkrint", null, null, "pkxint", null,
	}
	,

	/* Multiplexed files */

	{	
		"mx", 0, 0,
		"mxopen", "mxclose", "mcread", "mcwrite", "mxioctl",
		null, null, null, null, null,
	}
	,
	/* Unet */

	{	
		"upk", 0, 0,
		"upkopen", "upkclose", "upkread", "upkwrite", "upkioctl",
		"upkrint", "upkrend", null, "upkstart", null
	}
	,
	/* Unet */

	{	
		"fpk", 0, 0,
		"upkopen", "upkclose", "upkread", "upkwrite", "upkioctl",
		"fpkrint", "fpkrend", null, "upkstart", null
	}
	,

	/* Null name marks end of table */

	{	
		0		}
	,
};

/*
 * Table for special device assignments
 */
struct spdev {
	char	*s_name;		/* external name */
	int	s_flag;			/* d_flag type required */
	int	s_minor;		/* minor device number */
	struct devconf *s_dev;		/* pointer to device description */
} 
spdev[] = {

	"root",	FSDEV,		0, 0,
	"swap",	FSDEV,		0, 0,
	"pipe",	FSDEV,		0, 0,
	"cons",	CONSDEV,	0, 0,
	0
};

/*
 * Table for parsing main-level keywords
 */

#define K_REQ            1      /* definition required */
#define K_NUM            2      /* needs numeric parameter */
#define K_KEY            4      /* needs keyword parameter */
#define K_STR            8      /* needs string parameter */
#define K_NAME          16      /* needs name parameter */
#define K_UNET          32      /* dummy flag to force Punet */

int	Pcpu(), Pmemory(), Ptotmem(), Pfloat(), Pdfloat();
int     Pswaplo(), Pnswap(), Pdump();
int     Ptimezone(), Pdaylight();
int     Pndevs();
int     Pserial(), Plicense(), Playout();
int     Punet();
int	Pmm();
struct keyparm {
	char	*k_name;		/* external name */
	int	k_flags;		/* see #defines above */
	int	(*k_fn)();		/* routine to process keyword */
	int	k_defined;		/* has been defined if nonzero */
} 
keyparm[] = {

	"cpu",                  K_REQ+K_NUM,    Pcpu,           0,
	"memory",               K_NUM,          Pmemory,        0,
	"user-memory",          K_NUM,          Pmemory,        0,
	"total-memory",         K_NUM,          Ptotmem,        0,
	"float",                K_KEY,          Pfloat,         0,
	"dfloat",               K_KEY,          Pdfloat,        0,
	"wcs",                  0,              0,              0,
	"timezone",             K_REQ+K_NUM,    Ptimezone,      0,
	"daylight",             K_REQ+K_NUM,    Pdaylight,      0,
	"swaplo",               K_REQ+K_NUM,    Pswaplo,        0,
	"nswap",                K_REQ+K_NUM,    Pnswap,         0,
	"dump",                 K_KEY,          Pdump,          0,
	"lra",                  0,              0,              0,
	"pic",                  0,              0,              0,
	"ndevs",                K_NUM,          Pndevs,         0,
	"cpu-serial-number",    K_REQ+K_STR,    Pserial,        0,
	"license",              K_REQ+K_STR,    Plicense,       0,
	"layout",               K_REQ+K_NAME,   Playout,        0,
	"unet",                 K_UNET,         Punet,          0,
	"mm",			0,		Pmm,		0,
	0
};

/*
 * Table for parsing device parameters
 */

int     Pdev(), Pcntl(), Pselch(), Ptype(), Pname(), Psize(),
Ppict(), Pmap(), Pspeeds(), Pdios();

struct devparm {
	char	*p_name;	/* external name */
	int	p_flag;		/* d_flags value associated with this parm */
	int	(*p_fn)();	/* function to process this parm */
} 
devparm[] = {

	"dev",		DEVAD,		Pdev,
	"cntl",		CNTLAD,		Pcntl,
	"selch",        SELCHAD,        Pselch,
	"type",         TYPE,           Ptype,
	"name",         NAME,           Pname,
	"size",         SIZE,           Psize,
	"map",		MAP,		Pmap,
	"pict",         PICT,           Ppict,
	"speeds",	SPEEDS,		Pspeeds,
	"dios",         DIOSAD,         Pdios,
	0
};

/*
 * Preprocessor #include files for c.c
 */
char	*includes[] = {
	"../h/local.h",
	"../h/param.h",
	"../h/systm.h",
	"../h/buf.h",
	"../h/tty.h",
	"../h/conf.h",
	"../h/proc.h",
	"../h/text.h",
	"../h/file.h",
	"../h/inode.h",
	"../h/acct.h",
	"../h/dskmap.h",
	"../h/ccb.h",
	0
};

/*
 * Other declarations for c.c
 */
char	*junk[] = {
	"buf\tbfreelist",
	"acct\tacctbuf",
	"inode\t*acctp",
	0
};

/*
 * Configuration data from input
 */
int	*speeds;		/* comm mux baud rates */
int	nspeeds;		/* number of entries in above */
int	cpu;			/* cpu model number */
int	memory;			/* maximum memory */
int	totmem;			/* total memory for seting up kernel seg regs */
int	singlefloat;		/* single precision floating point:
					0=none, 1=hardware, 2=software */
int	doublefloat;		/* double precision floating point: as above */
int	swaplo;			/* origin of swap area */
int	nswap;			/* length of swap area */
int     timezone;               /* number of hours west of greenwich */
int     daylight;               /* whether or not daylight savings time */
char	*dumpsw;		/* dump routine */
int	nccb;			/* number of auto-driver 'channel' cntl blks */
int	mx;			/* nonzero if mx files included */
char    serialnum[64];          /* cpu serial number */
char    license[64];            /* license number */

#define MAXDEV 1024             /* Maximum number of device addresses */
int     ndevs = 256;            /* number of device addresses supported */
int     highestdev;             /* highest device address specified */
#ifdef  CDEVMAJMAP
char    cdevmajmap[MAXDEV];/* map char device address => major device number */
/* dioses and selches have no major device number, so they */
/* appear as 0 in cdevmajmap */
/* This is currently not used */
#endif  CDEVMAJMAP
char    devmap[MAXDEV];         /* map device address => minor device number */
char    devint[MAXDEV];         /* map device address => interrupt table offset */
/* NOTE: only 64 (= 256/4) handlers can be handled by the present */
/* scheme.  This is because when we write out the devint array */
/* we write out the array index of the handler times 4 */
#define	MAXHANDLER	64	/* maximum number of interrupt routines */
#define SPURHANDLER     0       /* offset of built-in spurious entry */
#define SELCHOFFS       1       /* offset of built-in selch entry in table */
char	*handler[MAXHANDLER] = {	/* names of interrupt routines */
	"spurint",                      /* offset 0 is unexpected interrupt */
	"selchint",			/* selector channel always included */
};
int     nhandler = 2;           /* number of interrupt routines */
#define MAXSELCH 16             /* max number of selches mkconf can handle */
#define MAXCNTRL 32             /* max number of selches mkconf can handle */
#define MAXDTYPE 16
#define MAXMAP   32
int     diosoffs;               /* offset of dios entry in table */
static int     ndios;                   /* number of DIOSes */
static short   diosaddr[MAXDIOS];       /* DIOS addresses */
static int nselch;                      /* number of selector channels */
static short selchaddr[MAXSELCH]; /* selector channel addresses */
int     cntrloffs;
static int ncntrl;              /* number of controllers */
static short cntrladdr[MAXCNTRL]; /* controller addresses */
static short spindle[32];
static  int     nlayout =0;
char    mapname [32][16];       /* local array of map names */
char    discname [32][16];       /* local array of disc names */
int     *maplist [32];          /* pointers to maps */

/*
 * Files
 */
char    *configfile;                    /* input file name */
char    *cfile = "c.c";                 /* C output file name */
char    *sfile = "param.s";             /* as output file name */
#ifdef  MAKEDEV
char    *mfile = "mkdev.sh";            /* mkdev shell output file name */
FILE    *mfileno;
#endif  MAKEDEV

/*
 * Input parsing information
 */
enum token_t {          /* types of input token */
	ENDFILE,
	KEYWORD,
	NUMERIC
};
enum token_t    token_type;             /* type of next token on input file */
int     token_value;            /* value of token if numeric */
char    token_key[64];          /* value of token if keyword */
int     linenumber = 0;                 /* current input line number */


/*
 * Shorthand macros
 */
#define match(a)        !strcmp(a, token_key)

/*
 *      mkconf [ -c cfile ] [ -s sfile ] [ config ]
 *
 * Read configuration description in config file (default stdin).
 * Produce cfile (default "c.c") containing device driver information,
 * sfile (default "param.s") containing conditional assembly parameters,
#ifdef  MAKEDEV
 * and mfile (default mkdev.sh) shell file to make entries in /dev
#endif  MAKEDEV
 */

main(argc, argv)
char **argv;
{
	/*
		 * Obey arguments
		 */
	while (--argc > 0) {
		argv++;
		if (argv[0][0] == '-') switch (argv[0][1]) {
		case 'c':       /* change cfile */
			if (--argc <= 0)
				error("Missing cfile name");
			cfile = *++argv;
			break;

#ifdef  MAKEDEV
		case 'm':       /* change mfile */
			if (--argc <= 0)
				error("Missing sfile name");
			mfile = *++argv;
			break;
#endif  MAKEDEV

		case 's':       /* change sfile */
			if (--argc <= 0)
				error("Missing sfile name");
			sfile = *++argv;
			break;

		default:
			error("Unknown argument %s", *argv);
			break;
		} 
		else configfile = *argv;
	}
	/*
		 * Read config file and store data in internal form
		 */
	if (configfile && freopen(configfile, "r", stdin) == NULL)
		error("Can't open %s", configfile);
	linenumber = 1;         /* start line numbering for error messages */
	token();
	while (getconfig())
		;
	linenumber = 0;         /* turn off line numbers for error messages */

	/*
		 * Check validity of data and compute remaining parameters
		 */
	mkconf();

#ifdef  MAKEDEV
	/*
		 * Make mkdev.sh file
		 */
	if ((mfileno = fopen(mfile, "w")) == NULL)
		error("Can't write %s", mfile);
#endif  MAKEDEV
	/*
		 * Make C file
		 */
	if (freopen(cfile, "w", stdout) == NULL)
		error("Can't write %s", cfile);
	putcfile();

	/*
		 * Make as file
		 */
	if (freopen(sfile, "w", stdout) == NULL)
		error("Can't write %s", sfile);
	putsfile();

	exit(0);
}
/*
 * Make "c.c" file
 *
 * Contains     #includes
 *              bdevsw, cdevsw and linesw tables
 *              device assignments for root, swap, pipes
 *              other configuration variables
 *              device driver configuration information
 */
putcfile()
{
	register struct devconf         *dp;
	register struct lineconf        *lp;
	register int                    major, i;
	register int                    nldisp = 0;
	int     ii;

	printf ("/* from \"%s\" */\n\n", configfile ? configfile :
	"standard input");

	/*
		 * #includes
		 */
	{       
		register char   **pp;

		for (pp = includes; *pp; pp++)
			printf("#include \"%s\"\n", *pp);
	}
	/*
		 * Declarations for char switch routines
		 */
	printf("\nint   %s(), %s();\n", no, null);
	for (dp = devconf; dp->d_name; dp++) {
		if ((dp->d_flags&BDEV) && dp->d_ndev > 0) {
			register char *sep = "int\t";

			for (i = 0; i < 3; i++) {
				if (dp->d_bsw[i] != no && dp->d_bsw[i] != null) {
					printf("%s%s()", sep, dp->d_bsw[i]);
					sep = ", ";
				}
			}
			printf(";\n");
			if (dp->d_bsw[3] != zero)
				printf("struct buf      %s;\n", dp->d_bsw[3]);
		}
	}
	/*
		 * bdevsw
		 */
	printf("\nstruct bdevsw bdevsw[] = {\n");
	for (major = 0, dp = devconf; dp->d_name; dp++) {
		if (!(dp->d_flags&BDEV))
			continue;
		if (dp->d_ndev > 0) {
			printf("\t%s, %s, %s, ",
			dp->d_bsw[0], dp->d_bsw[1], dp->d_bsw[2]);
			if (dp->d_bsw[3] == zero)
				printf("0,");
			else
				printf("&%s,", dp->d_bsw[3]);
			printf(" %s,", dp->d_bsw[4]);
		} 
		else
			printf("\tnodev, nodev, nodev, 0, 0,");
		printf("\t/* %s = %d */\n", dp->d_name, major);
		putmfile(dp,major,BDEV);
		dp->d_major = major;
		major++;
	}
	printf("\t0\n};\n\n");
	/*
		 * declarations for char switch routines
		 */
	for (dp = devconf; dp->d_name; dp++) {
		if ((dp->d_flags&CDEV) && dp->d_ndev > 0) {
			register char *sep = "int\t";

			for (i = 0; i < 6; i++) {
				if (dp->d_csw[i] != no && dp->d_csw[i] != null) {
					printf("%s%s()", sep, dp->d_csw[i]);
					sep = ", ";
				}
			}
			printf(";\n");
			if (dp->d_csw[6] != zero){
				if (!strcmp(dp->d_name,"ptc")){
					printf("struct tty      %s[];\n",
					dp->d_csw[6]);
					continue;
				}
				if (!strcmp(dp->d_name,"pts"))
					continue;
				printf("struct tty      %s[%d];\n",
				dp->d_csw[6], dp->d_ndev);
			}
		}
	}
	/*
		 * cdevsw
		 */
	printf("\nstruct cdevsw cdevsw[] = {\n");
	for (major = 0, dp = devconf; dp->d_name; dp++) {
		if (!(dp->d_flags&CDEV))
			continue;
		if (dp->d_ndev > 0) {
			printf("\t%s, %s, %s, %s, %s, %s, %s,",
			dp->d_csw[0], dp->d_csw[1], dp->d_csw[2],
			dp->d_csw[3], dp->d_csw[4], dp->d_csw[5],
			dp->d_csw[6]);
		} 
		else
			printf("\tnodev, nodev, nodev, nodev, nodev, nodev, 0,");
		printf("\t/* %s = %d */\n", dp->d_name, major);
		putmfile(dp,major,CDEV);
		if (!(dp->d_flags & BDEV)) {
			register int i;
			dp->d_major = major;
#ifdef  CDEVMAJMAP
			/* fill in the cdevmajmap[] array */
			if (dp->d_dev)
				for (i = 0; i < dp->d_ndev; ++i)
					cdevmajmap[dp->d_dev[i]] = major;
#endif  CDEVMAJMAP
		}
		major++;
	}
	printf("\t0\n};\n\n");

	/*
		 * Definitions for linesw
		 */
	for (lp = lineconf; lp->l_name; lp++) {
		if (lp->l_include) {
			register char   *sep = "int\t";
			for (i = 0; i < 10; i++)
				if (lp->l_lsw[i] != no && lp->l_lsw[i] != null) {
					printf("%s%s()", sep, lp->l_lsw[i]);
					sep = ", ";
				}
			printf(";\n");
		}
	}
	/*
		 * linesw
		 */
	printf("\nstruct linesw linesw[] = {\n");
	for (major = 0, lp = lineconf; lp->l_name; lp++) {
		if (lp->l_include) {
			for (i = 0; i < 10; i++) {
				printf(i==0? "\t": i==5? ",\n\t\t": ", ");
				printf("%s", lp->l_lsw[i]);
			}
			printf(",\t/* %s = %d */\n", lp->l_name, major);
			if (lp->l_select)
				nldisp++;
			major++;
		}
	}
	printf("\t0\n};\n\nint  nldisp = %d;\n", nldisp);
	if (!mx)        /* force loading of fake mx routines */
		printf("int\tmpxchan();\nint\t(*ldmpx)() = mpxchan;\n");
	/*
		 * Special device assignments
		 */
	{       
		register struct spdev   *sp;

		for (sp = spdev; sp->s_name; sp++) {
			printf("dev_t\t%sdev = makedev(%d, %d);\n",
			sp->s_name, sp->s_dev->d_major, sp->s_minor);
		}
	}
	/*
		 * Assorted configuration variables
		 */
	printf("daddr_t\tswplo\t= %d;\nint\tnswap\t= %d;\n", swaplo, nswap);
	if(memory != 0 && totmem != 0 && memory > totmem) memory = totmem;
	if (memory == 0) memory = totmem;
	printf("caddr_t\tmemtop\t= %d;\n", memory);
	if (totmem == 0) totmem = memory;
	printf("caddr_t\ttotmem\t= %d;\n", totmem);
	if(strcmp(dumpsw,"none"))
	  printf("\nint\t%s();\nint\t(*dumpsw)() = %s;\n\n", dumpsw, dumpsw);
	else
	  printf("\nint\t(*dumpsw)() = nulldev;\n\n");
	printf("int\ttimezone = %d;\nint\tdaylight = %d;\n",
	timezone, daylight);
	{       
		register char   **pp;

		for (pp = junk; *pp; pp++)
			printf("struct\t%s;\n", *pp);
	}
	printf("char\tserialnum[32] = \"%.32s\";\n", serialnum);
	printf("char\tlicense[32] = \"%.32s\";\n", license);
	/*
		 * Driver configuration data
		 */
	for (dp = devconf; dp->d_name; dp++)
	   /* output device if ndev>0 or the dev is mm and not defined */
	   if (dp->d_ndev > 0 || dp->d_ndev == -999) {
		if (dp->d_flags & DEVAD) {
			if (dp->d_maxdev != 1 || !strcmp(dp->d_name,"vdu"))
				printf("int\tn%s = %d;\n", dp->d_name,
				dp->d_ndev);
			printf("char\t%saddr[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s0x%02x,", i&07 ? " " : "\n\t",
				dp->d_dev[i]);
			printf("\n};\n");
		}
		if (dp->d_nccb) {
			printf("struct ccb *%sccb = &ccb[%d];\n", dp->d_name,
			nccb);
			nccb += dp->d_ndev * dp->d_nccb;
		}
		if (dp->d_flags & DIOSAD) {
			printf("int\t%sdios[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s0x%02x,", i&07 ? " " : "\n\t",
				dp->d_channel.d_dios[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & SPEEDS) {
			printf("char\t%srate[][4] = {", dp->d_name);
			for (i = 0; i < nspeeds; i++)
				printf("%sB%d,", i&03 ? " " : "\n\t",
				speeds[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & CNTLAD){
			printf("char\t%scntl[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s0x%02x,", i&07 ? " " : "\n\t",
				dp->d_cntl[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & SELCHAD){
			printf("int\t%sselch[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s0x%02x,", i&07 ? " " : "\n\t",
				dp->d_channel.d_selch[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & TYPE){
			printf("char\t%stype[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s%d,", i&07 ? " " : "\n\t",
				dp->d_disc.d_type[i]);
			printf("\n};\n");
			for( i = 0; i < dp->d_ndev; i++ )
				spindle[i] = devmap[dp->d_dev[i]];

			printf("char\t%sspindle[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s%d,", i&07 ? " " : "\n\t",
				spindle[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & SIZE)
			printf("int\t%ssize = %d;\n", dp->d_name, dp->d_size);
		if (dp->d_flags & MAP) {
			printf("int\t%smap[] = {", dp->d_name);
			for (i = 0; i < 16; i++)
				printf("%s%8d,", i&01 ? "\t" : "\n\t",
				dp->d_map[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & PICT){
			printf("char\t%smap[] = {", dp->d_name);
			for (i = 0; i < dp->d_ndev; i++)
				printf("%s%d,", i&07 ? " " : "\n\t",
				dp->d_map[i]);
			printf("\n};\n");
		}
		if (dp->d_flags & LAYOUT) {
			printf("int\t%slayout[][16] = {", dp->d_name);
			for (ii = 0; ii < nlayout; ii++){
				printf("\n{");
				for (i = 0; i < 16; i++)
					printf("%s%8d,", i&01 ? "\t" : "\n\t",
					maplist[ii][i]);
				printf("\n},\n");
			}
			printf("\n};\n");
		}
	}

	/*
	 * diosaddr[] = dios address table, indexed by dios minor device
	 */
	printf("\nshort\tdiosaddr[] = {\n\t");
	for (i = 0; i < ndios; ++i)
		printf("0x%03x, ", diosaddr[i]);
	printf("\n};\n");

	/*
	 * file locking hook
	 */
	printf("\nstruct\tlocklist locklist[NFLOCKS];\n");

#ifdef  CDEVMAJMAP
	/*
	 * cdevmajmap[] = table of major device numbers,
	 * indexed by device address
	 */
	printf("\nchar\tcdevmajmap[] = {");
	for (i = 0; i < ndevs; i++) {
		if (i & 017)
			putchar (' ');
		else
			printf ("\n /* %3x */ ", i);
		printf("%2d,", cdevmajmap[i] & 0xff);
	}
	printf("\n};\n");
#endif  CDEVMAJMAP

	/*
		 * devmap[] = table of minor device numbers, indexed by device address
		 */
	printf("\nchar\tdevmap[] = {");
	for (i = 0; i < ndevs; i++) {
		if (i & 017)
			putchar (' ');
		else
			printf ("\n /* %3x */ ", i);
		printf("%2d,", devmap[i] & 0xff);
	}
	printf("\n};\n");

	/*
		 * devint[]
		 */
	printf("\nchar\tdevint[] = {");
	for (i = 0; i < ndevs; i++) {
		if (i & 017)
			putchar (' ');
		else
			printf ("\n /* %3x */ ", i);
		printf("%2d,", devint[i] * sizeof handler[0]);
	}
	printf("\n};\n");

	/*
		 * handler table
		 */
	{       
		register char   *sep = "int\t";

		for (i = 0; i < nhandler; i++) {
			printf("%s%s()", sep, handler[i]);
			if (i%6 == 5)
				sep = ";\nint\t";
			else
				sep = ", ";
		}
	}
	printf(";\n\nint\t(*handler[])() = {\n");
	for (i = 0; i < nhandler; i++)
		printf("\t%s,\n", handler[i]);
	printf("};\n");
}

/*
 * Make mkdev.sh file
 */
putmfile(dp,major,flag)
register struct devconf *dp;
int major,flag;
{
#ifdef  MAKEDEV
	register int minor;
	int type;

		if (dp->d_ndev <= 0)
			return;
		type = dp->d_flags;
		if (type & PICT) {
			if (flag & BDEV){
				for (minor = 0; minor < dp->d_ndev * NMAP; ++minor) {
					fprintf (mfileno,"/etc/mknod /dev/%s%c b %d %d\n",
					discname[minor >> 3], "abcdefg "[minor & 7],
					major, minor);
				}
			} else {
				for (minor = 0; minor < dp->d_ndev * NMAP; ++minor) {
					fprintf (mfileno,"/etc/mknod /dev/r%s%c c %d %d\n",
					discname[minor >> 3], "abcdefg "[minor & 7],
					major, minor);
				}
			}
		}
		if( (!strcmp(dp->d_name,"vdu")) && type &CDEV ){
			for (minor = 0; minor < dp->d_ndev; ++minor) {
				fprintf (mfileno,"/etc/mknod /dev/tty%d c %d %d\n",
				minor, major, minor);
			}
		}

		if( (!strncmp(dp->d_name, "mt", 2)) ){
			int num = 0;
			if(!strcmp(dp->d_name, "mta")) num = 4;
			if(!strcmp(dp->d_name, "mtb")) num = 8;

			if (flag & BDEV){
				for (minor = 0; minor < dp->d_ndev; ++minor) {
					fprintf (mfileno,"/etc/mknod /dev/mt%d b %d %d\n",
					num+minor, major, minor);
				}
				for (minor = 0; minor < dp->d_ndev; ++minor) {
					fprintf (mfileno,"/etc/mknod /dev/mmt%d b %d %d\n",
					num+minor, major, minor+4);
				}
			} else {
				for (minor = 0; minor < dp->d_ndev; ++minor) {
					fprintf (mfileno,"/etc/mknod /dev/rmt%d c %d %d\n",
					num+minor, major, minor);
				}
				for (minor = 0; minor < dp->d_ndev; ++minor) {
					fprintf (mfileno,"/etc/mknod /dev/rmmt%d c %d %d\n",
					num+minor, major, minor+4);
				}
			}
		}
#endif  MAKEDEV
}

/*
 * Find entry in keyword table
 * It had better be there!
 */
struct keyparm *
findkey(name)
register char   *name;
{
	register struct keyparm *kp;

	for (kp = keyparm; kp->k_name; kp++)
		if (!strcmp(kp->k_name, name))
			return(kp);
	abort();
	/* NOTREACHED */
}


/*
 * Make "param.s" file
 *
 * Contains     parameters for conditional assembly of UNIX kernel routines
 */
putsfile()
{
	putparm("M3200", (cpu == 3220) || (cpu == 3240));
	putparm("M3240", cpu == 3240);
	putparm("FPREGS", singlefloat == 1);
	putparm("DPREGS", doublefloat == 1);
	putparm("SPFPT", singlefloat == 2);
	putparm("DPFPT", doublefloat == 2);
	putparm("NCCB", nccb);
	putparm("WCS", findkey("wcs")->k_defined);
	putparm("LRA", (cpu == 3220) || (cpu == 3240) || findkey("lra")->k_defined);
	putparm("PIC", findkey("pic")->k_defined);
	putparm("NDEVS", ndevs);
}

/*
 * Equate assembly parameter name to value
 */
putparm(name, value)
char    *name;
{
	printf("%s\tequ\t%d\n", name, value);
}

/*
 * Check validity of input data and generate internal parameters
 */
mkconf()
{
	register struct devconf *dp;
	int mem;
	char p[50];

	/*
		 * Check that all required main keywords are defined
		 */
	{       
		register struct keyparm *kp;

		mem = 0;	/* have not seen any memory params yet */
		for (kp = keyparm; kp->k_name; kp++)
		{
			if ((kp->k_flags & K_REQ) && !kp->k_defined)
				error("%s not specified", kp->k_name);
			strcpy(p,kp->k_name);

			/*
			 * if the param is a memory parameter and it
			 * has been defined then increment mem variable.
			 * If no memory gets defined then an error is
			 * raised.
			 */
			if( (!strcmp(p,"memory") || !strcmp(p,"user-memory")
			    || !strcmp(p,"total-memory")) && kp->k_defined)
				mem++;

			/*
			 * if the param is for the shared memory driver
			 * "mm" check to see if its been defined by calling
			 * its routine.  In this configuration a declaration
			 * for mmmap must be given so that mem.c doesn't
			 * croak.
			 */
			if(!strcmp(p, "mm"))
				(*kp->k_fn)();
		}
		if(!mem)
			error("No memory parameter specified");
	}

	/*
		 * Check that special device assignments have been defined
		 */
	{       
		register struct spdev   *sp;

		for (sp = spdev; sp->s_name; sp++)
			if (sp->s_dev == 0)
				error("%s device not specified", sp->s_name);
	}

	/*
		 * Check at least one in each class of required device is specified
		 */
	{       
		register struct reqdev  *rq;

		for (rq = reqdev; rq->rq_names; rq++)
			if (rq->rq_def == 0)
				error("must specify at least one: %s",
				rq->rq_names);
	}

	/*
		 * Device processing
		 */
	for (dp = devconf; dp->d_name; dp++) {
		register struct devparm *pp;

		/*
				 * Check that required devices & parameters are defined
				 */
		if (dp->d_ndev == 0) {
			if (dp->d_flags & DREQ)
				error("%s not defined.  Required by %s",
				dp->d_name, dp->d_reqby);
			continue;
		}
		for (pp = devparm; pp->p_name; pp++)
			if ((dp->d_flags & pp->p_flag)
			    && ! (dp->d_defined & pp->p_flag)) {
				if (dp->d_flags & DIOSAD) {
					/* default to no dioses */
					getlist (&dp->d_ndev,
					&dp->d_channel.d_dios, 0);
				}
				else
					error("%s not specified for %s",
					pp->p_name, dp->d_name);
			}
		/*
				 * Remember interrupt information
				 */
		if (dp->d_flags & DEVAD) {
			register int    i;
			register int    int0, int1;
			int0 = mkhandler(dp->d_int[0]);
			if (dp->d_int[1])
				int1 = mkhandler(dp->d_int[1]);
			else
				int1 = SPURHANDLER;
			for (i = 0; i < dp->d_ndev; i++) {
				register int    dev = dp->d_dev[i];

				mkintvec(dev, int0);
				if( devmap[dev] == 0 ) devmap[dev] = i;
				if (   (dp->d_flags&(B_AND_CDEV)) == CDEV
				    && int1 != SPURHANDLER) {
					if (dev & 01)
						error("%s addresses must be even",
						dp->d_name);
					mkintvec(dev + 1, int1);
					devmap[dev + 1] = i;
				}
			}
			if (dp->d_flags & CNTLAD)
				mkcntrl(dp);
			if (dp->d_flags & SELCHAD)
				mkselch(dp);
			if (dp->d_flags & DIOSAD)
				mkdios(dp);
		}
	}
}

/*
 * Add a name to the interrupt handler table
 *
 * Return handler[] index
 */
mkhandler(name)
char    *name;
{
	handler[nhandler] = name;
	if (++nhandler > MAXHANDLER)
		error("Interrupt table overflow");
	return nhandler - 1;
}

/*
 * Add an address to the coltroller address table (if not already present)
 * Also record 'minor device number' and interrupt handler for controller address
 */
mkcntrl(dp)
register struct devconf *dp;
{
	register int    i;
	register int    address;
	register int    ind;

	for (ind = 0; ind < dp->d_ndev; ++ind) {
		/* see if we know about this cntrl already */
		address = dp->d_cntl[ind];
		for (i = 0; i < ncntrl; i++)
			if (cntrladdr[i] == address)
				goto next;
		cntrladdr[i] = address;
		if (i == 0)
			cntrloffs = mkhandler("msmcintr");
		devmap[address] = i;
		devint[address] = cntrloffs;
		++ncntrl;
next:          
		continue;
	}
}
mkselch(dp)
register struct devconf *dp;
{
	register int    i;
	register int    address;
	register int    ind;


	for (ind = 0; ind < dp->d_ndev; ++ind) {
		/* see if we know about this selch already */
		address = dp->d_channel.d_selch[ind];
		for (i = 0; i < nselch; i++)
			if (selchaddr[i] == address)
				goto next;
		selchaddr[i] = address;
		devmap[address] = i;
		devint[address] = SELCHOFFS;
		++nselch;
next:          
		continue;
	}
}

/*
 * Add an address to the dios address table (if not already present)
 * Also record 'minor device number' and interrupt handler for dios address
 */
mkdios(dp)
register struct devconf *dp;
{
	register int    i;
	register int    address;
	register int    ind;

	for (ind = 0; ind < dp->d_ndev; ++ind) {
		/* if address is 0, this is not a real dios,
				 * but a place holder, so don't put it in the table
				 */
		if (!(address = dp->d_channel.d_dios[ind]))
			continue;
		/* see if we know about this dios already */
		if (address & 0x3F)
			error ("%s: dios address least significant 6 bits must be 0",
			dp->d_name);
		if (!(dp->d_dev[ind] & 0x3F))
			error ("%s: least significant 6 address bits of device on dios can't be 0",
			dp->d_name);
		if ((address & 0x3C0) != (dp->d_dev[ind] & 0x3C0))
			error ("%s: dios and device upper bits don't match",
			dp->d_name);
		for (i = 0; i < ndios; i++)
			if (diosaddr[i] == address)
				goto next;
		diosaddr[i] = address;
		if (i == 0)
			diosoffs = mkhandler("diosint");
		devmap[address] = i;
		devint[address] = diosoffs;
		++ndios;
next:          
		continue;
	}
}

/*
 * Put interrupt handler table offset into interrupt table for given device
 *
 * Check that addresses are not reassigned
 */
mkintvec(address, offset)
{
	if (devint[address] == offset){
		printf("WARRING!! device address 0x%02x already used\n", address);
		return;
	} 
	else {
		if(devint[address] != 0)
			error("device address 0x%02x reassigned", address);
	}
	devint[address] = offset;
}

/*
 * Get the next complete configuration entry from the input file
 *
 * Returns 0 at end of file
 */
getconfig()
{
	register struct devconf         *dp;
	register struct keyparm         *kp;
	register struct spdev           *sp;

	/*
		 * First thing must be a keyword
		 */
	switch (token_type) {
	case ENDFILE:
		return(0);

	case NUMERIC:
		error("Extraneous numeric data");
		return(0);

	case KEYWORD:
		break;

	default:
		abort();
	}
	/*
		 * Check for device declaration
		 */
	for (dp = devconf; dp->d_name; dp++) {
		if (match(dp->d_name)) {
			token();
			while (getdevice(dp))
				;
			if (dp->d_req)
				dp->d_req->rq_def = 1;
			if (!(dp->d_flags & DEVAD))
				dp->d_ndev = 1;
			/*
						 * Some device names are also line discipline names
						 */
			getline(dp->d_name);
			return(1);
		}
	}
	/*
		 * Check for line discipline declaration
		 */
	if (getline(token_key)) {
		token();
		return(1);
	}
	/*
		 * Check for special device assignment
		 */
	for (sp = spdev; sp->s_name; sp++) {
		if (match(sp->s_name)) {
			token();
			if (token_type != KEYWORD)
				error("%s requires device name",
				sp->s_name);
			for (dp = devconf; dp->d_name; dp++) {
				if (match(dp->d_name)) {

					token();
					if (!(dp->d_flags & sp->s_flag))
						error("%s is wrong type of device",
						dp->d_name);
					sp->s_dev = dp;
					dp->d_flags |= DREQ;
					dp->d_reqby = sp->s_name;
					if (token_type != NUMERIC)
						error("missing minor device number");
					sp->s_minor = token_value;
					token();
					break;
				}
			}
			if (dp->d_name == 0)
				error("unknown device %s", token_key);
			return(1);
		}
	}
	/*
		 * Check for other main-level keywords
		 */
	for (kp = keyparm; kp->k_name; kp++) {
		if (match(kp->k_name)) {
			token();
			getkey(kp);
			kp->k_defined = 1;
			return(1);
		}
	}
	error("Unknown keyword %s", token_key);
	/* NOTREACHED */
}

/*
 * Process a single main-level non-device entry
 */
getkey(kp)
register struct keyparm *kp;
{
	if (kp->k_flags & K_NUM) {
		if (token_type != NUMERIC)
			error("%s requires a numeric parameter",
			kp->k_name);
		(*kp->k_fn)();
		token();
	} 
	else if (kp->k_flags & K_KEY) {
		if (token_type != KEYWORD)
			error("%s requires a keyword parameter",
			kp->k_name);
		(*kp->k_fn)();
		token();
	} 
	else if (kp->k_flags & K_STR) {
		(*kp->k_fn)();
		token();

	} 
	else if (kp->k_flags & K_NAME) {
		(*kp->k_fn)();
	}
	else if (kp->k_flags & K_UNET) {
		(*kp->k_fn)();
	}
}

/*
 * Main keyword parameter routines
 */
Pcpu()
{
	/* so far, there are basically only 3 types of cpus 832, 3220, 3240 */
	switch (token_value) {
	case 732: 
	case 832:
		cpu = 832;
		break;

	case 3220:
		cpu = 3220;
		break;

	case 3210: 
	case 3230: 
	case 3240:
	case 3250:
		cpu = 3240;
		break;

	default:
		error("Unknown CPU model %d", token_value);
	}
}

Pmemory()
{
	if (token_value < 128*1024)
		error("Sorry, not enough memory to run UNIX");
	if ( cpu == 3240 ){
		if (token_value > 16*1024*1024-65536)
			error("Sorry, a maximumum of 15936K memory currently supported");
	} else {
		if (token_value > 15*65536 )
			error("Sorry, a maximumum of 960K memory currently supported");
	}
	memory = token_value;
}

Ptotmem()
{
	if (token_value < 128*1024)
		error("Sorry, not enough memory to run UNIX");
	if ( cpu == 3240 ){
		if (token_value > 16*1024*1024-65536)
			error("Sorry, a maximumum of 15936K memory currently supported");
	} else {
		if (token_value > 15*65536 )
			error("Sorry, a maximumum of 960K memory currently supported");
	}
	totmem = token_value;
}

Pfloat()
{
	if (match("hard"))
		singlefloat = 1;
	else if (match("soft"))
		singlefloat = 2;
	else
		error("'hard' or 'soft' expected");
}

Pdfloat()
{
	if (match("hard"))
		doublefloat = 1;
	else if (match("soft"))
		doublefloat = 2;
	else
		error("'hard' or 'soft' expected");
}

Pswaplo()
{
	swaplo = token_value;
}

Pnswap()
{
	nswap = token_value;
}

Pdump()
{
	if (match("mt"))
		dumpsw = "mtdump";
	else if (match("dsk"))
		dumpsw = "dskdump";
	else if (match("none"))
		dumpsw = "none";
	else
		error("%s not a dump device", token_key);
}

Pndevs()
{
	switch (ndevs = token_value) {
	case 256:
	case 512:
	case 1024:
		return;
	}
	error("ndevs must be 256, 512, or 1024");
}

Pserial()
{
	strcpy (serialnum, token_key);
}

Plicense()
{
	strcpy (license, token_key);
}

/*
 * This routine gets called when no mm device is given
 * in the conf file.  However in order to appease mem.c
 * a dummy mmmap must be given in c.c.  To do this ndev
 * is set to -999 and this will allow putcfile to output
 * a mmmap to c.c.
 */
Pmm()
{
	register struct devconf	*dp;

	for(dp=devconf; dp->d_name; dp++)
		if(!strcmp(dp->d_name, "mm"))
		{
			if(dp->d_defined & MAP) /* return if already defined */
				return;
			dp->d_defined |= MAP;
			dp->d_ndev = -999;
			dp->d_map = &zeromap;
		}
}


Punet()
{
	register struct devconf         *dp;
	register struct lineconf        *lp;

	for ( dp = devconf; dp->d_name; dp++) {
		if ((!strcmp(dp->d_name,"pts")) ||
		    (!strcmp(dp->d_name,"ptc")) ||
		    (!strcmp(dp->d_name,"up" )) ||
		    (!strcmp(dp->d_name,"uu" )) ||
		    (!strcmp(dp->d_name,"ud" )) ){
			dp->d_ndev++;
		}
	}
	for ( lp = lineconf; lp->l_name; lp++) {
		if (!strcmp(lp->l_name,"upk") || !strcmp(lp->l_name,"fpk")) {
			lp->l_include++;
			lp->l_select++;
		}
	}
}
/*
 * Get next entry in input description of a device
 *
 * Check that parameter is permitted for this device type
 * Call routine to process parameter
 * Returns 0 for failure
 */
getdevice(dp)
register struct devconf *dp;
{
	register struct devparm         *pp;

	for (pp = devparm; pp->p_name; pp++) {
		if (match(pp->p_name)) {
			token();
			if (!(dp->d_flags & pp->p_flag))
				error("%s not permitted for %s", pp->p_name,
				dp->d_name);
			(*pp->p_fn)(dp);
			dp->d_defined |= pp->p_flag;
			return(1);
		}
	}
	return(0);
}

/*
 * Device parameter routines
 */

Pdev(dp)
register struct devconf *dp;
{

	getlist(&dp->d_ndev, &dp->d_dev, 1);
	if (dp->d_maxdev && dp->d_ndev > dp->d_maxdev)
		error("Too many device addresses for %s", dp->d_name);
	chkdevaddr (dp->d_dev, dp->d_ndev);
}

Pcntl(dp)
register struct devconf *dp;
{
	int     ncntl;
	getlist(&ncntl, &dp->d_cntl, 1);
	if (ncntl > dp->d_ndev)
		error("Too many controler addresses for %s", dp->d_name);
	if (ncntl < dp->d_ndev)
		error("%s must have as many controler entries as devices", dp->d_name);
	chkdevaddr (dp->d_cntl, ncntl);
}

Pselch(dp)
register struct devconf *dp;
{
	int     ndevselch;
	getlist(&ndevselch, &dp->d_channel.d_selch, 1);
	if (ndevselch > dp->d_ndev)
		error("Too many selch addresses for %s", dp->d_name);
	if (ndevselch < dp->d_ndev)
		error("%s must have as many selch entries as devices", dp->d_name);
	chkdevaddr (dp->d_channel.d_selch, ndevselch);
}

Pdios(dp)
register struct devconf *dp;
{
	int ndevdios;

	getlist(&ndevdios, &dp->d_channel.d_dios, 1);
	if (ndevdios > dp->d_ndev)
		error("Too many dios addresses for %s", dp->d_name);
	if (ndevdios < dp->d_ndev)
		error("%s must have as many dios entries as devices", dp->d_name);
	chkdevaddr (dp->d_channel.d_dios, ndevdios);
}

Psize(dp)
register struct devconf *dp;
{
	register int    size;

	size = getnum();
	if (!strcmp(dp->d_name, "dsk")) {
		switch (size) {
		case 10: 
		case 10*1024*1024:
			dp->d_disc.d_size = 1;
			return;
		case 2: 
		case 2*1024*1024:
			dp->d_disc.d_size = 0;
			return;
		}
	} 
	else {
		if ( !strcmp(dp->d_name, "ud1" ) ) {
			switch (size) {
			case 67: 
			case 67*1024*1024:
				dp->d_disc.d_size = 0;
				return;
			case 256: 
			case 256*1024*1024:
				dp->d_disc.d_size = 1;
				return;
			}
		}
	}
	error("incorrect size for %s", dp->d_name);
}
Ptype(dp)
register struct devconf *dp;
{
	int     i;
	int     ntype = MAXDTYPE;
	getlist(&dp->d_ndev, &dp->d_disc.d_type, 0);
	if ( !strcmp(dp->d_name, "msm")){
		for ( i=0; i < dp->d_ndev; i++ ){
			if ( match( "d67" ) || match( "D67" ) ){
				dp->d_disc.d_type[i] = 0;
				goto next;
			}
			if ( match( "d256" ) || match( "D256" ) ){
				dp->d_disc.d_type[i] = 1;
				goto next;
			}
			if ( match( "c13r" ) || match( "C13R" ) ||
			    match( "C13r" ) || match( "c13R" ) ){
				dp->d_disc.d_type[i] = 2;
				goto next;
			}
			if( match( "c13f" ) || match( "C13F" ) ||
			    match( "C13f" ) || match( "c13F" ) ){
				dp->d_disc.d_type[i] = 3;
				goto next;
			}
			if ( match( "c40f" ) || match( "C40F" ) ||
			    match( "C40f" ) || match( "c40F" ) ){
				dp->d_disc.d_type[i] = 4;
				goto next;
			}
			if ( match( "c67f" ) || match( "C67F" ) ||
			    match( "C67f" ) || match( "c67F" ) ){
				dp->d_disc.d_type[i] = 5;
				goto next;
			}
			if ( match( "udd" ) || match( "UDD" ) ){
				dp->d_disc.d_type[i] = 6;
				goto next;
			}
			error("incorrect type for %s %s", dp->d_name,token_key);
			break;
next:                   
			token();
		}
		return;
	}
}

Pmap(dp)
register struct devconf *dp;
{
	int	nmap;

	getlist(&nmap, &dp->d_map, 1);
	if (nmap != 16)
		error("%s map must have 16 entries", dp->d_name);
}

Ppict(dp)
register struct devconf *dp;
{
	int     nmap = MAXMAP;
	int     i,ii;
	int     j=0;
	getlist(&nmap, &dp->d_map, 0);
	for ( ii=0; ii<dp->d_ndev; ii++ ){
		for (i=0; i<=nlayout; i++){
			if ( !strcmp(mapname[i],token_key) ) break;
		}
		if ( !(i < nlayout) )
			error("%s not defined in layout",token_key);
		dp->d_map[ii] = i;
		token();
		j++;
	}
	if (j != dp->d_ndev)
		error("%s map must have %d entries", dp->d_name,dp->d_ndev);
}
Playout()
{
	int     layout,i;

	for (i=0; i<nlayout; i++){
		if( !strcmp(mapname[i],token_key) ){
			error("%s already used as layout name",mapname[i]);
		}
	}
	strcpy(&mapname[i],token_key);
	nlayout++;
	token();
	getlist(&layout, &maplist[i], 1);
	if (layout != 16)
		error("%s layout must have 16 entries", token_key);
}
Pname(dp)
register struct devconf *dp;
{
	int     name=0,ii,i;

	for (i=0; i<dp->d_ndev; i++){
		for ( ii=0; ii<name; ii++ ){
			if( !strcmp(discname[i],token_key) ){
				error("%s already used as disc name",discname[i]);
			}
		}
		strcpy(&discname[i],token_key);
		name++;
		token();
	}
	if (name != dp->d_ndev)
		error("name must have %d entries", dp->d_ndev);
}

Pspeeds()
{
	getlist(&nspeeds, &speeds, 1);
	if (nspeeds & 03)
		error("must define 4 speeds per port");
}

Ptimezone()
{
	timezone = token_value * 60;
}

Pdaylight()
{
	daylight = token_value;
}

/*
 * If key is name of a line discipline, mark it for inclusion
 *
 * Return 0 if not found
 * make special note of "mx" discipline, to prevent loading of fakemx
 */
getline(key)
char	*key;
{
	register struct lineconf	*lp;

	for (lp = lineconf; lp->l_name; lp++) {
		if (!strcmp(key, lp->l_name)) {
			lp->l_include = 1;
			if (!strcmp(key, "mx"))
				mx = 1;
			return(1);
		}
	}
	return(0);
}

/*
 * Get a list of (one or more) input numbers
 *
 * Return indirectly in *length the number of addresses read, and in *list
 * a pointer to the array of integer values read
 * The space for the list is allocated using sbrk
 */
getlist(length, list, inpflg)
int	*length;
int	**list;
int     inpflg;         /* 1 if from input, 0 if zero-fill */
{
	extern int	end[];
	static int	*avail = end;		/* top of available memory */
	static int	*next = end;		/* next available word */
	register int	len = 0;

	*list = next;
	if (!inpflg) {
		/* fill out a list with zeroes */
		len = *length;
		while (--len >= 0) {
			if (next >= avail) {
				avail += 64;
				if (brk(avail) != 0)
					error("Out of memory");
			}
			*next++ = 0;
		}
		return;
	}
	if (token_type != NUMERIC)
		error("Numeric item expected");
	while (token_type == NUMERIC) {
		if (next >= avail) {
			avail += 64;
			if (brk(avail) != 0)
				error("Out of memory");
		}
		*next++ = token_value;
		len++;
		token();
	}
	*length = len;
}

/*
 * Get a number from the input
 *
 * Return its value
 * Check that not more than one number is supplied
 */
getnum()
{
	register	retval;

	if (token_type != NUMERIC)
		error("Numeric item required");
	retval = token_value;
	token();
	if (token_type == NUMERIC)
		error("Only one numeric item expected");
	return(retval);
}

int tokenroom;  /* how many characters we can take */
char *tokenptr; /* where to put input characters */

/*
 * Get the next input token
 *
 *	Skip white space
 *	Skip comments (from '*' to end of line)
 *	Read the next token (non-white-space string)
 *	Set token_type to:
 *		ENDFILE		- no more input
 *		NUMERIC		- integer (may be preceded by 0 or 0x for octal
 *				 or hex, followed by K or M for kilo- or mega-)
 *                               may be negative.
 *				- store value in token_value
 *		KEYWORD		- anything else
 *				- store keyword string in token_key
 */
token()
{
	register int	c;
	int sign;

	tokenroom = sizeof(token_key);
	tokenptr = token_key;
	skipspace();
	sign = 1;
	if (feof(stdin) || (c = getchar()) == EOF) {
		/* no more input */
		token_type = ENDFILE;
		return;
	}
	if (c == '-') {
		sign = -1;
		putp(c);
		if (feof(stdin) || (c = getchar()) == EOF)
			error ("Malformed number");
	}
	if (isdigit(c)) {	/* a number */
		register int	accum = 0, base = 10;

		if (c == '0') {
			putp(c);
			c = getchar();
			if (c == 'x' || c == 'X') {
				putp(c);
				c = getchar();
				base = 16;
			} 
			else
				base = 8;
		}
		for (;;) {
			int num;
			if (isdigit(c))
				num = c - '0';
			else if (c >= 'a' && c <= 'f')
				num = c - ('a'-10);
			else if (c >= 'A' && c <= 'F')
				num = c - ('A'-10);
			else
				break;
			if (num >= base)
				goto string;
			accum *= base;
			accum += num;
			putp(c);
			c = getchar();
		}
		if (c == 'k' || c == 'K') {
			putp(c);
			c = getchar();
			accum *= 1024;
		} 
		else if (c == 'm' || c == 'M') {
			putp(c);
			c = getchar();
			accum *= 1024*1024;
		} 
		else if (!isspace(c))
			goto string;
		ungetc(c, stdin);
		token_type = NUMERIC;
		token_value = sign < 0 ? -accum : accum;
	} 
	else {		/* a keyword */
string:
		while (!(isspace(c)) && c != EOF) {
			putp (c);
			c = getchar();
		}
		token_type = KEYWORD;
		ungetc(c, stdin);
	}
	putp ('\0');
}

putp (chr)
int chr;
{
	*tokenptr++ = chr;
	if (--tokenroom <= 0)
		error("Impossibly long word");
}


/*
 * Skip space and comments on the input file
 * Also keep track of line numbers
 */
skipspace()
{
	register int	c;

	for (;;) {
		c = getchar();
		if (c == '*')
			while ((c = getchar()) != '\n')
				;
		if (c == '\n')
			linenumber++;
		else if (!(isspace(c)))
			break;
	}
	if (c != EOF)
		ungetc(c, stdin);
}

chkdevaddr (list, num)
int *list;
int num;
{
	for (; --num >= 0; ++list) {
		if (*list > ndevs)
			error ("device address larger than ndevs");
		if (highestdev < *list)
			highestdev = *list;
	}
}

/*
 * Print error message and exit
 */
/* VARARGS 1 */
error(msg, a1, a2)
char *msg;
char *a1, *a2;
{
	if (linenumber != 0)
		fprintf(stderr, "Line %d: ", linenumber);
	fprintf(stderr, msg, a1, a2);
	fprintf(stderr, "\n");
	exit(1);

}
