%union {
	struct	expr *expr;
	char	*str;
	int	val;
}

%token	ADJUST			/* adjust a configuration parameter value */
%token	ARCH_SYS		/* defines arch_type (sys_config) */
%token	ARCHIVE_SYS		/* defines product archive (sys_config) */
%token	ASSERT			/* assert configuration parameters constraint */
%token	BINARIES_MOD		/* adds to %BINARIES (mod_config) */
%token	BLOCK_MOD		/* cur->d_block (mod_config) */
%token	BOUND			/* bound configuration parameter */
%token	BUS_MOD			/* cur->d_bus (mod_config) */
%token	CHARACTER_MOD		/* cur->d_chararter (mod_config) */
%token	CHECKFOR_MOD		/* check for a mod_config */
%token	CHECKFOR_SYS		/* check for a layered product (sys_config) */
%token	CONF			/* add to conf.c */
%token	CONFH			/* add to config.h */
%token	DECLARE			/* declare a configuration parameter */
%token	DEFAULT			/* define default configuration parameter */
				/* values */
%token	DESC_SYS		/* prints configuration descriptions */
				/* (sys_config) */
%token	ELSE			/* ifTYP () {} [ elsifTYP {} ] [ else {} ] } */
%token	ELSIFCONFIG		/* ifTYP () {} [ elsifconfig {} ] ... */
%token	ELSIFDEF		/* ifTYP () {} [ elsifdef {} ] ... */
%token	ELSIFNCONFIG		/* ifTYP () {} [ elsifnconfig {} ] ... */
%token	ELSIFNDEF		/* ifTYP () {} [ elsifndef {} ] ... */
%token	ELSIFNPRODUCT		/* ifTYP () {} [ elsifnproduct {} ] ... */
%token	ELSIFPRODUCT		/* ifTYP () {} [ elsifproduct {} ] ... */
%token	ENVIRONMENT_SYS		/* defines the site environment (sys_config) */
%token	FATAL			/* fatal configuration situation encountered */
%token	FILESYSTEM_MOD		/* type FILESYSTEM (mod_config) */
%token	FUNCTIONS_MOD		/* device driver entry points (mod_config) */
%token  HEADERS_MOD		/* adds to %HFILES (mod_config) */
%token	IFCONFIG		/* ifconfig () {} ... */
%token	IFDEF			/* ifdef () {} [ elsXXXX {} ] [ else {} ] } */
%token	IFNCONFIG		/* ifnconfig () {} ... */
%token	IFNDEF			/* ifndef () {} [ elsXXXX {} ] [ else {} ] } */
%token	IFNPRODUCT		/* ifnproduct () {} ... */
%token	IFPRODUCT		/* ifproduct () {} ... */
%token	IMPORT_SYS		/* import a layered product (sys_config) */
%token	INCLUDE_SYS		/* nest a new config (sys_config) */
%token	INITS			/* add init function */
%token	MKFILE_SYS		/* name of skeleton makefile (sys_config) */
%token	MKOPTS			/* add to %MKOPTS */
%token	MODULES			/* either S_pmod_list or m->mod_subm */
%token	NEW_DEFAULT		/* define new default configuration parameter */
				/* values */
%token	NEXT_MOD		/* end this mod_config start another */
%token	ON			/* device spec punctuation */
%token	ON_BUS			/* cur->d_on_bus */
%token	NSEGS			/* number of device segments */
%token	PREFIX_MOD		/* cur->d_prefix (mod_config) */
%token	PRINT			/* print arguments */
%token	PRULE_SYS		/* defines makefile rule */
%token	PSEUDO_DEVICE_MOD	/* pseudo_dev prefix (mod_config) */
%token	PSEUDO_DEVICE_SYS	/* pseudo_dev prefix (sys_config) */
%token	SEGALIGN		/* device segments alignment */
%token	SEGSIZE			/* device segments size */
%token	SET			/* set (absolutely) a configuration parameter */
%token	SPACE_MOD		/* add to %SPACE (mod_config) */
%token	STREAMD_MOD		/* type STREAMD (mod_config) */
%token	STREAMM_MOD		/* type STREAMM (mod_config) */
%token	STUBS_MOD		/* add to stubs (mod_config) */
%token	SYSCALL_MOD		/* declare a syscall table (mod_config) */
%token	UNIT			/* device spec punctuation */
%token	VERSION_SYS		/* versions (sys_config) */

%token	MAXUNIT_MOD		/* max number of units (mod_config) */
%token	MINUNIT_MOD		/* min number of units (mod_config) */

/* punctuation definitions */
%token	TRACE
%token	YYDEBUG
%token	COMMA
%token	SEMICOLON
%token	OPENP
%token	CLOSEP
%token	OPENC
%token	CLOSEC
%token	OPENB
%token	CLOSEB
%token	LESS
%token	LESSEQ
%token	EQ
%token	GTREQ
%token	GTR
%token	PLUS
%token	MULT
%token	SLASH
%token	MINUS
%token	ELLIPSIS

%token	<str>	ID			/* string token */
%token	<val>	NUMBER			/* number token */
%type	<str>	Save_id
%type	<str>	Module_or_stub
%type	<val>	One_int
%type	<val>	Relop
%type	<expr>	Expression
%type	<expr>	Actual_expr
%type	<expr>	Term
%type	<expr>	Factor
%type	<val>	Addop
%type	<val>	Mulop

