NetBSD/usr.bin/config/gram.y

1473 lines
32 KiB
Plaintext

%{
/* $NetBSD: gram.y,v 1.57 2024/01/18 04:41:37 thorpej Exp $ */
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratories.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)gram.y 8.1 (Berkeley) 6/6/93
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: gram.y,v 1.57 2024/01/18 04:41:37 thorpej Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "defs.h"
#include "sem.h"
#define FORMAT(n) (((n).fmt == 8 && (n).val != 0) ? "0%llo" : \
((n).fmt == 16) ? "0x%llx" : "%lld")
#define stop(s) cfgerror(s), exit(1)
static struct config conf; /* at most one active at a time */
static int nowarn; /* if warning suppression is on */
/*
* Allocation wrapper functions
*/
static void wrap_alloc(void *ptr, unsigned code);
static void wrap_continue(void);
static void wrap_cleanup(void);
/*
* Allocation wrapper type codes
*/
#define WRAP_CODE_nvlist 1
#define WRAP_CODE_defoptlist 2
#define WRAP_CODE_loclist 3
#define WRAP_CODE_attrlist 4
#define WRAP_CODE_condexpr 5
/*
* The allocation wrappers themselves
*/
#define DECL_ALLOCWRAP(t) static struct t *wrap_mk_##t(struct t *arg)
DECL_ALLOCWRAP(nvlist);
DECL_ALLOCWRAP(defoptlist);
DECL_ALLOCWRAP(loclist);
DECL_ALLOCWRAP(attrlist);
DECL_ALLOCWRAP(condexpr);
/* allow shorter names */
#define wrap_mk_loc(p) wrap_mk_loclist(p)
#define wrap_mk_cx(p) wrap_mk_condexpr(p)
/*
* Macros for allocating new objects
*/
/* old-style for struct nvlist */
#define new0(n,s,p,i,x) wrap_mk_nvlist(newnv(n, s, p, i, x))
#define new_n(n) new0(n, NULL, NULL, 0, NULL)
#define new_nx(n, x) new0(n, NULL, NULL, 0, x)
#define new_ns(n, s) new0(n, s, NULL, 0, NULL)
#define new_si(s, i) new0(NULL, s, NULL, i, NULL)
#define new_spi(s, p, i) new0(NULL, s, p, i, NULL)
#define new_nsi(n,s,i) new0(n, s, NULL, i, NULL)
#define new_np(n, p) new0(n, NULL, p, 0, NULL)
#define new_s(s) new0(NULL, s, NULL, 0, NULL)
#define new_p(p) new0(NULL, NULL, p, 0, NULL)
#define new_px(p, x) new0(NULL, NULL, p, 0, x)
#define new_sx(s, x) new0(NULL, s, NULL, 0, x)
#define new_nsx(n,s,x) new0(n, s, NULL, 0, x)
#define new_i(i) new0(NULL, NULL, NULL, i, NULL)
/* new style, type-polymorphic; ordinary and for types with multiple flavors */
#define MK0(t) wrap_mk_##t(mk_##t())
#define MK1(t, a0) wrap_mk_##t(mk_##t(a0))
#define MK2(t, a0, a1) wrap_mk_##t(mk_##t(a0, a1))
#define MK3(t, a0, a1, a2) wrap_mk_##t(mk_##t(a0, a1, a2))
#define MKF0(t, f) wrap_mk_##t(mk_##t##_##f())
#define MKF1(t, f, a0) wrap_mk_##t(mk_##t##_##f(a0))
#define MKF2(t, f, a0, a1) wrap_mk_##t(mk_##t##_##f(a0, a1))
/*
* Data constructors
*/
static struct defoptlist *mk_defoptlist(const char *, const char *,
const char *);
static struct loclist *mk_loc(const char *, const char *, long long);
static struct loclist *mk_loc_val(const char *, struct loclist *);
static struct attrlist *mk_attrlist(struct attrlist *, struct attr *);
static struct condexpr *mk_cx_atom(const char *);
static struct condexpr *mk_cx_not(struct condexpr *);
static struct condexpr *mk_cx_and(struct condexpr *, struct condexpr *);
static struct condexpr *mk_cx_or(struct condexpr *, struct condexpr *);
/*
* Other private functions
*/
static void setmachine(const char *, const char *, struct nvlist *, int);
static void check_maxpart(void);
static struct loclist *present_loclist(struct loclist *ll);
static void app(struct loclist *, struct loclist *);
static struct loclist *locarray(const char *, int, struct loclist *, int);
static struct loclist *namelocvals(const char *, struct loclist *);
%}
%union {
struct attr *attr;
struct devbase *devb;
struct deva *deva;
struct nvlist *list;
struct defoptlist *defoptlist;
struct loclist *loclist;
struct attrlist *attrlist;
struct condexpr *condexpr;
const char *str;
struct numconst num;
int64_t val;
u_char flag;
devmajor_t devmajor;
int32_t i32;
}
%token AND AT ATTACH
%token BLOCK BUILD
%token CHAR COLONEQ COMPILE_WITH CONFIG
%token DEFFS DEFINE DEFOPT DEFPARAM DEFFLAG DEFPSEUDO DEFPSEUDODEV
%token DEVICE DEVCLASS DUMPS DEVICE_MAJOR
%token ENDFILE
%token XFILE FILE_SYSTEM FLAGS
%token IDENT IOCONF
%token LINKZERO
%token XMACHINE MAJOR MAKEOPTIONS MAXUSERS MAXPARTITIONS MINOR
%token MKFLAGVAR
%token NEEDS_COUNT NEEDS_FLAG NO CNO
%token XOBJECT OBSOLETE ON OPTIONS
%token PACKAGE PLUSEQ PREFIX BUILDPREFIX PSEUDO_DEVICE PSEUDO_ROOT
%token ROOT
%token SELECT SINGLE SOURCE
%token TYPE
%token VECTOR VERSION
%token WITH
%token <num> NUMBER
%token <str> PATHNAME QSTRING WORD EMPTYSTRING
%token ENDDEFS
%type <condexpr> fopts condexpr condatom
%type <condexpr> cond_or_expr cond_and_expr cond_prefix_expr
%type <condexpr> cond_base_expr
%type <str> fs_spec
%type <flag> fflags fflag oflags oflag
%type <str> rule
%type <attr> depend
%type <devb> devbase
%type <deva> devattach_opt
%type <list> atlist
%type <loclist> interface_opt
%type <str> atname
%type <loclist> loclist locdef
%type <str> locdefault
%type <loclist> values locdefaults
%type <attrlist> depend_list depends
%type <loclist> locators locator
%type <list> dev_spec
%type <str> device_instance
%type <str> attachment
%type <str> value
%type <val> major_minor
%type <num> signed_number
%type <i32> int32 npseudo device_flags no
%type <str> deffs
%type <list> deffses
%type <defoptlist> defopt
%type <defoptlist> defopts
%type <str> optdepend
%type <list> optdepends
%type <list> optdepend_list
%type <str> optfile_opt
%type <list> subarches
%type <str> filename stringvalue locname mkvarname
%type <devmajor> device_major_block device_major_char
%type <list> devnodes devnodetype devnodeflags devnode_dims
%%
/*
* A complete configuration consists of both the selection part (a
* kernel config such as GENERIC or SKYNET, plus also the various
* std.* files), which selects the material to be in the kernel, and
* also the definition part (files, files.*, etc.) that declares what
* material is available to be placed in kernels.
*
* The two parts have almost entirely separate syntaxes. This grammar
* covers both of them. When config is run on a kernel configuration
* file, the std.* file for the port is included explicitly. The
* files.* files are included implicitly when the std.* file declares
* the machine type.
*
* The machine spec, which brings in the definition part, must appear
* before all configuration material except for the "topthings"; these
* are the "source" and "build" declarations that tell config where
* things are. These are not used by default.
*
* A previous version of this comment contained the following text:
*
* Note that we do not have sufficient keywords to enforce any
* order between elements of "topthings" without introducing
* shift/reduce conflicts. Instead, check order requirements in
* the C code.
*
* As of March 2012 this comment makes no sense, as there are only two
* topthings and no reason for them to be forcibly ordered.
* Furthermore, the statement about conflicts is false.
*/
/* Complete configuration. */
configuration:
topthings machine_spec definition_part selection_part
;
/* Sequence of zero or more topthings. */
topthings:
/* empty */
| topthings topthing
;
/* Directory specification. */
topthing:
'\n'
| SOURCE filename '\n' { if (!srcdir) srcdir = $2; }
| BUILD filename '\n' { if (!builddir) builddir = $2; }
;
/* "machine foo" from std.whatever */
machine_spec:
XMACHINE WORD '\n' { setmachine($2,NULL,NULL,0); }
| XMACHINE WORD WORD '\n' { setmachine($2,$3,NULL,0); }
| XMACHINE WORD WORD subarches '\n' { setmachine($2,$3,$4,0); }
| IOCONF WORD '\n' { setmachine($2,NULL,NULL,1); }
| error { stop("cannot proceed without machine or ioconf specifier"); }
;
/* One or more sub-arches. */
subarches:
WORD { $$ = new_n($1); }
| subarches WORD { $$ = new_nx($2, $1); }
;
no:
NO { $$ = 0; }
| CNO { $$ = 1; }
;
/************************************************************/
/*
* The machine definitions grammar.
*/
/* Complete definition part: the contents of all files.* files. */
definition_part:
definitions ENDDEFS {
CFGDBG(1, "ENDDEFS");
check_maxpart();
check_version();
}
;
/* Zero or more definitions. Trap errors. */
definitions:
/* empty */
| definitions '\n'
| definitions definition '\n' { wrap_continue(); }
| definitions error '\n' { wrap_cleanup(); }
| definitions ENDFILE { enddefs(); checkfiles(); }
;
/* A single definition. */
definition:
define_file
| define_object
| define_device_major
| define_prefix
| define_buildprefix
| define_devclass
| define_filesystems
| define_attribute
| define_option
| define_flag
| define_flag_mkvar
| define_obsolete_flag
| define_param
| define_obsolete_param
| define_device
| define_device_attachment
| define_maxpartitions
| define_maxusers
| define_makeoptions
| define_pseudo
| define_pseudodev
| define_major
| define_version
;
/* source file: file foo/bar.c bar|baz needs-flag compile-with blah */
define_file:
XFILE filename fopts fflags rule { addfile($2, $3, $4, $5); }
;
/* object file: object zot.o foo|zot needs-flag */
define_object:
XOBJECT filename fopts oflags { addfile($2, $3, $4, NULL); }
;
/* device major declaration */
define_device_major:
DEVICE_MAJOR WORD device_major_char device_major_block fopts devnodes
{
adddevm($2, $3, $4, $5, $6);
do_devsw = 1;
}
;
/* prefix delimiter */
define_prefix:
PREFIX filename { prefix_push($2); }
| PREFIX { prefix_pop(); }
;
define_buildprefix:
BUILDPREFIX filename { buildprefix_push($2); }
| BUILDPREFIX WORD { buildprefix_push($2); }
| BUILDPREFIX { buildprefix_pop(); }
;
define_devclass:
DEVCLASS WORD { (void)defdevclass($2, NULL, NULL, 1); }
;
define_filesystems:
DEFFS deffses optdepend_list { deffilesystem($2, $3); }
;
define_attribute:
DEFINE WORD interface_opt depend_list
{ (void)defattr0($2, $3, $4, 0); }
;
define_option:
DEFOPT optfile_opt defopts optdepend_list
{ defoption($2, $3, $4); }
;
define_flag:
DEFFLAG optfile_opt defopts optdepend_list
{ defflag($2, $3, $4, 0); }
;
define_flag_mkvar:
MKFLAGVAR defopts
{ mkflagvar($2); }
define_obsolete_flag:
OBSOLETE DEFFLAG optfile_opt defopts
{ defflag($3, $4, NULL, 1); }
;
define_param:
DEFPARAM optfile_opt defopts optdepend_list
{ defparam($2, $3, $4, 0); }
;
define_obsolete_param:
OBSOLETE DEFPARAM optfile_opt defopts
{ defparam($3, $4, NULL, 1); }
;
define_device:
DEVICE devbase interface_opt depend_list
{ defdev($2, $3, $4, 0); }
;
define_device_attachment:
ATTACH devbase AT atlist devattach_opt depend_list
{ defdevattach($5, $2, $4, $6); }
;
define_maxpartitions:
MAXPARTITIONS int32 { maxpartitions = $2; }
;
define_maxusers:
MAXUSERS int32 int32 int32
{ setdefmaxusers($2, $3, $4); }
;
define_makeoptions:
MAKEOPTIONS condmkopt_list
;
define_pseudo:
/* interface_opt in DEFPSEUDO is for backwards compatibility */
DEFPSEUDO devbase interface_opt depend_list
{ defdev($2, $3, $4, 1); }
;
define_pseudodev:
DEFPSEUDODEV devbase interface_opt depend_list
{ defdev($2, $3, $4, 2); }
;
define_major:
MAJOR '{' majorlist '}'
;
define_version:
VERSION int32 { setversion($2); }
;
/* file options: optional expression of conditions */
fopts:
/* empty */ { $$ = NULL; }
| condexpr { $$ = $1; }
;
/* zero or more flags for a file */
fflags:
/* empty */ { $$ = 0; }
| fflags fflag { $$ = $1 | $2; }
;
/* one flag for a file */
fflag:
NEEDS_COUNT { $$ = FI_NEEDSCOUNT; }
| NEEDS_FLAG { $$ = FI_NEEDSFLAG; }
;
/* extra compile directive for a source file */
rule:
/* empty */ { $$ = NULL; }
| COMPILE_WITH stringvalue { $$ = $2; }
;
/* zero or more flags for an object file */
oflags:
/* empty */ { $$ = 0; }
| oflags oflag { $$ = $1 | $2; }
;
/* a single flag for an object file */
oflag:
NEEDS_FLAG { $$ = FI_NEEDSFLAG; }
;
/* char 55 */
device_major_char:
/* empty */ { $$ = -1; }
| CHAR int32 { $$ = $2; }
;
/* block 33 */
device_major_block:
/* empty */ { $$ = -1; }
| BLOCK int32 { $$ = $2; }
;
/* device node specification */
devnodes:
/* empty */ { $$ = new_s("DEVNODE_DONTBOTHER"); }
| devnodetype ',' devnodeflags { $$ = nvcat($1, $3); }
| devnodetype { $$ = $1; }
;
/* device nodes without flags */
devnodetype:
SINGLE { $$ = new_s("DEVNODE_SINGLE"); }
| VECTOR '=' devnode_dims { $$ = nvcat(new_s("DEVNODE_VECTOR"), $3); }
;
/* dimensions (?) */
devnode_dims:
NUMBER { $$ = new_i($1.val); }
| NUMBER ':' NUMBER {
struct nvlist *__nv1, *__nv2;
__nv1 = new_i($1.val);
__nv2 = new_i($3.val);
$$ = nvcat(__nv1, __nv2);
}
;
/* flags for device nodes */
devnodeflags:
LINKZERO { $$ = new_s("DEVNODE_FLAG_LINKZERO");}
;
/* one or more file system names */
deffses:
deffs { $$ = new_n($1); }
| deffses deffs { $$ = new_nx($2, $1); }
;
/* a single file system name */
deffs:
WORD { $$ = $1; }
;
/* optional locator specification */
interface_opt:
/* empty */ { $$ = NULL; }
| '{' '}' { $$ = present_loclist(NULL); }
| '{' loclist '}' { $$ = present_loclist($2); }
;
/*
* loclist order matters, must use right recursion
* XXX wot?
*/
/* list of locator definitions */
loclist:
locdef { $$ = $1; }
| locdef ',' loclist { $$ = $1; app($1, $3); }
;
/*
* "[ WORD locdefault ]" syntax may be unnecessary...
*/
/* one locator definition */
locdef:
locname locdefault { $$ = MK3(loc, $1, $2, 0); }
| locname { $$ = MK3(loc, $1, NULL, 0); }
| '[' locname locdefault ']' { $$ = MK3(loc, $2, $3, 1); }
| locname '[' int32 ']' { $$ = locarray($1, $3, NULL, 0); }
| locname '[' int32 ']' locdefaults
{ $$ = locarray($1, $3, $5, 0); }
| '[' locname '[' int32 ']' locdefaults ']'
{ $$ = locarray($2, $4, $6, 1); }
;
/* locator name */
locname:
WORD { $$ = $1; }
| QSTRING { $$ = $1; }
;
/* locator default value */
locdefault:
'=' value { $$ = $2; }
;
/* multiple locator default values */
locdefaults:
'=' '{' values '}' { $$ = $3; }
;
/* list of depends, may be empty */
depend_list:
/* empty */ { $$ = NULL; }
| ':' depends { $$ = $2; }
;
/* one or more depend items */
depends:
depend { $$ = MK2(attrlist, NULL, $1); }
| depends ',' depend { $$ = MK2(attrlist, $1, $3); }
;
/* one depend item (which is an attribute) */
depend:
WORD { $$ = refattr($1); }
;
/* list of option depends, may be empty */
optdepend_list:
/* empty */ { $$ = NULL; }
| ':' optdepends { $$ = $2; }
;
/* a list of option dependencies */
optdepends:
optdepend { $$ = new_n($1); }
| optdepends ',' optdepend { $$ = new_nx($3, $1); }
;
/* one option depend, which is an option name */
optdepend:
WORD { $$ = $1; }
;
/* list of places to attach: attach blah at ... */
atlist:
atname { $$ = new_n($1); }
| atlist ',' atname { $$ = new_nx($3, $1); }
;
/* a place to attach a device */
atname:
WORD { $$ = $1; }
| ROOT { $$ = NULL; }
;
/* one or more defined options */
defopts:
defopt { $$ = $1; }
| defopts defopt { $$ = defoptlist_append($2, $1); }
;
/* one defined option */
defopt:
WORD { $$ = MK3(defoptlist, $1, NULL, NULL); }
| WORD '=' value { $$ = MK3(defoptlist, $1, $3, NULL); }
| WORD COLONEQ value { $$ = MK3(defoptlist, $1, NULL, $3); }
| WORD '=' value COLONEQ value { $$ = MK3(defoptlist, $1, $3, $5); }
;
/* list of conditional makeoptions */
condmkopt_list:
condmkoption
| condmkopt_list ',' condmkoption
;
/* one conditional make option */
condmkoption:
condexpr mkvarname PLUSEQ value { appendcondmkoption($1, $2, $4); }
;
/* device name */
devbase:
WORD { $$ = getdevbase($1); }
;
/* optional attachment: with foo */
devattach_opt:
/* empty */ { $$ = NULL; }
| WITH WORD { $$ = getdevattach($2); }
;
/* list of major numbers */
/* XXX why is this right-recursive? */
majorlist:
majordef
| majorlist ',' majordef
;
/* one major number */
majordef:
devbase '=' int32 { setmajor($1, $3); }
;
int32:
NUMBER {
if ($1.val > INT_MAX || $1.val < INT_MIN)
cfgerror("overflow %" PRId64, $1.val);
else
$$ = (int32_t)$1.val;
}
;
/************************************************************/
/*
* The selection grammar.
*/
/* Complete selection part: all std.* files plus selected config. */
selection_part:
selections
;
/* Zero or more config items. Trap errors. */
selections:
/* empty */
| selections '\n'
| selections selection '\n' { wrap_continue(); }
| selections error '\n' { wrap_cleanup(); }
;
/* One config item. */
selection:
definition
| select_attr
| select_no_attr
| select_no_filesystems
| select_filesystems
| select_no_makeoptions
| select_makeoptions
| select_no_options
| select_options
| select_maxusers
| select_ident
| select_no_ident
| select_config
| select_no_config
| select_no_pseudodev
| select_pseudodev
| select_pseudoroot
| select_no_device_instance_attachment
| select_no_device_attachment
| select_no_device_instance
| select_device_instance
;
select_attr:
SELECT WORD { addattr($2); }
;
select_no_attr:
no SELECT WORD { delattr($3, $1); }
;
select_no_filesystems:
no FILE_SYSTEM { nowarn = $1; } no_fs_list { nowarn = 0; }
;
select_filesystems:
FILE_SYSTEM fs_list
;
select_no_makeoptions:
no MAKEOPTIONS { nowarn = $1; } no_mkopt_list { nowarn = 0; }
;
select_makeoptions:
MAKEOPTIONS mkopt_list
;
select_no_options:
no OPTIONS { nowarn = $1; } no_opt_list { nowarn = 0; }
;
select_options:
OPTIONS opt_list
;
select_maxusers:
MAXUSERS int32 { setmaxusers($2); }
;
select_ident:
IDENT stringvalue { setident($2); }
;
select_no_ident:
no IDENT { setident(NULL); }
;
select_config:
CONFIG conf root_spec sysparam_list
{ addconf(&conf); }
;
select_no_config:
no CONFIG WORD { delconf($3, $1); }
;
select_no_pseudodev:
no PSEUDO_DEVICE WORD { delpseudo($3, $1); }
;
select_pseudodev:
PSEUDO_DEVICE WORD npseudo { addpseudo($2, $3); }
;
select_pseudoroot:
PSEUDO_ROOT device_instance { addpseudoroot($2); }
;
select_no_device_instance_attachment:
no device_instance AT attachment
{ deldevi($2, $4, $1); }
;
select_no_device_attachment:
no DEVICE AT attachment { deldeva($4, $1); }
;
select_no_device_instance:
no device_instance { deldev($2, $1); }
;
select_device_instance:
device_instance AT attachment locators device_flags
{ adddev($1, $3, $4, $5); }
;
/* list of filesystems */
fs_list:
fsoption
| fs_list ',' fsoption
;
/* one filesystem */
fsoption:
WORD { addfsoption($1); }
;
/* list of filesystems that had NO in front */
no_fs_list:
no_fsoption
| no_fs_list ',' no_fsoption
;
/* one filesystem that had NO in front */
no_fsoption:
WORD { delfsoption($1, nowarn); }
;
/* list of make options */
/* XXX why is this right-recursive? */
mkopt_list:
mkoption
| mkopt_list ',' mkoption
;
/* one make option */
mkoption:
mkvarname '=' value { addmkoption($1, $3); }
| mkvarname PLUSEQ value { appendmkoption($1, $3); }
;
/* list of make options that had NO in front */
no_mkopt_list:
no_mkoption
| no_mkopt_list ',' no_mkoption
;
/* one make option that had NO in front */
/* XXX shouldn't this be mkvarname rather than WORD? */
no_mkoption:
WORD { delmkoption($1, nowarn); }
;
/* list of options */
opt_list:
option
| opt_list ',' option
;
/* one option */
option:
WORD { addoption($1, NULL); }
| WORD '=' value { addoption($1, $3); }
;
/* list of options that had NO in front */
no_opt_list:
no_option
| no_opt_list ',' no_option
;
/* one option that had NO in front */
no_option:
WORD { deloption($1, nowarn); }
;
/* the name in "config name root on ..." */
conf:
WORD {
conf.cf_name = $1;
conf.cf_where.w_srcline = currentline();
conf.cf_fstype = NULL;
conf.cf_root = NULL;
conf.cf_dump = NULL;
}
;
/* root fs specification */
root_spec:
ROOT on_opt dev_spec { setconf(&conf.cf_root, "root", $3); }
| ROOT on_opt dev_spec fs_spec { setconf(&conf.cf_root, "root", $3); }
;
/* device for root fs or dump */
dev_spec:
'?' { $$ = new_spi(intern("?"),
NULL,
(long long)NODEV); }
| QSTRING { $$ = new_spi($1,
__UNCONST("spec"),
(long long)NODEV); }
| WORD { $$ = new_spi($1,
NULL,
(long long)NODEV); }
| major_minor { $$ = new_si(NULL, $1); }
;
/* major and minor device number */
major_minor:
MAJOR NUMBER MINOR NUMBER { $$ = (int64_t)makedev($2.val, $4.val); }
;
/* filesystem type for root fs specification */
fs_spec:
TYPE '?' { setfstype(&conf.cf_fstype, intern("?")); }
| TYPE WORD { setfstype(&conf.cf_fstype, $2); }
;
/* zero or more additional system parameters */
sysparam_list:
/* empty */
| sysparam_list sysparam
;
/* one additional system parameter (there's only one: dumps) */
sysparam:
DUMPS on_opt dev_spec { setconf(&conf.cf_dump, "dumps", $3); }
;
/* number of pseudo devices to configure (which is optional) */
npseudo:
/* empty */ { $$ = 1; }
| int32 { $$ = $1; }
;
/* name of a device to configure */
device_instance:
WORD { $$ = $1; }
| WORD '*' { $$ = starref($1); }
;
/* name of a device to configure an attachment to */
attachment:
ROOT { $$ = NULL; }
| WORD { $$ = $1; }
| WORD '?' { $$ = wildref($1); }
;
/* zero or more locators */
locators:
/* empty */ { $$ = NULL; }
| locators locator { $$ = $2; app($2, $1); }
;
/* one locator */
locator:
WORD '?' { $$ = MK3(loc, $1, NULL, 0); }
| WORD values { $$ = namelocvals($1, $2); }
;
/* optional device flags */
device_flags:
/* empty */ { $$ = 0; }
| FLAGS int32 { $$ = $2; }
;
/************************************************************/
/*
* conditions
*/
/*
* order of options is important, must use right recursion
*
* dholland 20120310: wut?
*/
/* expression of conditions */
condexpr:
cond_or_expr
;
cond_or_expr:
cond_and_expr
| cond_or_expr '|' cond_and_expr { $$ = MKF2(cx, or, $1, $3); }
;
cond_and_expr:
cond_prefix_expr
| cond_and_expr '&' cond_prefix_expr { $$ = MKF2(cx, and, $1, $3); }
;
cond_prefix_expr:
cond_base_expr
/* XXX notyet - need to strengthen downstream first */
/* | '!' cond_prefix_expr { $$ = MKF1(cx, not, $2); } */
;
cond_base_expr:
condatom { $$ = $1; }
| '!' condatom { $$ = MKF1(cx, not, $2); }
| '(' condexpr ')' { $$ = $2; }
;
/* basic element of config element expression: a config element */
condatom:
WORD { $$ = MKF1(cx, atom, $1); }
;
/************************************************************/
/*
* Various nonterminals shared between the grammars.
*/
/* variable name for make option */
mkvarname:
QSTRING { $$ = $1; }
| WORD { $$ = $1; }
;
/* optional file for an option */
optfile_opt:
/* empty */ { $$ = NULL; }
| filename { $$ = $1; }
;
/* filename. */
filename:
QSTRING { $$ = $1; }
| PATHNAME { $$ = $1; }
;
/* constant value */
value:
QSTRING { $$ = $1; }
| WORD { $$ = $1; }
| EMPTYSTRING { $$ = $1; }
| signed_number {
char bf[40];
(void)snprintf(bf, sizeof(bf), FORMAT($1), (long long)$1.val);
$$ = intern(bf);
}
;
/* constant value that is a string */
stringvalue:
QSTRING { $$ = $1; }
| WORD { $$ = $1; }
;
/* comma-separated list of values */
/* XXX why right-recursive? */
values:
value { $$ = MKF2(loc, val, $1, NULL); }
| value ',' values { $$ = MKF2(loc, val, $1, $3); }
;
/* possibly negative number */
signed_number:
NUMBER { $$ = $1; }
| '-' NUMBER { $$.fmt = $2.fmt; $$.val = -$2.val; }
;
/* optional ON keyword */
on_opt:
/* empty */
| ON
;
%%
void
yyerror(const char *s)
{
cfgerror("%s", s);
}
/************************************************************/
/*
* Wrap allocations that live on the parser stack so that we can free
* them again on error instead of leaking.
*/
#define MAX_WRAP 1000
struct wrap_entry {
void *ptr;
unsigned typecode;
};
static struct wrap_entry wrapstack[MAX_WRAP];
static unsigned wrap_depth;
/*
* Remember pointer PTR with type-code CODE.
*/
static void
wrap_alloc(void *ptr, unsigned code)
{
unsigned pos;
if (wrap_depth >= MAX_WRAP) {
panic("allocation wrapper stack overflow");
}
pos = wrap_depth++;
wrapstack[pos].ptr = ptr;
wrapstack[pos].typecode = code;
}
/*
* We succeeded; commit to keeping everything that's been allocated so
* far and clear the stack.
*/
static void
wrap_continue(void)
{
wrap_depth = 0;
}
/*
* We failed; destroy all the objects allocated.
*/
static void
wrap_cleanup(void)
{
unsigned i;
/*
* Destroy each item. Note that because everything allocated
* is entered on the list separately, lists and trees need to
* have their links blanked before being destroyed. Also note
* that strings are interned elsewhere and not handled by this
* mechanism.
*/
for (i=0; i<wrap_depth; i++) {
switch (wrapstack[i].typecode) {
case WRAP_CODE_nvlist:
nvfree(wrapstack[i].ptr);
break;
case WRAP_CODE_defoptlist:
{
struct defoptlist *dl = wrapstack[i].ptr;
dl->dl_next = NULL;
defoptlist_destroy(dl);
}
break;
case WRAP_CODE_loclist:
{
struct loclist *ll = wrapstack[i].ptr;
ll->ll_next = NULL;
loclist_destroy(ll);
}
break;
case WRAP_CODE_attrlist:
{
struct attrlist *al = wrapstack[i].ptr;
al->al_next = NULL;
al->al_this = NULL;
attrlist_destroy(al);
}
break;
case WRAP_CODE_condexpr:
{
struct condexpr *cx = wrapstack[i].ptr;
cx->cx_type = CX_ATOM;
cx->cx_atom = NULL;
condexpr_destroy(cx);
}
break;
default:
panic("invalid code %u on allocation wrapper stack",
wrapstack[i].typecode);
}
}
wrap_depth = 0;
}
/*
* Instantiate the wrapper functions.
*
* Each one calls wrap_alloc to save the pointer and then returns the
* pointer again; these need to be generated with the preprocessor in
* order to be typesafe.
*/
#define DEF_ALLOCWRAP(t) \
static struct t * \
wrap_mk_##t(struct t *arg) \
{ \
wrap_alloc(arg, WRAP_CODE_##t); \
return arg; \
}
DEF_ALLOCWRAP(nvlist);
DEF_ALLOCWRAP(defoptlist);
DEF_ALLOCWRAP(loclist);
DEF_ALLOCWRAP(attrlist);
DEF_ALLOCWRAP(condexpr);
/************************************************************/
/*
* Data constructors
*
* (These are *beneath* the allocation wrappers.)
*/
static struct defoptlist *
mk_defoptlist(const char *name, const char *val, const char *lintval)
{
return defoptlist_create(name, val, lintval);
}
static struct loclist *
mk_loc(const char *name, const char *str, long long num)
{
return loclist_create(name, str, num);
}
static struct loclist *
mk_loc_val(const char *str, struct loclist *next)
{
struct loclist *ll;
ll = mk_loc(NULL, str, 0);
ll->ll_next = next;
return ll;
}
static struct attrlist *
mk_attrlist(struct attrlist *next, struct attr *a)
{
return attrlist_cons(next, a);
}
static struct condexpr *
mk_cx_atom(const char *s)
{
struct condexpr *cx;
cx = condexpr_create(CX_ATOM);
cx->cx_atom = s;
return cx;
}
static struct condexpr *
mk_cx_not(struct condexpr *sub)
{
struct condexpr *cx;
cx = condexpr_create(CX_NOT);
cx->cx_not = sub;
return cx;
}
static struct condexpr *
mk_cx_and(struct condexpr *left, struct condexpr *right)
{
struct condexpr *cx;
cx = condexpr_create(CX_AND);
cx->cx_and.left = left;
cx->cx_and.right = right;
return cx;
}
static struct condexpr *
mk_cx_or(struct condexpr *left, struct condexpr *right)
{
struct condexpr *cx;
cx = condexpr_create(CX_OR);
cx->cx_or.left = left;
cx->cx_or.right = right;
return cx;
}
/************************************************************/
static void
setmachine(const char *mch, const char *mcharch, struct nvlist *mchsubarches,
int isioconf)
{
char buf[MAXPATHLEN];
struct nvlist *nv;
if (isioconf) {
if (include(_PATH_DEVNULL, ENDDEFS, 0, 0) != 0)
exit(1);
ioconfname = mch;
return;
}
machine = mch;
machinearch = mcharch;
machinesubarches = mchsubarches;
/*
* Define attributes for all the given names
*/
if (defattr(machine, NULL, NULL, 0) != 0 ||
(machinearch != NULL &&
defattr(machinearch, NULL, NULL, 0) != 0))
exit(1);
for (nv = machinesubarches; nv != NULL; nv = nv->nv_next) {
if (defattr(nv->nv_name, NULL, NULL, 0) != 0)
exit(1);
}
/*
* Set up the file inclusion stack. This empty include tells
* the parser there are no more device definitions coming.
*/
if (include(_PATH_DEVNULL, ENDDEFS, 0, 0) != 0)
exit(1);
/* Include arch/${MACHINE}/conf/files.${MACHINE} */
(void)snprintf(buf, sizeof(buf), "arch/%s/conf/files.%s",
machine, machine);
if (include(buf, ENDFILE, 0, 0) != 0)
exit(1);
/* Include any arch/${MACHINE_SUBARCH}/conf/files.${MACHINE_SUBARCH} */
for (nv = machinesubarches; nv != NULL; nv = nv->nv_next) {
(void)snprintf(buf, sizeof(buf), "arch/%s/conf/files.%s",
nv->nv_name, nv->nv_name);
if (include(buf, ENDFILE, 0, 0) != 0)
exit(1);
}
/* Include any arch/${MACHINE_ARCH}/conf/files.${MACHINE_ARCH} */
if (machinearch != NULL)
(void)snprintf(buf, sizeof(buf), "arch/%s/conf/files.%s",
machinearch, machinearch);
else
strlcpy(buf, _PATH_DEVNULL, sizeof(buf));
if (include(buf, ENDFILE, 0, 0) != 0)
exit(1);
/*
* Include the global conf/files. As the last thing
* pushed on the stack, it will be processed first.
*/
if (include("conf/files", ENDFILE, 0, 0) != 0)
exit(1);
oktopackage = 1;
}
static void
check_maxpart(void)
{
if (maxpartitions <= 0 && ioconfname == NULL) {
stop("cannot proceed without maxpartitions specifier");
}
}
static void
check_version(void)
{
/*
* In essence, version is 0 and is not supported anymore
*/
if (version < CONFIG_MINVERSION)
stop("your sources are out of date -- please update.");
}
/*
* Prepend a blank entry to the locator definitions so the code in
* sem.c can distinguish "empty locator list" from "no locator list".
* XXX gross.
*/
static struct loclist *
present_loclist(struct loclist *ll)
{
struct loclist *ret;
ret = MK3(loc, "", NULL, 0);
ret->ll_next = ll;
return ret;
}
static void
app(struct loclist *p, struct loclist *q)
{
while (p->ll_next)
p = p->ll_next;
p->ll_next = q;
}
static struct loclist *
locarray(const char *name, int count, struct loclist *adefs, int opt)
{
struct loclist *defs = adefs;
struct loclist **p;
char buf[200];
int i;
if (count <= 0) {
fprintf(stderr, "config: array with <= 0 size: %s\n", name);
exit(1);
}
p = &defs;
for(i = 0; i < count; i++) {
if (*p == NULL)
*p = MK3(loc, NULL, "0", 0);
snprintf(buf, sizeof(buf), "%s%c%d", name, ARRCHR, i);
(*p)->ll_name = i == 0 ? name : intern(buf);
(*p)->ll_num = i > 0 || opt;
p = &(*p)->ll_next;
}
*p = 0;
return defs;
}
static struct loclist *
namelocvals(const char *name, struct loclist *vals)
{
struct loclist *p;
char buf[200];
int i;
for (i = 0, p = vals; p; i++, p = p->ll_next) {
snprintf(buf, sizeof(buf), "%s%c%d", name, ARRCHR, i);
p->ll_name = i == 0 ? name : intern(buf);
}
return vals;
}