%{ /* $NetBSD: gram.y,v 1.54 2016/08/07 10:37:24 christos 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 __RCSID("$NetBSD: gram.y,v 1.54 2016/08/07 10:37:24 christos Exp $"); #include #include #include #include #include #include #include #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 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 NUMBER %token PATHNAME QSTRING WORD EMPTYSTRING %token ENDDEFS %type fopts condexpr condatom %type cond_or_expr cond_and_expr cond_prefix_expr %type cond_base_expr %type fs_spec %type fflags fflag oflags oflag %type rule %type depend %type devbase %type devattach_opt %type atlist %type interface_opt %type atname %type loclist locdef %type locdefault %type values locdefaults %type depend_list depends %type locators locator %type dev_spec %type device_instance %type attachment %type value %type major_minor %type signed_number %type int32 npseudo device_flags no %type deffs %type deffses %type defopt %type defopts %type optdepend %type optdepends %type optdepend_list %type optfile_opt %type subarches %type filename stringvalue locname mkvarname %type device_major_block device_major_char %type 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 { 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_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_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_lineno = 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; idl_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; }