%{

/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: config.y 1.34 90/05/21 $"

#include "stdio.h"
#include "config.h"
#include "expression.h"
#include "symbols.h"				/* symbol table functions */
#include "allocate.h"				/* reuseable_to_tail(),.... */
						/* free_list(), perm_str(), */
						/* new_expr() */

/*
 *  Conditional (IFtype....ELSIFtype....ELSE) nesting definitions:
 *
 *	At any given time, the conditional stack top contains
 *	either:
 *	  COND_TRUE:  The current conditional evaluated to TRUE.
 *	  COND_FALSE: The current conditional evaluated to FALSE.
 *
 *	All elements below the conditional stack top are either:
 *	  COND_TRUE:
 *		The previous nested conditional evaluated to TRUE.
 *	  COND_NEST_TRUE:
 *		The previous nested conditional evaluated to TRUE,
 *		and one of the previous conditions (at the current
 *		stack level) also evaluated to TRUE.
 *	  COND_NEST_FALSE:
 *		The previous nested conditional evaluated to FALSE.
 */
#define	COND_FALSE	'\000'			/* Conditional FALSE */
#define	COND_TRUE	'\001'			/* Conditional TRUE */
#define	COND_NEST_FALSE '\002'			/* Conditional nested FALSE */
#define	COND_NEST_TRUE	'\003'			/* Conditional nested TRUE */
#define	MAX_COND_NEST	32			/* Max IFtype ELSE nest depth */

#define	MAX_STRLEN	255			/* Max string length */

#define	PARSE_SYS	0			/* sys_config parse mode */
#define	PARSE_MOD	1			/* mod_config parse mode */

extern char		*malloc();
extern char		*realloc();
extern struct symbol	*reference_par();	/* reference parameter */
extern void		*memcpy();
extern char		*strcpy();
extern void		check_arch_type();	/* check architecture type */
extern void		do_mod_imports();	/* do mod_config imports */
extern void		do_sys_imports();	/* do sys_config imports */
extern boolean		import_mod();		/* intra-product import */
extern boolean		import_sys();		/* inter-product import */
extern boolean		include_sys();		/* include file in sys_config */
extern boolean		end_sys();		/* end of sys_config file */
extern boolean		ifconfig();		/* if configuration ... */
extern boolean		ifnconfig();		/* if not configuration ... */
extern boolean		ifdef();		/* if parameter defined ... */
extern boolean		ifndef();		/* if parameter undefined ... */
extern boolean		ifproduct();		/* if product present ... */
extern boolean		ifnproduct();		/* if product not present ... */

int			yylineno;		/* current line number */
boolean 		desc_flag;		/* description seen flag */
extern struct		device *cur;		/* current device */

static boolean		(*iffuncptr)();		/* ptr to fn returning boolean*/
static struct expr	*binary_op();		/* binary operand expression */
static struct expr	*unary_op();		/* unary operand expression */
static char		*limited_strncpy();	/* strncpy() with error check */
static char		*divert();		/* gather until delimiter */
static void		expression_syntax_error_recovery();
static struct list_head	list;			/* strings list */
static int		list_count;		/* #of options in 'opt' */
static char		conditional_stack[MAX_COND_NEST];
static int		cond_sp;		/* conditional stack level */
static int		parameter_visibility;	/* parameter "visibility */
static struct		hdc hdc;
static char		**next_modulep;		/* where to ret. NEXT=modname */
static boolean		trace_flag;		/* trace flag */
static boolean		select_flag;		/* select parameter flag */
static boolean		configured_module;	/* sub-module is configured */
static boolean		expression_error;	/* expression error flag */
static boolean		modules_set;		/* parsing module set flag */
static boolean		mandatory_default;	/* new_default directive flag */
static boolean		mandatory_import;	/* mandatory import flag */
static boolean		sys_import;		/* {sys,mod}_config_import */
static boolean		mod_import_ok;		/* "checkfor" module (not) ok */
static boolean		trivial_imports;	/* imports already parsed flag*/
static char		string1[MAX_STRLEN+1];	/* one string buffer */
static char		string2[MAX_STRLEN+1];	/* second string buffer */
static char		string3[MAX_STRLEN+1];	/* third string buffer */
static struct bounds {
	struct expr *lowptr;			/* ptr to L.B. if specified */
	struct expr *highptr;			/* ptr to H.B. if specified */
} bounds;

%}
%%
Configuration:
	Zero_or_more_semicolons Actual_configuration
	;

Actual_configuration:
	Mod_imports Many_specs
	|
	Non_empty_spec SEMICOLON Many_specs
	|
	/* lambda */
	;

Mod_imports:
	CHECKFOR_MOD Mod_soft_import Imports Do_imports SEMICOLON
	;

Many_specs:
	Many_specs Terminated_spec
	|
	/* lambda */
	;

Terminated_spec:
	Non_empty_spec SEMICOLON	/* {sys,mod,xxx}_config directives */
	|
	SEMICOLON			/* blank lines */
	;

Non_empty_spec:
	Sys_config_spec			/* sys_config directives */
	|
	Mod_config_spec			/* mod_config directives */
	|
	Xxx_config_spec			/* xxx_config directives */
	|
	error				/* on error skip line */
	;

Sys_config_spec:
	ARCH_SYS Save_id
	{
		if (conditional_stack[cond_sp] && Config) {
			extern char *arch_type;	/* Architecture type */

			check_arch_type( $2 );
			arch_type = $2;
			/*
			 * add root bus device to configuration
			 */
			hdc.dev_spec = $2;
			hdc.dev_val = 0;
			hdc.type_spec = $2;
			hdc.type_val = 0;
			hdc.unit_spec = 0;
			new_connect(&hdc, TRUE);
		}
	} 
	|
	VERSION_SYS Version_info
	|
	DESC_SYS One_str
	{
		if (conditional_stack[cond_sp]) {
			desc_flag = TRUE;
			fputs(string1, stdout);
			putchar('\n');
		}
	}
	|
	ENVIRONMENT_SYS Restricted_names One_str Unrestricted_names
	{
		if (conditional_stack[cond_sp])
			set_environment( string1 );
	}
	|
	ARCHIVE_SYS One_str
	{
		if (conditional_stack[cond_sp])
			define_archive( string1 );
	}
	|
	MKFILE_SYS Save_id
	{
		if (conditional_stack[cond_sp] && Config)
			(void)sprintf(Smk_file,"%s/%s", Mach_dir, $2);
	}
	|
	IMPORT_SYS Sys_mandatory_import Imports Do_imports
	|
	CHECKFOR_SYS Sys_soft_import Imports Do_imports
	|
	Connect_spec
	{ 
		if (conditional_stack[cond_sp] && Config)
			new_connect(&hdc, FALSE);
	}
	|
	PSEUDO_DEVICE_SYS Dev_spec
	{
		if (conditional_stack[cond_sp] && Config) {
			hdc.type_spec = (char *)NULL;
			hdc.type_val = 0;
			hdc.unit_spec = 0;
			new_connect(&hdc, FALSE);
		}
	}
	|
	PRULE_SYS Prule			/* %RULES */
	|
	INCLUDE_SYS One_str
	{
		/* new system configuration file spec */
		if (conditional_stack[cond_sp]) {
			if (include_sys(string1, yylineno, desc_flag,
					trace_flag)) {
				unput('\n');
			}
		}
	}
	;

Mod_config_spec:
	Dev_config_spec
	{	
		if (conditional_stack[cond_sp])
			device_spec = TRUE; 
	}
	| 
	Mod_config_stmts
	;

Dev_config_spec:
	BUS_MOD Strs
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12)) {
				printf("BUS ");
				lop( list.l_head );
			}
			cur->d_type |= T_BUSD;
			add_bus( list.l_head, list_count );
		}
	}
	|
	ON_BUS Strs
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12)) {
				printf("ON_BUS ");
				lop( list.l_head ); 
			}
			add_on_bus( list.l_head, list_count );
		}
	}
	|
	MAXUNIT_MOD One_int
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("MAXUNIT %d\n", $2);
			cur->d_maxunit = $2;
		}
	}
	|
	MINUNIT_MOD One_int
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("MINUNIT %d\n", $2);
			cur->d_minunit = $2;
		}
	}
	|
	BLOCK_MOD One_int
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("BLOCK %d\n", $2);
			cur->d_block = $2;
			cur->d_type |= T_BLOCK;
			add_bdev( cur );
		}
	}
	|
	CHARACTER_MOD One_int
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("CHARACTER %d\n", $2);
			cur->d_character = $2;
			cur->d_type |= T_CHAR;
			add_cdev ( cur );
		}
	}
	|
	STREAMM_MOD Strs
	{
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12)) {
				printf("STREAMM: ");
				lop( list.l_head );
			}
			cur->d_type |= T_STREAMM;
			add_streamm_aliases(list.l_head);
		}
	}
	|
	STREAMM_MOD
	{
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("STREAMM\n");
			cur->d_type |= T_STREAMM;
		}
	}
	|
	STREAMD_MOD One_int
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("STREAMD %d\n", $2);
			cur->d_character = $2;
			cur->d_type |= T_CHAR| T_STREAMD;
			add_cdev ( cur );
		}
	}
	|
	PSEUDO_DEVICE_MOD 
	{
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("PSEUDO_DEVICE\n");
			cur->d_type |= T_PSEUDO;
		}
	}
	|
	FILESYSTEM_MOD One_int
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("FILESYSTEM %d\n", $2);
			cur->d_character = $2;
			cur->d_type |= T_FS;
			add_fs ( cur );
		}
	}
	;

Mod_config_stmts:
	FUNCTIONS_MOD Strs
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12)) {
				printf("FUNCTIONS ");
				lop( list.l_head );
			}
			dev_entry(list.l_head);
		}
	}
	|
	PREFIX_MOD One_str
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12))
				printf("PREFIX %s\n", string1);
			(void)limited_strncpy(cur->d_prefix, string1,
						MAX_DEVNAMELEN,
						"Device prefix too long.");
			to_upper(strcpy(cur->d_uprefix, cur->d_prefix));
		}
	}
	|
	HEADERS_MOD Options
	{
		/*
		 *  Headers are installed, even when the module is NOT
		 *  configured....
		 */
		if (conditional_stack[cond_sp] && Config)
			add_headers( list );
	}
	|
	BINARIES_MOD Options
	{
		if (conditional_stack[cond_sp] && Config) {
			if (configured_module)
				add_binaries( list );
			else
				free_list( list );
		}
	}
	|
	SPACE_MOD Options
	{
		if (conditional_stack[cond_sp] && Config) {
			if (configured_module)
				add_spacefiles( list );
			else
				add_bfiles_space( list );
		}
	}
	|
	Stubs
	|
	SYSCALL_MOD One_int
	{
		if (conditional_stack[cond_sp] && Config && configured_module)
			add_syscall( $2 );
	}
	|
	NEXT_MOD One_str SEMICOLON
	{
		if (cond_sp > 0)
			yyerror("NEXT cannot be nested within IFtype...ELSE");
		else {					/* new device spec */
			*next_modulep = perm_str(string1);
			YYACCEPT;
			/* NOT REACHED */
		}
	}
	;

Xxx_config_spec:
	Modules
	|
	CONF OPENC SEMICOLON		/* divertions */
	{ 	
		char	*d;

		if (DEBUG_LEVEL(12))
			printf("conf\n");
		if ((d = divert( '}' )) != (char *)NULL)
			add_conf(d);
	}
	|
	CONFH OPENC SEMICOLON		/* divertions */
	{ 	
		char	*d;

		if (DEBUG_LEVEL(12))
			printf("confh\n");
		if ((d = divert( '}' )) != (char *)NULL)
			add_confh(d);
	}
	|
	MKOPTS OPENC SEMICOLON		/* divertions */
	{ 
		char	*d;

		if (DEBUG_LEVEL(12))
			printf("make options\n");
		if ((d = divert( '}' )) != (char *)NULL)
			add_mkopts(d);
	}
	|
	PRINT One_str
	{ 
		if (conditional_stack[cond_sp]) {
			fputs(string1, stdout);
			putchar('\n');
		}
	}
	|
	FATAL One_str
	{ 
		if (conditional_stack[cond_sp]) {
			fputs(string1, stdout);
			putchar('\n');
			fatal("config aborted by FATAL directive.\n");
			exit(1);
		}
	}
	|
	TRACE				/* trace toggle */
	{ 
		/* unconditional */
		trace_flag = !trace_flag;
	} 
	|
	YYDEBUG				/* yyparse debugging toggle */
	{
		/* unconditional */
		extern int yydebug;
		yydebug = !yydebug;
	}
	|
	INITS Strs
	{
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (DEBUG_LEVEL(12)) {
				printf("INITS ");
				lop( list.l_head );
			}
			add_init( list );
		}
	}
	|
	Sub_options_list
	|
	Param_spec			/* configuration parameters */
	|
	If_spec				/* config ifTYPE conditional */
	;

Version_info:
	OPENP Version_levels CLOSEP
	|
	OPENC Version_levels CLOSEC
	;

Version_levels:
	NUMBER
	{
		/* non-conditional */
		check_version($1, 0, 0);
	}
	|
	NUMBER COMMA NUMBER
	{
		/* non-conditional */
		check_version($1, $3, 0);
	}
	|
	NUMBER COMMA NUMBER COMMA NUMBER
	{
		/* non-conditional */
		check_version($1, $3, $5);
	}
	;

Modules:
	Modules_intro Modules_or_stubs
	;

Modules_intro:
	MODULES
	{
		modules_set = TRUE;
	}
	;

Stubs:
	Stubs_intro Modules_or_stubs
	;

Stubs_intro:
	STUBS_MOD
	{
		modules_set = FALSE;
	}
	;

Modules_or_stubs:
	OPENC Modules_or_stubs_set CLOSEC
	|
	OPENP Modules_or_stubs_set CLOSEP
	;

Modules_or_stubs_set:
	Zero_or_more_semicolons Modules_or_stubs_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Modules_or_stubs_list:
	Modules_or_stubs_list Semicolons Module_or_stub
	|
	Module_or_stub
	;

Module_or_stub:
	One_str_only Sub_options_list
	{
		if (conditional_stack[cond_sp]) {
			if (parse_mode == PARSE_SYS) {
				configure_module( string1 );
			} else if (Config) {
				/*
				 *  If a module, and configured, --or--
				 *  If a stub, and deconfigured, then....
				 */
				if (modules_set == configured_module)
					new_submodule( string1, modules_set );
				if (!modules_set)	/* remember stub */
					add_bfiles_stubs( string1 );
			}
		}
	}
	|
	One_str_only
	{
		if (conditional_stack[cond_sp]) {
			if (parse_mode == PARSE_SYS) {
				configure_module( string1 );
			} else if (Config) {
				/*
				 *  If a module, and configured, --or--
				 *  If a stub, and deconfigured, then....
				 */
				if (modules_set == configured_module)
					new_submodule( string1, modules_set );
				if (!modules_set)	/* remember stub */
					add_bfiles_stubs( string1 );
			}
		}
	}
	;

Sys_mandatory_import:
	/* lambda */
	{
		trivial_imports = TRUE;	  /* Assume imports already parsed */
		mandatory_import = TRUE;  /* This is a mandatory import */
		sys_import = TRUE;	  /* This is a sys_config import */
	}
	;

Sys_soft_import:
	/* lambda */
	{
		trivial_imports = TRUE;	  /* Assume imports already parsed */
		mandatory_import = FALSE; /* This is a "soft" import */
		sys_import = TRUE;	  /* This is a sys_config import */
	}
	;

Mod_soft_import:
	/* lambda */
	{
		trivial_imports = TRUE;	  /* Assume imports already parsed */
		mandatory_import = FALSE; /* This is a "soft" import */
		sys_import = FALSE;	  /* This is a mod_config import */
	}
	;

Do_imports:
	/* lambda */
	{
		if (!trivial_imports) {
			if (sys_import)
				do_sys_imports(yylineno, desc_flag, trace_flag);
			else
				do_mod_imports();
			YYACCEPT;
			/* NOT REACHED */
		}
	}
	;

Imports:
	OPENC Imports_set CLOSEC
	|
	OPENP Imports_set CLOSEP
	;

Imports_set:
	Zero_or_more_semicolons Imports_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Imports_list:
	Imports_list Semicolons Import
	|
	Import
	;

Import:
	One_str_only
	{
		if (cond_sp > 0) {
			yyerror(
			"IMPORT and CHECKFOR cannot be nested in IFtype...ELSE"
				);
		} else if (sys_import) {		/* sys_config import */
			if (import_sys(string1, mandatory_import))
				trivial_imports = FALSE;/* non-trivial import */
		} else if (mod_import_ok) {		/* mod_config import */
			if (import_mod(string1, mandatory_import))
				trivial_imports = FALSE;/* non-trivial import */
		} else {		/* invalidly placed mod_config import */
			yyerror("CHECKFOR allowed only as first directive");
		}
	}
	;

Strs:
	OPENC Opt_list_begin Str_list Zero_or_more_semicolons CLOSEC
	|
	OPENP Opt_list_begin Str_list Zero_or_more_semicolons CLOSEP
	;

Str_list:
	Str_list COMMA ID
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			reuseable_to_tail( &list, $3 );
			list_count++;
		}
	}
	|
	Str_list Semicolons ID
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			reuseable_to_tail( &list, $3 );
			list_count++;
		}
	}
	|
	ID
	{ 
		if (conditional_stack[cond_sp] && Config && configured_module) {
			reuseable_to_tail( &list, $1 );
			list_count++;
		}
	}
	;

Options:
	OPENC Opt_list_begin Opt_list Zero_or_more_semicolons CLOSEC
	|
	OPENP Opt_list_begin Opt_list Zero_or_more_semicolons CLOSEP
	;

Opt_list_begin:
	Zero_or_more_semicolons
	{
		list.l_head = list.l_last = (struct list *)NULL;
		list_count = 0;
	}
	;

Opt_list:
	Opt_list COMMA Opt_value
	{ 
		if (conditional_stack[cond_sp] && Config) {
			reuseable_to_tail( &list, string1 );
			list_count++;
		}
	}
	|
	Opt_list Semicolons Opt_value
	{ 
		if (conditional_stack[cond_sp] && Config) {
			reuseable_to_tail( &list, string1 );
			list_count++;
		}
	}
	|
	Opt_value
	{ 
		if (conditional_stack[cond_sp] && Config) {
			reuseable_to_tail( &list, string1 );
			list_count++;
		}
	}
	;

Opt_value:		/* WARNING: primary option returned in "string1" */
	One_str_only Sub_options_list
	|
	One_str_only
	;

Sub_options_list:	/* WARNING: cannot use "string1"; see */
			/*	    Opt_value and Module rules */	
	Sub_options_list Sub_options
	|
	Sub_options
	;

Sub_options:		/* WARNING: cannot use "string1": see */
			/*	    Opt_value and Module rules */
	SLASH Another_str EQ Third_str
	{
		if (conditional_stack[cond_sp] && Config && configured_module) {
			if (parse_mode == PARSE_SYS)
				add_gmop( string2, string3 );
			else
				add_lmop( string2, string3 );
		}
	}
	;

Connect_spec:
	Dev_spec ON Type_spec UNIT Unit_spec
	|
	Dev_spec ON Type_spec UNIT Unit_spec Segs_spec
	;

Dev_spec:
	Save_id NUMBER
	{				/* allocate file structure */
		hdc.dev_spec = $1;
		hdc.dev_val = $2;
	}
	|
	Save_id 
	{				/* allocate file structure */
		hdc.dev_spec = $1;
		hdc.dev_val = UNKNOWN;
	}
	;

Type_spec:
	Save_id NUMBER
	{				/* insert type in field */
		hdc.type_spec = $1;
		hdc.type_val = $2;
	}
	|
	Save_id 
	{				/* insert type in field */
		hdc.type_spec = $1;
		hdc.type_val = UNKNOWN;
	}
	;

Unit_spec:
	NUMBER
	{				/* insert unit in field */
		hdc.unit_spec = $1;
	}
	;

Segs_spec:
	Segsize Nsegs Segalign
	|
	Segsize Nsegs
	;

Segsize:
	SEGSIZE NUMBER
	{				/* insert segsize in field */
		hdc.segsize = $2;
	}
	;

Nsegs:
	NSEGS NUMBER
	{				/* insert nsegs in field */
		hdc.nsegs = $2;
	}
	;

Segalign:
	SEGALIGN NUMBER
	{				/* insert segalign in field */
		hdc.segalign = $2;
	}
	;

Prule:
	One_str_only OPENC Another_str CLOSEC
	{
		if (conditional_stack[cond_sp] && Config) {
			if (DEBUG_LEVEL(12))
				printf("%%RULE_%s\n", string1);
			set_def_rule(string1, string2);
		}
	}
	|
	One_str_only OPENC SEMICOLON
	{
		char	*p;

		if ((p = divert( '}' )) != (char *)NULL) {
			if (DEBUG_LEVEL(12))
				printf("%%RULE_%s\n", string1);
			set_def_rule(string1, p);
			if (DEBUG_LEVEL(12))
				printf("%%RULE done\n");
		}
	}
	;

Param_spec:
	Restricted_names Actual_param_spec Unrestricted_names
	;

Actual_param_spec:
	Declare_intro Declare
	|
	Declare_intro OPENC Declare_set CLOSEC
	|
	Default_intro Default
	|
	Default_intro OPENC Default_set CLOSEC
	|
	New_default_intro Default
	|
	New_default_intro OPENC Default_set CLOSEC
	|
	ADJUST Adjust
	|
	ADJUST OPENC Adjust_set CLOSEC
	|
	SET Set
	|
	SET OPENC Set_set CLOSEC
	|
	BOUND Bound
	|
	BOUND OPENC Bound_set CLOSEC
	|
	ASSERT Assert
	|
	ASSERT OPENC Assert_set CLOSEC
	;

Restricted_names:
	/* lambda */
	{
		BEGIN RESTRICTED_NAMES;
	}
	;

Unrestricted_names:
	/* lambda */
	{
		BEGIN INITIAL;
	}
	;

Declare_intro:
	DECLARE OPENB NUMBER CLOSEB
	{
		parameter_visibility = $3;
		if (parameter_visibility > LOWEST_VISIBILITY) {
			/*
			 *  The larger the number, the lower the visibility.
			 */
			yyerror("Visibility level out of bounds: [%d..%d]\n",
				HIGHEST_VISIBILITY, LOWEST_VISIBILITY);
			parameter_visibility = LOWEST_VISIBILITY;
		} else if (parameter_visibility < HIGHEST_VISIBILITY) {
			/*
			 *  The smaller the number, the higher the visibility.
			 */
			yyerror("Visibility level out of bounds: [%d..%d]\n",
				HIGHEST_VISIBILITY, LOWEST_VISIBILITY);
			parameter_visibility = HIGHEST_VISIBILITY;
		}
	}
	;

Default_intro:
	DEFAULT
	{
		mandatory_default = FALSE; /* This is an advisory default */
	}
	;

New_default_intro:
	NEW_DEFAULT
	{
		mandatory_default = TRUE;  /* This is a mandatory default */
	}
	;

Declare_set:
	Zero_or_more_semicolons Declare_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Declare_list:
	Declare_list Semicolons Declare
	|
	Declare
	;

Declare:
	One_str_only Another_str OPENC Third_str CLOSEC
	{
		if (conditional_stack[cond_sp] && configured_module) {
			declare_par ( string1, parameter_visibility,
				      select_flag, string2, string3, TRUE );
		}
	}
	;

Default_set:
	Zero_or_more_semicolons Default_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Default_list:
	Default_list Semicolons Default
	|
	Default
	;

Default:
	One_str_only Openc Expression Closec
	{
		if (($3) != (struct expr *)NULL) {
			default_par( string1, mandatory_default, $3,
				     (struct expr *)NULL,
				     (struct expr *)NULL, TRUE );
		}
	}
	|
	One_str_only Openc Expression SEMICOLON Parsed_bounds Closec
	{
		if (($3) != (struct expr *)NULL
		    && (   bounds.lowptr  != (struct expr *)NULL
			|| bounds.highptr != (struct expr *)NULL ) ) {
			default_par( string1, mandatory_default, $3,
				     bounds.lowptr, bounds.highptr,
				     TRUE );
		}
	}
	;

Adjust_set:
	Zero_or_more_semicolons Adjust_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Adjust_list:
	Adjust_list Semicolons Adjust
	|
	Adjust
	;

Adjust:
	One_str_only Openc Expression Closec
	{
		if (($3) != (struct expr *)NULL)
			adjust_par( string1, $3 );
	}
	;

Set_set:
	Zero_or_more_semicolons Set_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Set_list:
	Set_list Semicolons Set
	|
	Set
	;

Set:
	One_str_only Openc Expression Closec
	{
		if (($3) != (struct expr *)NULL)
			set_par( string1, $3 );
	}
	;

Bound_set:
	Zero_or_more_semicolons Bound_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Bound_list:
	Bound_list Semicolons Bound
	|
	Bound
	;

Bound:
	One_str_only Openc Parsed_bounds Closec
	{
		if (   bounds.lowptr  != (struct expr *)NULL
		    || bounds.highptr != (struct expr *)NULL ) {
			bound_par( string1, bounds.lowptr, bounds.highptr );
		}
	}
	;

Assert_set:
	Zero_or_more_semicolons Assert_list Zero_or_more_semicolons
	|
	Zero_or_more_semicolons
	;

Assert_list:
	Assert_list Semicolons Assert
	|
	Assert
	;

Assert:
	Openc Expression Relop Expression Semicolons One_str_only Closec
	{
		if (($2) != (struct expr *)NULL &&
		    ($4) != (struct expr *)NULL) {
			assert_par( $2, $3, $4, string1 );
		}
	}
	;

Parsed_bounds:
	Begin_bounds Bounds
	;

Begin_bounds:
	/* lambda */
	{
		bounds.lowptr = (struct expr *)NULL;  /* No lower bound seen */
		bounds.highptr = (struct expr *)NULL; /* No upper bound seen */
	}
	;

Bounds:
	Expression ELLIPSIS Expression
	{
		bounds.lowptr = $1;
		bounds.highptr = $3;
	}
	|
	Expression ELLIPSIS
	{
		bounds.lowptr = $1;
	}
	|
	ELLIPSIS Expression
	{
		bounds.highptr = $2;
	}
	;

Openc:
	OPENC Zero_or_more_semicolons
	{
		/*
		 *  Tell config.l to no longer accept names with embedded
		 *  '-' or '/' characters, as this will make expressions
		 *  rather difficult to parse....  This also makes the
		 *  newline character invisible so that long expressions
		 *  may be written.
		 */
		BEGIN IN_EXPRESSION;
	}
	;

Closec:
	CLOSEC
	{
		BEGIN RESTRICTED_NAMES;		/* see config.l */
	}
	;

Expression:
	Begin_expr Actual_expr
	{
		/*
		 *  This rule MUST return NULL if either the current conditional
		 *  is FALSE, or if we are parsing a sub-module that is not
		 *  configured.  In particular, the grammer rules for the
		 *  configurable parameters depend on this to avoid performing
		 *  actions when the module is not configured.
		 */
		if (conditional_stack[cond_sp] && configured_module) {
			if (expression_error) {
				if (($2) != (struct expr *)NULL)
					free_expr($2);
				$$ = (struct expr *)NULL;
			} else
				$$ = $2;
		} else
			$$ = (struct expr *)NULL;
	}
	;

Begin_expr:
	/* lambda */
	{
		expression_error = FALSE;
	}
	;

Actual_expr:
	Actual_expr Addop Term
	{
		if (conditional_stack[cond_sp] && configured_module)
			$$ = binary_op( $2, $1, $3 );
	}
	|
	Actual_expr Addop error
	{
		$$ = $1;
		expression_syntax_error_recovery();
	}
	|
	MINUS Term
	{
		if (conditional_stack[cond_sp] && configured_module)
			$$ = unary_op( EXOPneg, $2 );
	}
	|
	MINUS error
	{
		$$ = (struct expr *)NULL;
		expression_syntax_error_recovery();
	}
	|
	Term
	{
		$$ = $1;
	}
	;

Term:
	Term Mulop MINUS Factor
	{
		if (conditional_stack[cond_sp] && configured_module)
			$$ = binary_op( $2, $1, unary_op(EXOPneg, $4) );
	}
	|
	Term Mulop MINUS error
	{
		$$ = $1;
		expression_syntax_error_recovery();
	}
	|
	Term Mulop Factor
	{
		if (conditional_stack[cond_sp] && configured_module)
			$$ = binary_op( $2, $1, $3 );
	}
	|
	Term Mulop error
	{
		$$ = $1;
		expression_syntax_error_recovery();
	}
	|
	Factor
	{
		$$ = $1;
	}
	;

Factor:
	OPENP Actual_expr CLOSEP
	{
		$$ = $2;
	}
	|
	OPENP error
	{
		$$ = (struct expr *)NULL;
		expression_syntax_error_recovery();
	}
	|
	ID
	{	/* Reference to configuration parameter */
		if (conditional_stack[cond_sp] && configured_module) {
			struct expr *ep;

			ep = new_expr();
			ep->ex_op = EXOPsym;
			ep->ex_u.sym = reference_par( $1 );
			$$ = ep;
		}
	}
	|
	NUMBER
	{	/* Simple constant */
		if (conditional_stack[cond_sp] && configured_module) {
			struct expr *ep;

			ep = new_expr();
			ep->ex_op = EXOPconst;
			ep->ex_u.constant = $1;
			$$ = ep;
		}
	}
	;

Addop:
	PLUS
	{
		$$ = EXOPadd;
	}
	|
	MINUS
	{
		$$ = EXOPsub;
	}
	;

Mulop:
	MULT
	{
		$$ = EXOPmul;
	}
	|
	SLASH
	{
		$$ = EXOPdiv;
	}
	;

Relop:
	LESS
	{
		$$ = RELOPless;
	}
	|
	LESSEQ
	{
		$$ = RELOPlesseq;
	}
	|
	EQ
	{
		$$ = RELOPeq;
	}
	|
	GTREQ
	{
		$$ = RELOPgtreq;
	}
	|
	GTR
	{
		$$ = RELOPgtr;
	}
	;
	
If_spec:
	/*
	 *  Actions only apply if conditional_stack[cond_sp] = TRUE.
	 */
	If_intro Spec_bundle Optional_elsifspec Optional_else
	{
		if (DEBUG_LEVEL(0)) {
			printf("Return from IFtype or ELSE = %s (level %d)\n", 
				conditional_stack[cond_sp] ? "TRUE" : "FALSE",
				cond_sp);
		}
		if (cond_sp <= 0) {
			fatal("yyparse(): IFtype internal nesting error.\n");
			exit(1);
		}
		cond_sp--;
		if (conditional_stack[cond_sp] == COND_NEST_FALSE)
			conditional_stack[cond_sp] = COND_FALSE;
		else if (conditional_stack[cond_sp] == COND_NEST_TRUE)
			conditional_stack[cond_sp] = COND_TRUE;
	}
	;

If_intro:
	If_type OPENP One_str_only CLOSEP
	{
		if (cond_sp >= MAX_COND_NEST-1) {
			fatal("maximum IFtype...ELSE nesting limit reached.\n");
			exit(1);
		}
		if (conditional_stack[cond_sp]) {
			if ((*iffuncptr)(string1)) {
				conditional_stack[cond_sp++] = COND_NEST_TRUE;
				conditional_stack[cond_sp] = COND_TRUE;
			} else
				conditional_stack[++cond_sp] = COND_FALSE;
			if (DEBUG_LEVEL(0)) {
				printf(" IFtype (\"%s\") is %s (level %d)\n",
				  string1,
				  conditional_stack[cond_sp] ? "TRUE" : "FALSE",
				  cond_sp);
			}
		} else {
			conditional_stack[cond_sp++] = COND_NEST_FALSE;
			conditional_stack[cond_sp] = COND_FALSE;
			if (DEBUG_LEVEL(0)) {
				printf(" Nested false IFtype (level %d)\n",
					cond_sp);
			}
		}
	}
	;

If_type:
	IFCONFIG
	{
		iffuncptr = ifconfig;
	}
	|
	IFNCONFIG
	{
		iffuncptr = ifnconfig;
	}
	|
	IFDEF
	{
		iffuncptr = ifdef;
	}
	|
	IFNDEF
	{
		iffuncptr = ifndef;
	}
	|
	IFPRODUCT
	{
		iffuncptr = ifproduct;
	}
	|
	IFNPRODUCT
	{
		iffuncptr = ifnproduct;
	}
	;

Optional_elsifspec:
	Elsifspec_list
	|
	/* lambda */
	;

Elsifspec_list:
	Elsifspec_list Elsifspec
	|
	Elsifspec
	;

Elsifspec:
	Elsifspec_intro Spec_bundle
	;

Elsifspec_intro:
	Elsif_type OPENP One_str_only CLOSEP
	{
		if (cond_sp <= 0) {
			fatal("yyparse(): ELSE IFtype internal nest error.\n");
			exit(1);
		}
		conditional_stack[cond_sp] = COND_FALSE;
		if (conditional_stack[cond_sp-1] == COND_TRUE) {
			if ((*iffuncptr)(string1)) {
				conditional_stack[cond_sp-1] = COND_NEST_TRUE;
				conditional_stack[cond_sp] = COND_TRUE;
			}
			if (DEBUG_LEVEL(0)) {
			       printf(" ELSIFtype (\"%s\") is %s (level %d)\n",
				  string1,
				  conditional_stack[cond_sp] ? "TRUE" : "FALSE",
				  cond_sp);
			}
		}
	}
	;

Elsif_type:
	ELSIFCONFIG
	{
		iffuncptr = ifconfig;
	}
	|
	ELSIFNCONFIG
	{
		iffuncptr = ifnconfig;
	}
	|
	ELSIFDEF
	{
		iffuncptr = ifdef;
	}
	|
	ELSIFNDEF
	{
		iffuncptr = ifndef;
	}
	|
	ELSIFPRODUCT
	{
		iffuncptr = ifproduct;
	}
	|
	ELSIFNPRODUCT
	{
		iffuncptr = ifnproduct;
	}
	;

Optional_else:
	Else_intro Spec_bundle
	|
	/* lambda */
	;

Else_intro:
	ELSE
	{
		if (cond_sp <= 0) {
			fatal("yyparse(): ELSE internal nesting error.\n");
			exit(1);
		}
		conditional_stack[cond_sp] = COND_FALSE;
		if (conditional_stack[cond_sp-1] == COND_TRUE) {
			if (conditional_stack[cond_sp] == COND_FALSE)
				conditional_stack[cond_sp] = COND_TRUE;
		}
	}
	;

Spec_bundle:
	OPENC Many_specs CLOSEC
	;

One_int:
	EQ NUMBER
	{
		$$ = $2;
	}
	|
	OPENP NUMBER CLOSEP
	{
		$$ = $2;
	}
	|
	OPENC NUMBER CLOSEC
	{
		$$ = $2;
	}
	;

One_str:
	One_str_only
	|
	EQ One_str_only
	|
	OPENP One_str_only CLOSEP
	|
	OPENC One_str_only CLOSEC
	;

One_str_only:
	ID
	{	/* always copy for the benefit of: If_spec rules */
		(void)limited_strncpy(string1, $1, MAX_STRLEN,
					"String too long.");
	}
	;

Another_str:
	ID
	{
		if (conditional_stack[cond_sp])
			(void)limited_strncpy(string2, $1, MAX_STRLEN,
					"String too long.");
	}
	;

Third_str:
	ID
	{
		if (conditional_stack[cond_sp])
			(void)limited_strncpy(string3, $1, MAX_STRLEN,
					"String too long.");
	}
	;

Save_id:
	ID
	{ 	/* Don't allocate space if in FALSE conditional.... */
		/* --or-- if not generating a configuration........ */
		/* --or-- if parsing a deconfigured sub-module..... */
		if (conditional_stack[cond_sp] && Config && configured_module)
			$$ = perm_str($1); 
	}
	;

Zero_or_more_semicolons:
	Semicolons
	|
	/* lambda */
	;

Semicolons:
	Semicolons SEMICOLON
	|
	SEMICOLON
	;

%%

/*VARARGS2*/

/*
 * generic error printing routine.
 */
yyerror(s,args)
char *s;
struct {
	int	args[5];
} args;
{
	extern	char	*Ffile;	/* Pointer to current file (see fatal()) */
	char	buff[1024];

	(void)sprintf(buff,s,args);
	fflush(stdout);		/* In case going to same stream */
	fprintf(stderr, "config: \"%s\" line %d\n", Ffile, yylineno);
	fprintf(stderr, "      : %s\n", buff);
	Trouble = TRUE;		/* Don't make a configuration */
}


#include	"lex.yy.c"

/*
 *  Lexical initialization for a mod_config file.
 */
lexinit_mod(path, fp, full_init, select, configured, next_module)
char	*path;				/* pathname of file to be parsed */
FILE	*fp;				/* file stream for path */
boolean	full_init;			/* TRUE if full init; else FALSE */
boolean	select;				/* select parameter information */
boolean configured;			/* module is stubbed flag */
char	**next_module;			/* where to return NEXT=modname name */
{
	parse_mode = PARSE_MOD;		/* Establish mod_config parsing mode */
	next_modulep = next_module;	/* Save where to return NEXT=modname */
	if (full_init) {
		trace_flag = FALSE;
		yylineno = 1;
	}
	mod_import_ok = full_init;	/* module "checkfor" is (dis)allowed */
	select_flag = select;
	configured_module = configured;
	lexinit_common(path, fp, full_init);
}

/*
 *  Lexical initialization for a sys_config_file.
 */
lexinit_sys(path, fp, full_init, select, description, trace, lineno)
char	*path;				/* pathname of file to be parsed */
FILE	*fp;				/* file stream for path */
boolean	full_init;			/* TRUE if full init; else FALSE */
boolean	select;				/* select parameter information */
boolean	description;			/* description seen flag */
boolean	trace;				/* trace flag */
int	lineno;				/* line# */
{
	parse_mode = PARSE_SYS;		/* Establish sys_config parsing mode */
	mod_import_ok = FALSE;		/* module "checkfor" is not allowed */
	select_flag = select;
	desc_flag = description;
	trace_flag = trace;
	yylineno = lineno;
	configured_module = TRUE;
	lexinit_common(path, fp, full_init);
}

/*
 *  Common initialization for mod_ and sys_ config files.
 */
static
lexinit_common(path, fp, full_init)
char	*path;				/* pathname of file to be parsed */
FILE	*fp;				/* file stream for path */
boolean	full_init;			/* TRUE if full init; else FALSE */
{
	if (!lex_keywords_initialized)
		initialize_keywords();
	yyin = fp;
	if (full_init) {
		BEGIN	INITIAL;
		NLSTATE;
		yymorfg = 0;
		yysptr = yysbuf;
		cond_sp = 0;
		conditional_stack[cond_sp] = COND_TRUE;
	}
	if (path != (char *)NULL) {	/* caller wants current file set */
		/*
		 *  WARNING: 'path' must point to a data area that will NOT
		 *  be changed by subsequent actions of the program, since
		 *  the configurable parameter directives implemented in
		 *  parameters.c depend on this....
		 */
		SET_FILE(path);
	}
	/*
	 *  Initialise "found during parse" flags
	 */
	device_spec = FALSE;
}

/*
 * yywrap is called whenever lex hits EOF
 */
yywrap()
{
	if (parse_mode == PARSE_SYS && !end_sys()) {
		/*
		 *  We just finished parsing an included file.
		 *  Continue parsing back with the parent.
		 */
		return(0);
	}
	/*
	 * end of parse
	 */
	yylineno = 0;
	return(1);
}

/*
 *  Return the appropriate expression node for a binary operation
 *  of two expressions.
 */
static struct expr *
binary_op (op, lep, rep)
int op;					/* operation */
register struct expr *lep;		/* left expression */
register struct expr *rep;		/* right expression */
{
	register struct expr *new_ep;

	new_ep = new_expr();
	new_ep->ex_op = op;
	new_ep->ex_u.expr.lexpr = lep;
	new_ep->ex_u.expr.rexpr = rep;
	return(new_ep);
}

/*
 *  Return the appropriate expression node for a unary operation
 *  of a single expression.
 */
static struct expr *
unary_op (op, ep)
int op;					/* operation */
register struct expr *ep;		/* expression */
{
	register struct expr *new_ep;

	new_ep = new_expr();
	new_ep->ex_op = op;
	new_ep->ex_u.expr.lexpr = ep;
	new_ep->ex_u.expr.rexpr = (struct expr *) NULL;
	return(new_ep);
}

/*
 *  Implement a strncpy() function very similar to its namesake.
 *  However, there are two differences:
 *
 *	1.  This function complains when the source string
 *	    is longer than 'maxlen.'
 *	2.  This function appends but one trailing null to the
 *	    destination string.
 */
static char *
limited_strncpy(dest, source, maxlen, toolongmsg)
register char *dest;			/* destination */
register char *source;			/* source */
int maxlen;				/* maximum length */
char *toolongmsg;			/* message to display if too long */
{
	register int len;

	len = strlen(source);
	if (len > maxlen) {
		yyerror(toolongmsg);
		len = maxlen;
	}
	(void)memcpy(dest, source, len);
	dest[len] = '\0';
	return(dest);
}

/*
 *  Consume text from the current input stream until the sentinel
 *  character seen.  The pointer returned references a data area
 *  that WILL be overwritten on the next call!!!!
 */
static char *
divert(sentinel)
register int	sentinel;
{
#	define BUFFER_SIZE	4096	/* grow by 4K at a time */
	static char *buffer;		/* buffer */
	static int buffer_len = 0;	/* buffer size */
	register int cnt;
	register int c;			/* input character */
	char newline_seen;		/* boolean */

	/*
	 * read lex input up to sentinel.
	 */
	newline_seen = 1;
	if (conditional_stack[cond_sp] && Config && configured_module) {
		if (buffer_len == 0) {	/* no buffer allocated */
			if ((buffer = malloc(BUFFER_SIZE)) == (char *)NULL) {
				yyerror("divert unable to allocate");
				return((char *)NULL);
			}
			buffer_len = BUFFER_SIZE-1;
		}			/* leave room for null on eof */
		for( cnt=0; (c=input()) != EOF; cnt++) {
			if (cnt >= buffer_len) {
				buffer_len += BUFFER_SIZE;
				if (DEBUG_LEVEL(12)) {
					printf("divert: realloc: 0x%x %d\n",
						buffer, buffer_len+1);
				}
				buffer = realloc(buffer, buffer_len+1);
				if (buffer == (char*)NULL) {
					yyerror("divert unable to allocate");
					return((char *)NULL);
				}
			}
			buffer[cnt] = c;
			if (c == '\n') {
				newline_seen = 1;
			} else if (newline_seen) {
				newline_seen = 0;
				if (c == sentinel) {
					buffer[cnt] = '\0';
					return(buffer);
				}
			}
		}
		buffer[cnt] = '\0';
		return(buffer);
	}
	/*
	 *  We are not generating a configuration, or are in a FALSE
	 *  conditional, or are parsing a deconfigured module.  Swallow
	 *  up the input, but do not save it.
	 */
	for( cnt=0; (c=input()) != EOF; cnt++) {
		if (c == '\n') {
			newline_seen = 1;
		} else if (newline_seen) {
			newline_seen = 0;
			if (c == sentinel)
				return((char *)NULL);
		}
	}
	return((char *)NULL);
}

/*
 *  Recover from a syntax error from within an arithmetic expression
 *  for one of the configuration parameter directives.
 */
static void
expression_syntax_error_recovery()
{
	yyerrok;
	expression_error = TRUE;
	while (yychar != 0 && yychar != SEMICOLON && yychar != CLOSEC) {
		if ((yychar = yylex()) < 0)
			yychar = 0;
	}
}

lop(options)
struct	list	*options;
{
	struct	list	*op;
	int		comma;

	comma = 0;
	printf("(");
	for (op = options; op != (struct list *)NULL; op = op->l_next) {
		if (comma++)
			printf(",\"%s\" ", op->l_text);
		else
			printf(" \"%s\"", op->l_text);
	}
	printf(" )\n");
}
