2035 lines
55 KiB
Plaintext
2035 lines
55 KiB
Plaintext
/*
|
|
* Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* All rights reserved.
|
|
*
|
|
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
|
|
*
|
|
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
%{
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "symbol.h"
|
|
#include "lex.h"
|
|
#include "gen_locl.h"
|
|
#include "der.h"
|
|
|
|
static Type *new_type (Typetype t);
|
|
/*static IOSClass *new_class(struct fieldhead *);*/
|
|
/*static IOSObject *new_object(struct objfieldhead *);*/
|
|
/*IOSObjectSet *new_object_set(struct objectshead *);*/
|
|
static struct objectshead *add_object_set_spec(struct objectshead *, IOSObject *);
|
|
static ObjectField *new_field_setting(char *, Type *, struct value *);
|
|
static struct objfieldhead *add_field_setting(struct objfieldhead *, ObjectField *);
|
|
static struct fieldhead *add_field_spec(struct fieldhead *, Field *);
|
|
static Field *new_type_field(char *, int, Type *);
|
|
static Field *new_fixed_type_value_field(char *, Type *, int, int, struct value *);
|
|
static Type *parametrize_type(Type *, IOSClass *);
|
|
static Type *type_from_class_field(IOSClass *, const char *);
|
|
static void validate_object_set(IOSObjectSet *);
|
|
/*static Type *type_from_object(const char *, const char *);*/
|
|
static struct constraint_spec *new_constraint_spec(enum ctype);
|
|
static Type *new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype);
|
|
void yyerror (const char *);
|
|
#define yyerror yyerror
|
|
static struct objid *new_objid(const char *label, int value);
|
|
static void add_oid_to_tail(struct objid *, struct objid *);
|
|
static void fix_labels(Symbol *s);
|
|
|
|
struct string_list {
|
|
char *string;
|
|
struct string_list *next;
|
|
};
|
|
|
|
static int default_tag_env = TE_EXPLICIT;
|
|
static unsigned long idcounter;
|
|
|
|
/* Declarations for Bison */
|
|
#define YYMALLOC malloc
|
|
#define YYFREE free
|
|
|
|
%}
|
|
|
|
%union {
|
|
int64_t constant;
|
|
struct value *value;
|
|
struct range *range;
|
|
char *name;
|
|
Type *type;
|
|
IOSClass *class;
|
|
IOSObjectSet *objectset;
|
|
IOSObject *object;
|
|
Field *field;
|
|
ObjectField *objfield;
|
|
Member *member;
|
|
IOSClass *formalparam;
|
|
struct objid *objid;
|
|
char *defval;
|
|
struct string_list *sl;
|
|
struct tagtype tag;
|
|
struct memhead *members;
|
|
struct fieldhead *fields;
|
|
struct objectshead *objects;
|
|
struct objfieldhead *objfields;
|
|
struct constraint_spec *constraint_spec;
|
|
}
|
|
|
|
%token kw_ABSENT
|
|
%token kw_ABSTRACT_SYNTAX
|
|
%token kw_ALL
|
|
%token kw_APPLICATION
|
|
%token kw_AUTOMATIC
|
|
%token kw_BEGIN
|
|
%token kw_BIT
|
|
%token kw_BMPString
|
|
%token kw_BOOLEAN
|
|
%token kw_BY
|
|
%token kw_CHARACTER
|
|
%token kw_CHOICE
|
|
%token kw_CLASS
|
|
%token kw_COMPONENT
|
|
%token kw_COMPONENTS
|
|
%token kw_CONSTRAINED
|
|
%token kw_CONTAINING
|
|
%token kw_DEFAULT
|
|
%token kw_DEFINITIONS
|
|
%token kw_EMBEDDED
|
|
%token kw_ENCODED
|
|
%token kw_END
|
|
%token kw_ENUMERATED
|
|
%token kw_EXCEPT
|
|
%token kw_EXPLICIT
|
|
%token kw_EXPORTS
|
|
%token kw_EXTENSIBILITY
|
|
%token kw_EXTERNAL
|
|
%token kw_FALSE
|
|
%token kw_FROM
|
|
%token kw_GeneralString
|
|
%token kw_GeneralizedTime
|
|
%token kw_GraphicString
|
|
%token kw_IA5String
|
|
%token kw_IDENTIFIER
|
|
%token kw_IMPLICIT
|
|
%token kw_IMPLIED
|
|
%token kw_IMPORTS
|
|
%token kw_INCLUDES
|
|
%token kw_INSTANCE
|
|
%token kw_INTEGER
|
|
%token kw_INTERSECTION
|
|
%token kw_ISO646String
|
|
%token kw_MAX
|
|
%token kw_MIN
|
|
%token kw_MINUS_INFINITY
|
|
%token kw_NULL
|
|
%token kw_NumericString
|
|
%token kw_OBJECT
|
|
%token kw_OCTET
|
|
%token kw_OF
|
|
%token kw_OPTIONAL
|
|
%token kw_ObjectDescriptor
|
|
%token kw_PATTERN
|
|
%token kw_PDV
|
|
%token kw_PLUS_INFINITY
|
|
%token kw_PRESENT
|
|
%token kw_PRIVATE
|
|
%token kw_PrintableString
|
|
%token kw_REAL
|
|
%token kw_RELATIVE_OID
|
|
%token kw_SEQUENCE
|
|
%token kw_SET
|
|
%token kw_SIZE
|
|
%token kw_STRING
|
|
%token kw_SYNTAX
|
|
%token kw_T61String
|
|
%token kw_TAGS
|
|
%token kw_TRUE
|
|
%token kw_TYPE_IDENTIFIER
|
|
%token kw_TeletexString
|
|
%token kw_UNION
|
|
%token kw_UNIQUE
|
|
%token kw_UNIVERSAL
|
|
%token kw_UTCTime
|
|
%token kw_UTF8String
|
|
%token kw_UniversalString
|
|
%token kw_VideotexString
|
|
%token kw_VisibleString
|
|
%token kw_WITH
|
|
|
|
%token RANGE
|
|
%token EEQUAL
|
|
%token ELLIPSIS
|
|
|
|
%token <name> TYPE_IDENTIFIER referencename
|
|
%token <name> CLASS_IDENTIFIER
|
|
%token <name> VALUE_IDENTIFIER
|
|
%token <name> STRING
|
|
|
|
%token <constant> NUMBER
|
|
%type <constant> SignedNumber
|
|
%type <constant> Class tagenv
|
|
%type <constant> DummyReference
|
|
|
|
%type <name> Identifier
|
|
|
|
/*
|
|
* The NULL keyword being both a value and a type causes a reduce/reduce
|
|
* conflict in the FieldSetting production since its alternatives are
|
|
*
|
|
* '&' Identifier Type
|
|
*
|
|
* and
|
|
*
|
|
* '&' Identifier Value
|
|
*
|
|
* and NULL is both a type and a value.
|
|
*
|
|
* For now we work around this by having a ValueExNull production that excludes
|
|
* the NULL value. To really get past this will require unifying the type and
|
|
* value types (e.g., via type punning).
|
|
*/
|
|
%type <value> Value ValueExNull
|
|
%type <value> BuiltinValue BuiltinValueExNull
|
|
%type <value> IntegerValue
|
|
%type <value> BooleanValue
|
|
%type <value> ObjectIdentifierValue
|
|
%type <value> CharacterStringValue
|
|
%type <value> NullValue
|
|
%type <value> DefinedValue
|
|
%type <value> ReferencedValue
|
|
%type <value> Valuereference
|
|
|
|
%type <class> DefinedObjectClass ParamGovernor
|
|
%type <class> ObjectClassDefn
|
|
%type <class> Parameter
|
|
|
|
%type <type> Type
|
|
%type <type> BuiltinType
|
|
%type <type> BitStringType
|
|
%type <type> BooleanType
|
|
%type <type> ChoiceType
|
|
%type <type> ConstrainedType
|
|
%type <type> UnconstrainedType
|
|
%type <type> EnumeratedType
|
|
%type <type> IntegerType
|
|
%type <type> NullType
|
|
%type <type> OctetStringType
|
|
%type <type> SequenceType
|
|
%type <type> SequenceOfType
|
|
%type <type> SetType
|
|
%type <type> SetOfType
|
|
%type <type> TaggedType
|
|
%type <type> ReferencedType
|
|
%type <type> DefinedType
|
|
%type <type> UsefulType
|
|
%type <type> ObjectIdentifierType
|
|
%type <type> CharacterStringType
|
|
%type <type> RestrictedCharactedStringType
|
|
%type <type> ObjectClassFieldType
|
|
%type <type> ParameterizedType
|
|
/*%type <type> TypeFromObject*/
|
|
|
|
%type <objectset> ObjectSet DefinedObjectSet
|
|
%type <objectset> ActualParameter
|
|
%type <object> Object DefinedObject ObjectDefn
|
|
%type <objfield> FieldSetting
|
|
|
|
%type <tag> Tag
|
|
|
|
%type <field> FieldSpec TypeFieldSpec FixedTypeValueFieldSpec
|
|
%type <fields> FieldSpecList
|
|
%type <member> ComponentType
|
|
%type <member> NamedBit
|
|
%type <member> NamedNumber
|
|
%type <member> NamedType
|
|
%type <members> ComponentTypeList
|
|
%type <members> Enumerations
|
|
%type <members> NamedBitList
|
|
%type <members> NamedNumberList
|
|
%type <objects> ObjectSetSpec
|
|
%type <objfields> FieldSettings
|
|
|
|
%type <objid> objid objid_list objid_element objid_opt
|
|
%type <range> range size
|
|
|
|
%type <sl> referencenames
|
|
|
|
%type <constraint_spec> Constraint
|
|
%type <constraint_spec> ConstraintSpec
|
|
%type <constraint_spec> SubtypeConstraint
|
|
%type <constraint_spec> GeneralConstraint
|
|
%type <constraint_spec> ContentsConstraint
|
|
%type <constraint_spec> UserDefinedConstraint
|
|
%type <constraint_spec> SimpleTableConstraint TableConstraint
|
|
%type <constraint_spec> ComponentRelationConstraint
|
|
|
|
|
|
%start ModuleDefinition
|
|
|
|
%%
|
|
|
|
/*
|
|
* We have sinned by allowing types to have names that start with lower-case,
|
|
* and values that have names that start with upper-case.
|
|
*
|
|
* UPDATE: We sin no more. However, parts of this block comment are still
|
|
* relevant.
|
|
*
|
|
* That worked when we only supported basic X.680 because the rules for
|
|
* TypeAssignment and ValueAssignment are clearly unambiguous in spite of the
|
|
* case issue.
|
|
*
|
|
* We now pay the price because X.681 adds productions where the only thing we
|
|
* have to help us distinguish certain rules is the form of an identifier: the
|
|
* case of its first letter.
|
|
*
|
|
* We have cleansed our sin by not allowing wrong-case identifiers any more.
|
|
*
|
|
* Some historical instances of this sin in-tree:
|
|
*
|
|
* - DOMAIN-X500-COMPRESS (value (enum) but name starts with upper-case)
|
|
* - krb5int32 (type but name starts with lower-case)
|
|
* - krb5uint32 (type but name starts with lower-case)
|
|
* - hdb_keyset (type but name starts with lower-case)
|
|
* - hdb_entry (type but name starts with lower-case)
|
|
* - hdb_entry_alias (type but name starts with lower-case)
|
|
* - HDB_DB_FORMAT INTEGER (value (int) but name starts with upper-case)
|
|
*
|
|
* We have fixed all of these and others, in some cases leaving behind aliases
|
|
* in header files as needed.
|
|
*
|
|
* We have one shift/reduce conflict (shift ObjectClassAssignment, reduce
|
|
* TypeAssignment) and one reduce/reduce conflict (ObjectAssignment vs
|
|
* ValueAssignment) that we avoid by requiring CLASS names to start with an
|
|
* underscore.
|
|
*
|
|
* In the FieldSetting rule, also, we get a reduce/reduce conflict if we use
|
|
* `Identifier' instead of `TYPE_IDENTIFIER' for type field settings and
|
|
* `VALUE_IDENTIFIER' for value field settings, and then we can't make
|
|
* progress.
|
|
*
|
|
* Looking forward, we may not (will not) be able to distinguish ValueSet and
|
|
* ObjectSet field settings from each other either, and we may not (will not)
|
|
* be able distinguish Object and Value field settings from each other as well.
|
|
* To deal with those we will have to run-time type-tag and type-pun the C
|
|
* structures for valueset/objectset and value/object, and have one rule for
|
|
* each of those that inspects the type of the item to decide what kind of
|
|
* setting it is.
|
|
*
|
|
* Sadly, the extended syntax for ASN.1 (x.680 + x.681/2/3) appears to have
|
|
* ambiguities that cannot be resolved with bison/yacc.
|
|
*/
|
|
Identifier : TYPE_IDENTIFIER { $$ = $1; }
|
|
| VALUE_IDENTIFIER { $$ = $1; };
|
|
|
|
ModuleDefinition: Identifier objid_opt kw_DEFINITIONS TagDefault ExtensionDefault
|
|
EEQUAL kw_BEGIN ModuleBody kw_END
|
|
{
|
|
struct objid **o = objid2list($2);
|
|
size_t i;
|
|
|
|
fprintf(jsonfile,
|
|
"{\"module\":\"%s\",\"tagging\":\"%s\",\"objid\":[", $1,
|
|
default_tag_env == TE_EXPLICIT ? "explicit" : "implicit");
|
|
|
|
for (i = 0; o && o[i]; i++) {
|
|
fprintf(jsonfile, "%s{\"value\":%d", i ? "," : "", o[i]->value);
|
|
if (o[i]->label)
|
|
fprintf(jsonfile, ",\"label\":\"%s\"", o[i]->label);
|
|
fprintf(jsonfile, "}");
|
|
}
|
|
fprintf(jsonfile, "]}\n");
|
|
free(o);
|
|
}
|
|
;
|
|
|
|
TagDefault : kw_EXPLICIT kw_TAGS
|
|
{ default_tag_env = TE_EXPLICIT; }
|
|
| kw_IMPLICIT kw_TAGS
|
|
{ default_tag_env = TE_IMPLICIT; }
|
|
| kw_AUTOMATIC kw_TAGS
|
|
{ lex_error_message("automatic tagging is not supported"); }
|
|
| /* empty */
|
|
;
|
|
|
|
ExtensionDefault: kw_EXTENSIBILITY kw_IMPLIED
|
|
{ lex_error_message("no extensibility options supported"); }
|
|
| /* empty */
|
|
;
|
|
|
|
ModuleBody : Exports Imports AssignmentList
|
|
| /* empty */
|
|
;
|
|
|
|
Imports : kw_IMPORTS SymbolsImported ';'
|
|
| /* empty */
|
|
;
|
|
|
|
SymbolsImported : SymbolsFromModuleList
|
|
| /* empty */
|
|
;
|
|
|
|
SymbolsFromModuleList: SymbolsFromModule
|
|
| SymbolsFromModuleList SymbolsFromModule
|
|
;
|
|
|
|
SymbolsFromModule: referencenames kw_FROM Identifier objid_opt
|
|
{
|
|
/*
|
|
* FIXME We really could use knowing what kind of thing the
|
|
* identifier identifies -- a type, a value, what?
|
|
*
|
|
* Our sin of allowing type names to start with lower-case
|
|
* and values with upper-case means we can't tell. So we
|
|
* assume it's types only, but that means we can't import
|
|
* OID values, but we really want to!
|
|
*
|
|
* One thing we could do is not force `s->stype = Stype'
|
|
* here, instead set it to a new `Sunknown' value so that
|
|
* the first place that refers to this symbol with enough
|
|
* context to imply a symbol type can set it.
|
|
*/
|
|
struct string_list *sl;
|
|
for(sl = $1; sl != NULL; sl = sl->next) {
|
|
Symbol *s = addsym(sl->string);
|
|
s->stype = Stype;
|
|
gen_template_import(s);
|
|
}
|
|
add_import($3);
|
|
}
|
|
;
|
|
|
|
Exports : kw_EXPORTS referencenames ';'
|
|
{
|
|
struct string_list *sl;
|
|
for(sl = $2; sl != NULL; sl = sl->next)
|
|
add_export(sl->string);
|
|
}
|
|
| kw_EXPORTS kw_ALL
|
|
| /* empty */
|
|
;
|
|
|
|
AssignmentList : Assignment
|
|
| Assignment AssignmentList
|
|
;
|
|
|
|
Assignment : TypeAssignment
|
|
| ValueAssignment
|
|
| ParameterizedTypeAssignment
|
|
| ObjectClassAssignment
|
|
| ObjectAssignment
|
|
| ObjectSetAssignment
|
|
/* | ParameterizedAssignment // from X.683 */
|
|
;
|
|
|
|
referencenames : Identifier ',' referencenames
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->string = $1;
|
|
$$->next = $3;
|
|
}
|
|
| Identifier
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->string = $1;
|
|
$$->next = NULL;
|
|
}
|
|
;
|
|
|
|
DefinedObjectClass
|
|
: CLASS_IDENTIFIER
|
|
{
|
|
Symbol *s = addsym($1);
|
|
if(s->stype != Sclass)
|
|
lex_error_message ("%s is not a class\n", $1);
|
|
$$ = s->iosclass;
|
|
};
|
|
|
|
ObjectClassAssignment
|
|
: CLASS_IDENTIFIER EEQUAL ObjectClassDefn
|
|
{
|
|
Symbol *s = addsym($1);
|
|
s->stype = Sclass;
|
|
s->iosclass = $3;
|
|
s->iosclass->symbol = s;
|
|
fix_labels(s);
|
|
}
|
|
| CLASS_IDENTIFIER EEQUAL DefinedObjectClass
|
|
{
|
|
Symbol *s = addsym($1);
|
|
s->stype = Sclass;
|
|
s->iosclass = $3;
|
|
}
|
|
/* | ParameterizedObjectClass */
|
|
;
|
|
|
|
ObjectClassDefn : kw_CLASS '{' FieldSpecList '}'
|
|
{
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->fields = $3;
|
|
$$->id = idcounter++;
|
|
};
|
|
|
|
ObjectAssignment: VALUE_IDENTIFIER DefinedObjectClass EEQUAL Object
|
|
{
|
|
Symbol *s = addsym($1);
|
|
s->stype = Sobj;
|
|
s->object = $4;
|
|
s->object->iosclass = $2;
|
|
if (!s->object->symbol)
|
|
s->object->symbol = s;
|
|
fix_labels(s);
|
|
}
|
|
;
|
|
|
|
ObjectSetAssignment
|
|
: TYPE_IDENTIFIER DefinedObjectClass EEQUAL ObjectSet
|
|
{
|
|
Symbol *s = addsym($1);
|
|
s->stype = Sobjset;
|
|
s->iosclass = $2;
|
|
s->objectset = $4;
|
|
s->objectset->symbol = s->objectset->symbol ? s->objectset->symbol : s;
|
|
s->objectset->iosclass = $2;
|
|
validate_object_set($4);
|
|
generate_template_objectset_forwards(s);
|
|
}
|
|
;
|
|
|
|
ObjectSet : '{' ObjectSetSpec '}'
|
|
{
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->objects = $2;
|
|
$$->id = idcounter++;
|
|
}
|
|
;
|
|
|
|
ObjectSetSpec : DefinedObject
|
|
{ $$ = add_object_set_spec(NULL, $1); }
|
|
| ObjectSetSpec '|' DefinedObject
|
|
{ $$ = add_object_set_spec($1, $3); }
|
|
;
|
|
|
|
Object : DefinedObject
|
|
| ObjectDefn
|
|
/* | ObjectFromObject */
|
|
/* | ParameterizedObject */
|
|
;
|
|
|
|
DefinedObject : VALUE_IDENTIFIER
|
|
{
|
|
Symbol *s = addsym($1);
|
|
if(s->stype != Sobj)
|
|
lex_error_message ("%s is not an object\n", $1);
|
|
$$ = s->object;
|
|
}
|
|
;
|
|
|
|
DefinedObjectSet: TYPE_IDENTIFIER
|
|
{
|
|
Symbol *s = addsym($1);
|
|
if(s->stype != Sobjset && s->stype != SUndefined)
|
|
lex_error_message ("%s is not an object set\n", $1);
|
|
$$ = s->objectset;
|
|
}
|
|
;
|
|
|
|
|
|
ObjectDefn : '{' FieldSettings '}' /* DefaultSyntax */
|
|
{
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->objfields = $2;
|
|
$$->id = idcounter++;
|
|
}
|
|
/* | DefinedSyntax */
|
|
;
|
|
|
|
FieldSettings : FieldSetting
|
|
{
|
|
$$ = add_field_setting(NULL, $1);
|
|
}
|
|
| FieldSettings ',' FieldSetting
|
|
{
|
|
$$ = add_field_setting($1, $3);
|
|
}
|
|
;
|
|
|
|
/* See note on `Identifier' */
|
|
FieldSetting : '&' Identifier Type
|
|
{ $$ = new_field_setting($2, $3, NULL); }
|
|
| '&' Identifier ValueExNull
|
|
{ $$ = new_field_setting($2, NULL, $3); }
|
|
/* | '&' TYPE_IDENTIFIER ValueSet */
|
|
/* | '&' VALUE_IDENTIFIER Object */
|
|
/* | '&' TYPE_IDENTIFIER ObjectSet */
|
|
;
|
|
|
|
/* Fields of a class */
|
|
FieldSpecList : FieldSpec
|
|
{ $$ = add_field_spec(NULL, $1); }
|
|
| FieldSpecList ',' FieldSpec
|
|
{ $$ = add_field_spec($1, $3); };
|
|
|
|
/*
|
|
* Fields of a CLASS
|
|
*
|
|
* There are seven kinds of class/object fields:
|
|
*
|
|
* - type fields,
|
|
* - fixed-type value fields,
|
|
* - fixed-type value set fields,
|
|
* - variable-type value fields
|
|
* - variable-type value set fields
|
|
* - object fields
|
|
* - object set fields
|
|
*
|
|
* We care only to support the bare minimum to treat open types as a CHOICE of
|
|
* sorts and automatically encode/decode values in open types. That's: type
|
|
* fields and fixed-type value fields.
|
|
*/
|
|
FieldSpec : TypeFieldSpec
|
|
| FixedTypeValueFieldSpec
|
|
/* | VariableTypeValueFieldSpec */
|
|
/* | VariableTypeValueSetFieldSpec */
|
|
/* | FixedTypeValueSetFieldSpec */
|
|
/* | ObjectFieldSpec */
|
|
/* | ObjectSetFieldSpec */
|
|
;
|
|
TypeFieldSpec : '&' Identifier
|
|
{ $$ = new_type_field($2, 0, NULL); }
|
|
| '&' Identifier kw_OPTIONAL
|
|
{ $$ = new_type_field($2, 1, NULL); }
|
|
| '&' Identifier kw_DEFAULT Type
|
|
{ $$ = new_type_field($2, 1, $4); }
|
|
;
|
|
|
|
FixedTypeValueFieldSpec
|
|
: '&' Identifier Type
|
|
{ $$ = new_fixed_type_value_field($2, $3, 0, 0, NULL); }
|
|
| '&' Identifier Type kw_UNIQUE
|
|
{ $$ = new_fixed_type_value_field($2, $3, 1, 0, NULL); }
|
|
| '&' Identifier Type kw_UNIQUE kw_OPTIONAL
|
|
{ $$ = new_fixed_type_value_field($2, $3, 1, 1, NULL); }
|
|
| '&' Identifier Type kw_UNIQUE kw_DEFAULT Value
|
|
{ $$ = new_fixed_type_value_field($2, $3, 1, 0, $6); }
|
|
| '&' Identifier Type kw_OPTIONAL
|
|
{ $$ = new_fixed_type_value_field($2, $3, 0, 1, NULL); }
|
|
| '&' Identifier Type kw_DEFAULT Value
|
|
{ $$ = new_fixed_type_value_field($2, $3, 0, 0, $5); };
|
|
|
|
/*
|
|
* Now we need a bit of X.683, just enough to parse PKIX.
|
|
*
|
|
* E.g., we need to parse this sort of type definition, which isn't quite the
|
|
* final type definition because the ExtensionSet will be provided later.
|
|
*
|
|
*-- <- ObjectClassDefn ->
|
|
* EXTENSION ::= CLASS {
|
|
* &id OBJECT IDENTIFIER UNIQUE,
|
|
* -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* -- FixedTypeValueFieldSpec
|
|
*
|
|
* &ExtnType,
|
|
* -- ^^^^^^^^^
|
|
* -- TypeFieldSpec
|
|
*
|
|
* &Critical BOOLEAN DEFAULT {TRUE | FALSE }
|
|
* -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* -- FixedTypeValueFieldSpec
|
|
* } WITH SYNTAX {
|
|
* SYNTAX &ExtnType IDENTIFIED BY &id
|
|
* [CRITICALITY &Critical]
|
|
* }
|
|
*
|
|
*-- <--------- ParameterizedTypeAssignment -------->
|
|
* -- NOTE: The name of this type has to be Extension, really.
|
|
* -- But the name of the Extension type with the actual
|
|
* -- parameter provided also has to be Extension.
|
|
* -- We could disallow that and require that the various
|
|
* -- Extension types all have different names, then we'd
|
|
* -- let the one with the actual parameter in PKIX be the
|
|
* -- one named Extension. Or we could find a way to let
|
|
* -- them all share one symbol name, or at least two:
|
|
* -- the one with the formal parameter, and just one with
|
|
* -- an actual parameter.
|
|
* --
|
|
* -- Also, IMPORTing types that have formal parameters is
|
|
* -- almost certainly going to require parsing the IMPORTed
|
|
* -- module. Until we do that, users will be able to work
|
|
* -- around that by just copying CLASSes and pameterized
|
|
* -- type definitions around. But when we do start parsing
|
|
* -- IMPORTed modules we might need to do something about
|
|
* -- many types possibly having the same names, though we
|
|
* -- might do nothing and simply say "don't do that!".
|
|
* Extension{EXTENSION:ExtensionSet} ::= SEQUENCE {
|
|
* -- ^^^^^^^^^^^^
|
|
* -- is a DummyReference, which is a Reference, basically
|
|
* -- it is an object set variable which will have an object
|
|
* -- set value supplied where constrained types are defined
|
|
* -- from this one, possibly anonymous types where
|
|
* -- SEQUENCE/SET members of this type are defined.
|
|
* -- ^^^^^^^^^
|
|
* -- is a ParamGovernor, really, just Governor, either a Type or
|
|
* -- DefinedObjectClass (we only need DefinedObjectClass)
|
|
* -- ^^^^^^^^^^^^^^^^^^^^^^
|
|
* -- is a Parameter
|
|
* -- ^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* -- is a ParameterList (we need only support one param though)
|
|
* extnID EXTENSION.&id({ExtensionSet}),
|
|
* -- ^^^^^^^^^^^^^^^^
|
|
* -- simple table constraint limiting id to OIDs
|
|
* -- from ExtensionSet
|
|
* -- ^^^^^^^^^^^^^
|
|
* -- a reference to the id field of the EXTENSION CLASS
|
|
* critical BOOLEAN DEFAULT FALSE,
|
|
* extnValue OCTET STRING (CONTAINING
|
|
* -- ObjectClassFieldType
|
|
* -- vvvvvvvvvvvvvvvvvvv
|
|
* EXTENSION.&ExtnType({ExtensionSet}{@extnID}))
|
|
* -- ^^^^^^^^^
|
|
* -- AtNotation
|
|
* -- ^^^^^^^^^^^^^^
|
|
* -- DefinedObjectSet
|
|
* -- ^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* -- ComponentRelationConstraint
|
|
* -- says that extnValue will contain
|
|
* -- a value of a type identified by
|
|
* -- the OID in extnID in the object
|
|
* -- set ExtensionSet (which is a set
|
|
* -- of {OID, type} objects)
|
|
* -- ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* -- ConstraintSpec
|
|
* -- ^^^^^^^^^^^^^^^^^^^
|
|
* -- another type ref
|
|
* }
|
|
*
|
|
* Then later we'll see (ParameterizedType, a part of DefinedType):
|
|
*
|
|
* TBSCertificate ::= SEQUENCE {
|
|
* ...
|
|
* -- Here is where the object set is linked into the
|
|
* -- whole thing, making *magic* possible. This is
|
|
* -- where the real Extensions type is defined. Sadly
|
|
* -- this might mean we can't have a C type named
|
|
* -- Extensions. Hmmm. We might need an ASN.1
|
|
* -- extension that lets use do this:
|
|
* --
|
|
* -- Extension ::= Extension{{CertExtensions}}
|
|
* --
|
|
* -- or
|
|
* --
|
|
* -- Extension ::= ParameterizedExtension{{CertExtensions}}
|
|
* --
|
|
* -- and then rename the Extension type above to this.
|
|
* -- Then we can define Extensions as a SEQUENCE OF
|
|
* -- that.
|
|
* --
|
|
* -- <- ParameterizedType ->
|
|
* extensions [3] Extensions{{CertExtensions}} OPTIONAL
|
|
* -- ^^^^^^^^^^^^^^
|
|
* -- ObjectSetSpec
|
|
* -- ^^^^^^^^^^^^^^^^
|
|
* -- ObjectSet
|
|
* -- ^^^^^^^^^^^^^^^^^^
|
|
* -- ActualParameterList
|
|
* -- ^^^^^^^^^^
|
|
* -- Type
|
|
* }
|
|
*
|
|
* Then:
|
|
*
|
|
* -- Object set, limits what Extensions can be in TBSCertificate.
|
|
*-- <- ObjectSetAssignment ->
|
|
* CertExtensions EXTENSION ::= {
|
|
* -- ^^^^^^^^^
|
|
* -- DefinedObjectClass
|
|
*-- ^^^^^^^^^^^^^^
|
|
*-- objectsetreference, for us, IDENTIFIER
|
|
* ext-AuthorityKeyIdentifier | ext-SubjectKeyIdentifier | ...
|
|
* }
|
|
*
|
|
* and:
|
|
*
|
|
* -- ObjectAssignment (with defined syntax, which we're not going to support):
|
|
* --
|
|
* -- Defines one particular object in the CertExtensions object set.
|
|
* -- We don't need th SYNTAX bits though -- ETOOMUCHWORK.
|
|
* -- This says that the OID id-ce-authorityKeyIdentifier means the extnValue
|
|
* -- is a DER-encoded AuthorityKeyIdentifier.
|
|
* ext-AuthorityKeyIdentifier EXTENSION ::= { SYNTAX
|
|
* AuthorityKeyIdentifier IDENTIFIED BY
|
|
* id-ce-authorityKeyIdentifier }
|
|
* id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
|
|
*
|
|
* -- ObjectAssignment (with default syntax):
|
|
* ext-AuthorityKeyIdentifier EXTENSION ::= {
|
|
* -- fields don't have to be in order since we have the field names
|
|
* &extnId id-ce-authorityKeyIdentifier,
|
|
* &extnValue AuthorityKeyIdentifier
|
|
* }
|
|
*
|
|
* -- Plain old type def using only X.680
|
|
* AuthorityKeyIdentifier ::= SEQUENCE {
|
|
* keyIdentifier [0] KeyIdentifier OPTIONAL,
|
|
* authorityCertIssuer [1] GeneralNames OPTIONAL,
|
|
* authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
|
|
*
|
|
* In terms of compilation, we'll want to support only the template backend,
|
|
* though we'll generate the same C types for both, the template backend and
|
|
* the codegen backend.
|
|
*
|
|
* The generators should see a type for Extension that includes a) the
|
|
* parametrization (relating members in the SEQUENCE to fields in the CLASS),
|
|
* and b) the object set CertExtensions for the _same_ class.
|
|
*
|
|
* - The C types for ASN.1 parametrized types with object set parameters
|
|
* should be laid out just as before, but with additional fields:
|
|
*
|
|
* typedef struct Extension {
|
|
* heim_oid extnID;
|
|
* int *critical;
|
|
* heim_octet_string extnValue;
|
|
* // NEW FIELDS BELOW
|
|
* enum {
|
|
* opentypechoice_unknown_Extension = 0
|
|
* opentypechoice_Extension_id_ce_authorityKeyIdentifier,
|
|
* ...
|
|
* } _element;
|
|
* union {
|
|
* // er, what should this be named?! we have no name information
|
|
* // and naming it after its object value name is probably not a good
|
|
* // idea or not easy. We do have the OID value and possible name
|
|
* // though, so we should use that:
|
|
* AuthorityKeyIdentifier id_ce_authorityKeyIdentifier;
|
|
* ...
|
|
* } _u;
|
|
* } Extension;
|
|
*
|
|
* - The template for this should consist of new struct asn1_template entries
|
|
* following the ones for the normal fields of Extension. The first of these
|
|
* should have an OP that indicates that the following N entries correspond
|
|
* to the object set that specifies this open type, then the following N
|
|
* entries should each point to an object in the object set. Or maybe the
|
|
* object set should be a separate template -- either way. We'll also want a
|
|
* flag to indicate whether the object set is sorted (none of the type IDs
|
|
* are IMPORTed) or not (some of the type IDs are IMPORTed) so we can binary
|
|
* search the object set at encode/decode time.
|
|
*
|
|
* Hmm, we can assume the object sets are already sorted when there's
|
|
* IMPORTed IDs -- the author can do it. Yes, they're sets, but lexically
|
|
* they must be in some order.
|
|
*
|
|
* I like that, actually, requiring that the module author manually sort the
|
|
* object sets, at least when they refer to type IDs that are IMPORTed. Or
|
|
* maybe forbid object sets that use IMPORTed type IDs -- the module author
|
|
* can always copy their definitions anyways.
|
|
*/
|
|
|
|
TypeAssignment : Identifier EEQUAL Type
|
|
{
|
|
Symbol *s = addsym($1);
|
|
s->stype = Stype;
|
|
s->type = $3;
|
|
fix_labels(s);
|
|
|
|
/*
|
|
* Hack: make sure that non-anonymous enumeration types get
|
|
* a symbol tacked on so we can generate a template for
|
|
* their members for value printing.
|
|
*/
|
|
if (s->type->type == TTag && $3->symbol == NULL &&
|
|
$3->subtype != NULL && $3->subtype->type == TInteger &&
|
|
$3->subtype->symbol == NULL) {
|
|
$3->subtype->symbol = s;
|
|
}
|
|
if (original_order)
|
|
generate_type(s);
|
|
else
|
|
generate_type_header_forwards(s);
|
|
}
|
|
;
|
|
|
|
ParameterizedTypeAssignment
|
|
/* For now we'll only support one parameter -- enough for PKIX */
|
|
: Identifier '{' Parameter '}' EEQUAL Type
|
|
{
|
|
char *pname = NULL;
|
|
Symbol *s;
|
|
|
|
if (asprintf(&pname, "%s{%s:x}", $1, $3->symbol->name) == -1 ||
|
|
pname == NULL)
|
|
err(1, "Out of memory");
|
|
s = addsym(pname);
|
|
free($1);
|
|
s->stype = Sparamtype;
|
|
s->type = parametrize_type($6, $3);
|
|
s->type->symbol = s;
|
|
fix_labels(s);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* We're not going to support governor variables for now. We don't need to.
|
|
*
|
|
* Also, we're not going to support more than one formal parameter.
|
|
* Correspondingly we'll only support a single actual parameter (the count of
|
|
* formal and actual parameters has to match, naturally).
|
|
*/
|
|
|
|
Parameter : ParamGovernor ':' DummyReference
|
|
{ $$ = $1; };
|
|
/* | DummyReference */
|
|
;
|
|
|
|
DummyReference : TYPE_IDENTIFIER { $$ = idcounter++; };
|
|
|
|
ParamGovernor : DefinedObjectClass
|
|
{ $$ = $1; }
|
|
/* | DummyGovernor */
|
|
/* | Type */
|
|
;
|
|
|
|
UnconstrainedType : BitStringType
|
|
| BooleanType
|
|
| CharacterStringType
|
|
| ChoiceType
|
|
| EnumeratedType
|
|
| IntegerType
|
|
| NullType
|
|
| ObjectIdentifierType
|
|
| OctetStringType
|
|
| SequenceType
|
|
| SetType
|
|
| ObjectClassFieldType; /* X.681 */
|
|
|
|
Type : BuiltinType | ReferencedType | ConstrainedType ;
|
|
|
|
BuiltinType : BitStringType
|
|
| BooleanType
|
|
| CharacterStringType
|
|
| ChoiceType
|
|
| EnumeratedType
|
|
| IntegerType
|
|
| NullType
|
|
| ObjectIdentifierType
|
|
| OctetStringType
|
|
| SequenceType
|
|
| SequenceOfType
|
|
| SetType
|
|
| SetOfType
|
|
| TaggedType
|
|
| ObjectClassFieldType /* X.681 */
|
|
/* | InstanceOfType // X.681 */
|
|
;
|
|
|
|
ObjectClassFieldType
|
|
: DefinedObjectClass '.' '&' Identifier
|
|
{ $$ = type_from_class_field($1, $4); };
|
|
|
|
BooleanType : kw_BOOLEAN
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Boolean,
|
|
TE_EXPLICIT, new_type(TBoolean));
|
|
}
|
|
;
|
|
|
|
/*
|
|
* The spec says the values in a ValueRange are Values, but a) all
|
|
* the various value ranges do not involve OBJECT IDENTIFIER, b)
|
|
* we only support integer value ranges at this time (as opposed
|
|
* to, e.g., time ranges, and we don't even support time values at
|
|
* this time), c) allowing OBJECT IDENTIFIER here causes a
|
|
* shift-reduce conflict, so we limit ourselves to integer values
|
|
* in ranges. We could always define IntegerValueRange,
|
|
* TimeValueRange, etc. when we add support for more value types.
|
|
*/
|
|
range : IntegerValue RANGE IntegerValue
|
|
{
|
|
if($1->type != integervalue)
|
|
lex_error_message("Non-integer used in first part of range");
|
|
if($1->type != integervalue)
|
|
lex_error_message("Non-integer in second part of range");
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->min = $1->u.integervalue;
|
|
$$->max = $3->u.integervalue;
|
|
}
|
|
| IntegerValue RANGE kw_MAX
|
|
{
|
|
if($1->type != integervalue)
|
|
lex_error_message("Non-integer in first part of range");
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->min = $1->u.integervalue;
|
|
$$->max = INT_MAX;
|
|
}
|
|
| kw_MIN RANGE IntegerValue
|
|
{
|
|
if($3->type != integervalue)
|
|
lex_error_message("Non-integer in second part of range");
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->min = INT_MIN;
|
|
$$->max = $3->u.integervalue;
|
|
}
|
|
| IntegerValue
|
|
{
|
|
if($1->type != integervalue)
|
|
lex_error_message("Non-integer used in limit");
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->min = $1->u.integervalue;
|
|
$$->max = $1->u.integervalue;
|
|
}
|
|
;
|
|
|
|
|
|
IntegerType : kw_INTEGER
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Integer,
|
|
TE_EXPLICIT, new_type(TInteger));
|
|
}
|
|
| kw_INTEGER '{' NamedNumberList '}'
|
|
{
|
|
$$ = new_type(TInteger);
|
|
$$->members = $3;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, $$);
|
|
}
|
|
;
|
|
|
|
NamedNumberList : NamedNumber
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
HEIM_TAILQ_INIT($$);
|
|
HEIM_TAILQ_INSERT_HEAD($$, $1, members);
|
|
}
|
|
| NamedNumberList ',' NamedNumber
|
|
{
|
|
HEIM_TAILQ_INSERT_TAIL($1, $3, members);
|
|
$$ = $1;
|
|
}
|
|
| NamedNumberList ',' ELLIPSIS
|
|
{ $$ = $1; } /* XXX used for Enumerations */
|
|
;
|
|
|
|
NamedNumber : Identifier '(' SignedNumber ')'
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->name = $1;
|
|
$$->gen_name = estrdup($1);
|
|
output_name ($$->gen_name);
|
|
$$->val = $3;
|
|
$$->optional = 0;
|
|
$$->ellipsis = 0;
|
|
$$->type = NULL;
|
|
}
|
|
| Identifier '(' DefinedValue ')'
|
|
{
|
|
if ($3->type != integervalue)
|
|
lex_error_message("Named number %s not a numeric value",
|
|
$3->s->name);
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->name = $1;
|
|
$$->gen_name = estrdup($1);
|
|
output_name ($$->gen_name);
|
|
$$->val = $3->u.integervalue;
|
|
$$->optional = 0;
|
|
$$->ellipsis = 0;
|
|
$$->type = NULL;
|
|
}
|
|
;
|
|
|
|
EnumeratedType : kw_ENUMERATED '{' Enumerations '}'
|
|
{
|
|
$$ = new_type(TInteger);
|
|
$$->members = $3;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Enumerated, TE_EXPLICIT, $$);
|
|
}
|
|
;
|
|
|
|
Enumerations : NamedNumberList /* XXX */
|
|
;
|
|
|
|
BitStringType : kw_BIT kw_STRING
|
|
{
|
|
$$ = new_type(TBitString);
|
|
$$->members = emalloc(sizeof(*$$->members));
|
|
HEIM_TAILQ_INIT($$->members);
|
|
$$ = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, $$);
|
|
}
|
|
| kw_BIT kw_STRING '{' NamedBitList '}'
|
|
{
|
|
$$ = new_type(TBitString);
|
|
$$->members = $4;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, $$);
|
|
}
|
|
;
|
|
|
|
ObjectIdentifierType: kw_OBJECT kw_IDENTIFIER
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_OID,
|
|
TE_EXPLICIT, new_type(TOID));
|
|
}
|
|
;
|
|
OctetStringType : kw_OCTET kw_STRING size
|
|
{
|
|
Type *t = new_type(TOctetString);
|
|
t->range = $3;
|
|
if (t->range) {
|
|
if (t->range->min < 0)
|
|
lex_error_message("can't use a negative SIZE range "
|
|
"length for OCTET STRING");
|
|
}
|
|
$$ = new_tag(ASN1_C_UNIV, UT_OctetString,
|
|
TE_EXPLICIT, t);
|
|
}
|
|
;
|
|
|
|
NullType : kw_NULL
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Null,
|
|
TE_EXPLICIT, new_type(TNull));
|
|
}
|
|
;
|
|
|
|
size :
|
|
{ $$ = NULL; }
|
|
| kw_SIZE '(' range ')'
|
|
{ $$ = $3; }
|
|
;
|
|
|
|
|
|
SequenceType : kw_SEQUENCE '{' /* ComponentTypeLists */ ComponentTypeList '}'
|
|
{
|
|
$$ = new_type(TSequence);
|
|
$$->members = $3;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, $$);
|
|
}
|
|
| kw_SEQUENCE '{' '}'
|
|
{
|
|
$$ = new_type(TSequence);
|
|
$$->members = NULL;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, $$);
|
|
}
|
|
;
|
|
|
|
SequenceOfType : kw_SEQUENCE size kw_OF Type
|
|
{
|
|
$$ = new_type(TSequenceOf);
|
|
$$->range = $2;
|
|
if ($$->range) {
|
|
if ($$->range->min < 0)
|
|
lex_error_message("can't use a negative SIZE range "
|
|
"length for SEQUENCE OF");
|
|
}
|
|
|
|
$$->subtype = $4;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, $$);
|
|
}
|
|
;
|
|
|
|
SetType : kw_SET '{' /* ComponentTypeLists */ ComponentTypeList '}'
|
|
{
|
|
$$ = new_type(TSet);
|
|
$$->members = $3;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, $$);
|
|
}
|
|
| kw_SET '{' '}'
|
|
{
|
|
$$ = new_type(TSet);
|
|
$$->members = NULL;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, $$);
|
|
}
|
|
;
|
|
|
|
SetOfType : kw_SET kw_OF Type
|
|
{
|
|
$$ = new_type(TSetOf);
|
|
$$->subtype = $3;
|
|
$$ = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, $$);
|
|
}
|
|
;
|
|
|
|
ChoiceType : kw_CHOICE '{' /* AlternativeTypeLists */ ComponentTypeList '}'
|
|
{
|
|
$$ = new_type(TChoice);
|
|
$$->members = $3;
|
|
}
|
|
;
|
|
|
|
ReferencedType : DefinedType
|
|
| UsefulType
|
|
/* | TypeFromObject // X.681 */
|
|
/* | ValueSetFromObjects // X.681 */
|
|
;
|
|
|
|
/*
|
|
TypeFromObject : VALUE_IDENTIFIER '.' '&' TYPE_IDENTIFIER
|
|
{ $$ = type_from_object($1, $4); };
|
|
*/
|
|
|
|
DefinedType : TYPE_IDENTIFIER
|
|
{
|
|
Symbol *s = addsym($1);
|
|
$$ = new_type(TType);
|
|
if(s->stype != Stype && s->stype != SUndefined)
|
|
lex_error_message ("%s is not a type\n", $1);
|
|
else
|
|
$$->symbol = s;
|
|
}
|
|
| ParameterizedType
|
|
{ $$ = $1; }
|
|
;
|
|
|
|
/*
|
|
* Should be ActualParameterList, but we'll do just one for now
|
|
* as that's enough for PKIX.
|
|
*/
|
|
ParameterizedType
|
|
: Identifier '{' ActualParameter '}' /* XXX ActualParameterList */
|
|
{
|
|
Symbol *s, *ps;
|
|
char *pname = NULL;
|
|
|
|
if ($3 == NULL) {
|
|
lex_error_message("Unknown ActualParameter object set parametrizing %s\n", $1);
|
|
exit(1);
|
|
}
|
|
|
|
/* Lookup the type from a ParameterizedTypeAssignment */
|
|
if (asprintf(&pname, "%s{%s:x}", $1,
|
|
$3->iosclass->symbol->name) == -1 ||
|
|
pname == NULL)
|
|
err(1, "Out of memory");
|
|
ps = addsym(pname);
|
|
if (ps->stype != Sparamtype)
|
|
lex_error_message ("%s is not a parameterized type\n", $1);
|
|
|
|
s = addsym($1);
|
|
$$ = ps->type; /* XXX copy, probably */
|
|
if (!ps->type)
|
|
errx(1, "Wrong class (%s) parameter for parameterized "
|
|
"type %s", $3->iosclass->symbol->name, $1);
|
|
s->stype = Stype;
|
|
if(s->stype != Stype && s->stype != SUndefined)
|
|
lex_error_message ("%s is not a type\n", $1);
|
|
else
|
|
$$->symbol = s;
|
|
$$->actual_parameter = $3;
|
|
if ($$->type == TTag)
|
|
$$->subtype->actual_parameter = $3;
|
|
}
|
|
|
|
/*
|
|
* Per X.683 $1 for ActualParameter should be any of: a Type, a Value, a
|
|
* ValueSet, a DefinedObjectClass, an Object, or an ObjectSet. For PKIX we
|
|
* need nothing more than an ObjectSet here.
|
|
*
|
|
* Also, we can't lexically or syntactically tell the difference between all
|
|
* these things, though fortunately we can for ObjectSet.
|
|
*/
|
|
ActualParameter : DefinedObjectSet
|
|
{ $$ = $1; };
|
|
|
|
UsefulType : kw_GeneralizedTime
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_GeneralizedTime,
|
|
TE_EXPLICIT, new_type(TGeneralizedTime));
|
|
}
|
|
| kw_UTCTime
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_UTCTime,
|
|
TE_EXPLICIT, new_type(TUTCTime));
|
|
}
|
|
;
|
|
|
|
ConstrainedType : UnconstrainedType Constraint
|
|
{
|
|
$$ = $1;
|
|
if ($2->ctype == CT_RANGE) {
|
|
if ($1->type != TTag || $1->subtype->type != TInteger)
|
|
lex_error_message("RANGE constraints apply only to INTEGER types");
|
|
$$->subtype->range = $2->u.range;
|
|
free($2);
|
|
} else {
|
|
$$->constraint = $2;
|
|
}
|
|
/* if (Constraint.type == contentConstraint) {
|
|
assert(Constraint.u.constraint.type == octetstring|bitstring-w/o-NamedBitList); // remember to check type reference too
|
|
if (Constraint.u.constraint.type) {
|
|
assert((Constraint.u.constraint.type.length % 8) == 0);
|
|
}
|
|
}
|
|
if (Constraint.u.constraint.encoding) {
|
|
type == der-oid|ber-oid
|
|
}
|
|
*/
|
|
}
|
|
;
|
|
|
|
|
|
Constraint : '(' ConstraintSpec ')'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
ConstraintSpec : SubtypeConstraint | GeneralConstraint
|
|
;
|
|
|
|
SubtypeConstraint: range
|
|
{
|
|
$$ = new_constraint_spec(CT_RANGE);
|
|
$$->u.range = $1;
|
|
}
|
|
|
|
GeneralConstraint: ContentsConstraint
|
|
| UserDefinedConstraint
|
|
| TableConstraint
|
|
;
|
|
|
|
ContentsConstraint: kw_CONTAINING Type
|
|
{
|
|
$$ = new_constraint_spec(CT_CONTENTS);
|
|
$$->u.content.type = $2;
|
|
$$->u.content.encoding = NULL;
|
|
}
|
|
| kw_ENCODED kw_BY Value
|
|
{
|
|
if ($3->type != objectidentifiervalue)
|
|
lex_error_message("Non-OID used in ENCODED BY constraint");
|
|
$$ = new_constraint_spec(CT_CONTENTS);
|
|
$$->u.content.type = NULL;
|
|
$$->u.content.encoding = $3;
|
|
}
|
|
| kw_CONTAINING Type kw_ENCODED kw_BY Value
|
|
{
|
|
if ($5->type != objectidentifiervalue)
|
|
lex_error_message("Non-OID used in ENCODED BY constraint");
|
|
$$ = new_constraint_spec(CT_CONTENTS);
|
|
$$->u.content.type = $2;
|
|
$$->u.content.encoding = $5;
|
|
}
|
|
;
|
|
|
|
UserDefinedConstraint: kw_CONSTRAINED kw_BY '{' '}'
|
|
{
|
|
$$ = new_constraint_spec(CT_USER);
|
|
}
|
|
;
|
|
|
|
TableConstraint : SimpleTableConstraint
|
|
{ $$ = $1; }
|
|
| ComponentRelationConstraint
|
|
{ $$ = $1; };
|
|
|
|
SimpleTableConstraint
|
|
: '{' TYPE_IDENTIFIER '}'
|
|
{
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->ctype = CT_TABLE_CONSTRAINT;
|
|
$$->u.content.crel.objectname = $2;
|
|
$$->u.content.crel.membername = 0;
|
|
};
|
|
|
|
/*
|
|
* In X.682, ComponentRelationConstraint is a fantastically more complicated
|
|
* production. The stuff in the second set of braces is a list of AtNotation,
|
|
* and AtNotation is '@' followed by some number of '.'s, followed by a
|
|
* ComponentIdList, which is a non-empty set of identifiers separated by '.'s.
|
|
* The number of '.'s is a "level" used to identify a SET, SEQUENCE, or CHOICE
|
|
* where the path of member identifiers is rooted that ultimately identifies
|
|
* the field providing the constraint.
|
|
*
|
|
* So in
|
|
*
|
|
* extnValue OCTET STRING
|
|
* (CONTAINING
|
|
* EXTENSION.&ExtnType({ExtensionSet}{@extnID}))
|
|
* ^^^^^^^^^^^^^^^^^^^
|
|
* ObjectClassFieldType
|
|
* meaning the open type field
|
|
* &ExtnType of EXTENSION
|
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
* GeneralConstraint
|
|
* ^^^^^^^^^^^^^^^^^^^^^^^
|
|
* ComponentRelationConstraint
|
|
* ^^^^^^^^^^^^^^
|
|
* DefinedObjectSet
|
|
* ^^^^^^^^
|
|
* '{' AtNotation ',' + '}'
|
|
*
|
|
* we have EXTENSION.&ExtnType is the ObjectClassFieldType, and
|
|
* ({ExtensionSet}{@extnID}) is the ComponentRelationConstraint on the
|
|
* extnValue member, where {ExtensionSet} is the DummyReference from the formal
|
|
* parameter of the enclosing parameterized type, and {@extnID} is the
|
|
* AtNotation list identifying the field of the class/objects-in-the-object-set
|
|
* that will be identifying the type of the extnValue field.
|
|
*
|
|
* We need just the one AtNotation component.
|
|
*/
|
|
ComponentRelationConstraint
|
|
: '{' TYPE_IDENTIFIER '}' '{' '@' Identifier '}'
|
|
{
|
|
$$ = ecalloc(1, sizeof(*$$));
|
|
$$->ctype = CT_TABLE_CONSTRAINT;
|
|
$$->u.content.crel.objectname = $2;
|
|
$$->u.content.crel.membername = $6;
|
|
};
|
|
|
|
TaggedType : Tag tagenv Type
|
|
{
|
|
$$ = new_type(TTag);
|
|
$$->tag = $1;
|
|
$$->tag.tagenv = $2;
|
|
if (template_flag) {
|
|
$$->subtype = $3;
|
|
} else if ($2 == TE_IMPLICIT) {
|
|
Type *t = $3;
|
|
|
|
/*
|
|
* FIXME We shouldn't do this... The logic for
|
|
* dealing with IMPLICIT tags belongs elsewhere.
|
|
*/
|
|
while (t->type == TType) {
|
|
if (t->subtype)
|
|
t = t->subtype;
|
|
else if (t->symbol && t->symbol->type)
|
|
t = t->symbol->type;
|
|
else
|
|
break;
|
|
}
|
|
/*
|
|
* IMPLICIT tags of CHOICE types are EXPLICIT
|
|
* instead.
|
|
*/
|
|
if (t->type == TChoice)
|
|
$$->tag.tagenv = TE_EXPLICIT;
|
|
if($3->type == TTag && $2 == TE_IMPLICIT) {
|
|
$$->subtype = $3->subtype;
|
|
free($3);
|
|
} else {
|
|
$$->subtype = $3;
|
|
}
|
|
} else {
|
|
$$->subtype = $3;
|
|
}
|
|
}
|
|
;
|
|
|
|
Tag : '[' Class NUMBER ']'
|
|
{
|
|
$$.tagclass = $2;
|
|
$$.tagvalue = $3;
|
|
$$.tagenv = default_tag_env;
|
|
}
|
|
;
|
|
|
|
Class : /* */
|
|
{
|
|
$$ = ASN1_C_CONTEXT;
|
|
}
|
|
| kw_UNIVERSAL
|
|
{
|
|
$$ = ASN1_C_UNIV;
|
|
}
|
|
| kw_APPLICATION
|
|
{
|
|
$$ = ASN1_C_APPL;
|
|
}
|
|
| kw_PRIVATE
|
|
{
|
|
$$ = ASN1_C_PRIVATE;
|
|
}
|
|
;
|
|
|
|
tagenv : /* */
|
|
{
|
|
$$ = default_tag_env;
|
|
}
|
|
| kw_EXPLICIT
|
|
{
|
|
$$ = default_tag_env;
|
|
}
|
|
| kw_IMPLICIT
|
|
{
|
|
$$ = TE_IMPLICIT;
|
|
}
|
|
;
|
|
|
|
|
|
ValueAssignment : VALUE_IDENTIFIER Type EEQUAL Value
|
|
{
|
|
Symbol *s;
|
|
s = addsym ($1);
|
|
|
|
s->stype = SValue;
|
|
s->value = $4;
|
|
generate_constant (s);
|
|
/*
|
|
* Save this value's name so we can know some name for
|
|
* this value wherever _a_ name may be needed for it.
|
|
*
|
|
* This is useful for OIDs used as type IDs in objects
|
|
* sets of classes with open types. We'll generate
|
|
* enum labels from those OIDs' names.
|
|
*/
|
|
s->value->s = s;
|
|
}
|
|
;
|
|
|
|
CharacterStringType: RestrictedCharactedStringType
|
|
;
|
|
|
|
RestrictedCharactedStringType: kw_GeneralString
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_GeneralString,
|
|
TE_EXPLICIT, new_type(TGeneralString));
|
|
}
|
|
| kw_TeletexString
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_TeletexString,
|
|
TE_EXPLICIT, new_type(TTeletexString));
|
|
}
|
|
| kw_UTF8String
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_UTF8String,
|
|
TE_EXPLICIT, new_type(TUTF8String));
|
|
}
|
|
| kw_PrintableString
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_PrintableString,
|
|
TE_EXPLICIT, new_type(TPrintableString));
|
|
}
|
|
| kw_VisibleString
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_VisibleString,
|
|
TE_EXPLICIT, new_type(TVisibleString));
|
|
}
|
|
| kw_IA5String
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_IA5String,
|
|
TE_EXPLICIT, new_type(TIA5String));
|
|
}
|
|
| kw_BMPString
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_BMPString,
|
|
TE_EXPLICIT, new_type(TBMPString));
|
|
}
|
|
| kw_UniversalString
|
|
{
|
|
$$ = new_tag(ASN1_C_UNIV, UT_UniversalString,
|
|
TE_EXPLICIT, new_type(TUniversalString));
|
|
}
|
|
|
|
;
|
|
|
|
ComponentTypeList: ComponentType
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
HEIM_TAILQ_INIT($$);
|
|
HEIM_TAILQ_INSERT_HEAD($$, $1, members);
|
|
}
|
|
| ComponentTypeList ',' ComponentType
|
|
{
|
|
HEIM_TAILQ_INSERT_TAIL($1, $3, members);
|
|
$$ = $1;
|
|
}
|
|
| ComponentTypeList ',' ELLIPSIS
|
|
{
|
|
struct member *m = ecalloc(1, sizeof(*m));
|
|
m->name = estrdup("...");
|
|
m->gen_name = estrdup("asn1_ellipsis");
|
|
m->ellipsis = 1;
|
|
HEIM_TAILQ_INSERT_TAIL($1, m, members);
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
NamedType : Identifier Type
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->name = $1;
|
|
$$->gen_name = estrdup($1);
|
|
output_name ($$->gen_name);
|
|
$$->type = $2;
|
|
$$->ellipsis = 0;
|
|
}
|
|
;
|
|
|
|
ComponentType : NamedType
|
|
{
|
|
$$ = $1;
|
|
$$->optional = 0;
|
|
$$->defval = NULL;
|
|
}
|
|
| NamedType kw_OPTIONAL
|
|
{
|
|
$$ = $1;
|
|
$$->optional = 1;
|
|
$$->defval = NULL;
|
|
}
|
|
| NamedType kw_DEFAULT Value
|
|
{
|
|
$$ = $1;
|
|
$$->optional = 0;
|
|
$$->defval = $3;
|
|
}
|
|
;
|
|
|
|
NamedBitList : NamedBit
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
HEIM_TAILQ_INIT($$);
|
|
HEIM_TAILQ_INSERT_HEAD($$, $1, members);
|
|
}
|
|
| NamedBitList ',' NamedBit
|
|
{
|
|
HEIM_TAILQ_INSERT_TAIL($1, $3, members);
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
NamedBit : Identifier '(' NUMBER ')'
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->name = $1;
|
|
$$->gen_name = estrdup($1);
|
|
output_name ($$->gen_name);
|
|
$$->val = $3;
|
|
$$->optional = 0;
|
|
$$->ellipsis = 0;
|
|
$$->type = NULL;
|
|
}
|
|
;
|
|
|
|
objid_opt : objid
|
|
| /* empty */ { $$ = NULL; }
|
|
;
|
|
|
|
objid : '{' objid_list '}'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
objid_list : /* empty */
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| objid_element objid_list
|
|
{
|
|
if ($2) {
|
|
$$ = $2;
|
|
add_oid_to_tail($2, $1);
|
|
} else {
|
|
$$ = $1;
|
|
}
|
|
}
|
|
;
|
|
|
|
objid_element : Identifier '(' NUMBER ')'
|
|
{
|
|
$$ = new_objid($1, $3);
|
|
}
|
|
| Identifier
|
|
{
|
|
Symbol *s = addsym($1);
|
|
if(s->stype != SValue ||
|
|
s->value->type != objectidentifiervalue) {
|
|
lex_error_message("%s is not an object identifier\n",
|
|
s->name);
|
|
exit(1);
|
|
}
|
|
$$ = s->value->u.objectidentifiervalue;
|
|
}
|
|
| NUMBER
|
|
{
|
|
$$ = new_objid(NULL, $1);
|
|
}
|
|
;
|
|
|
|
Value : BuiltinValue
|
|
| ReferencedValue
|
|
;
|
|
|
|
ValueExNull : BuiltinValueExNull
|
|
| ReferencedValue
|
|
;
|
|
|
|
BuiltinValue : BooleanValue
|
|
| CharacterStringValue
|
|
| IntegerValue
|
|
| ObjectIdentifierValue
|
|
| NullValue
|
|
;
|
|
|
|
BuiltinValueExNull
|
|
: BooleanValue
|
|
| CharacterStringValue
|
|
| IntegerValue
|
|
| ObjectIdentifierValue
|
|
;
|
|
|
|
ReferencedValue : DefinedValue
|
|
;
|
|
|
|
DefinedValue : Valuereference
|
|
;
|
|
|
|
Valuereference : VALUE_IDENTIFIER
|
|
{
|
|
Symbol *s = addsym($1);
|
|
if(s->stype != SValue)
|
|
lex_error_message ("%s is not a value\n",
|
|
s->name);
|
|
else
|
|
$$ = s->value;
|
|
}
|
|
;
|
|
|
|
CharacterStringValue: STRING
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->type = stringvalue;
|
|
$$->u.stringvalue = $1;
|
|
}
|
|
;
|
|
|
|
BooleanValue : kw_TRUE
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->type = booleanvalue;
|
|
$$->u.booleanvalue = 1;
|
|
}
|
|
| kw_FALSE
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->type = booleanvalue;
|
|
$$->u.booleanvalue = 0;
|
|
}
|
|
;
|
|
|
|
IntegerValue : SignedNumber
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->type = integervalue;
|
|
$$->u.integervalue = $1;
|
|
}
|
|
;
|
|
|
|
SignedNumber : NUMBER
|
|
;
|
|
|
|
NullValue : kw_NULL
|
|
{
|
|
}
|
|
;
|
|
|
|
ObjectIdentifierValue: objid
|
|
{
|
|
$$ = emalloc(sizeof(*$$));
|
|
$$->type = objectidentifiervalue;
|
|
$$->u.objectidentifiervalue = $1;
|
|
}
|
|
;
|
|
|
|
%%
|
|
|
|
void
|
|
yyerror (const char *s)
|
|
{
|
|
lex_error_message ("%s\n", s);
|
|
}
|
|
|
|
static Type *
|
|
new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype)
|
|
{
|
|
Type *t;
|
|
if(oldtype->type == TTag && oldtype->tag.tagenv == TE_IMPLICIT) {
|
|
t = oldtype;
|
|
oldtype = oldtype->subtype; /* XXX */
|
|
} else
|
|
t = new_type (TTag);
|
|
|
|
t->tag.tagclass = tagclass;
|
|
t->tag.tagvalue = tagvalue;
|
|
t->tag.tagenv = tagenv;
|
|
t->subtype = oldtype;
|
|
return t;
|
|
}
|
|
|
|
static struct objid *
|
|
new_objid(const char *label, int value)
|
|
{
|
|
struct objid *s;
|
|
s = emalloc(sizeof(*s));
|
|
s->label = label;
|
|
s->value = value;
|
|
s->next = NULL;
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
add_oid_to_tail(struct objid *head, struct objid *tail)
|
|
{
|
|
struct objid *o;
|
|
o = head;
|
|
while (o->next)
|
|
o = o->next;
|
|
o->next = tail;
|
|
}
|
|
|
|
static Type *
|
|
new_type (Typetype tt)
|
|
{
|
|
Type *t = ecalloc(1, sizeof(*t));
|
|
t->type = tt;
|
|
t->id = idcounter++;
|
|
return t;
|
|
}
|
|
|
|
static struct constraint_spec *
|
|
new_constraint_spec(enum ctype ct)
|
|
{
|
|
struct constraint_spec *c = ecalloc(1, sizeof(*c));
|
|
c->ctype = ct;
|
|
return c;
|
|
}
|
|
|
|
static void fix_labels2(Type *t, const char *prefix);
|
|
static void fix_labels1(struct memhead *members, const char *prefix)
|
|
{
|
|
Member *m;
|
|
|
|
if(members == NULL)
|
|
return;
|
|
HEIM_TAILQ_FOREACH(m, members, members) {
|
|
if (asprintf(&m->label, "%s_%s", prefix, m->gen_name) < 0)
|
|
errx(1, "malloc");
|
|
if (m->label == NULL)
|
|
errx(1, "malloc");
|
|
if(m->type != NULL)
|
|
fix_labels2(m->type, m->label);
|
|
}
|
|
}
|
|
|
|
static void fix_labels2(Type *t, const char *prefix)
|
|
{
|
|
for(; t; t = t->subtype)
|
|
fix_labels1(t->members, prefix);
|
|
}
|
|
|
|
static void
|
|
fix_labels(Symbol *s)
|
|
{
|
|
char *p = NULL;
|
|
if (asprintf(&p, "choice_%s", s->gen_name) < 0 || p == NULL)
|
|
errx(1, "malloc");
|
|
if (s->type)
|
|
fix_labels2(s->type, p);
|
|
free(p);
|
|
}
|
|
|
|
static struct objectshead *
|
|
add_object_set_spec(struct objectshead *lst, IOSObject *o)
|
|
{
|
|
if (lst == NULL) {
|
|
lst = emalloc(sizeof(*lst));
|
|
HEIM_TAILQ_INIT(lst);
|
|
HEIM_TAILQ_INSERT_HEAD(lst, o, objects);
|
|
} else {
|
|
HEIM_TAILQ_INSERT_TAIL(lst, o, objects);
|
|
}
|
|
return lst;
|
|
}
|
|
|
|
static struct objfieldhead *
|
|
add_field_setting(struct objfieldhead *lst, ObjectField *f)
|
|
{
|
|
if (lst == NULL) {
|
|
lst = emalloc(sizeof(*lst));
|
|
HEIM_TAILQ_INIT(lst);
|
|
HEIM_TAILQ_INSERT_HEAD(lst, f, objfields);
|
|
} else {
|
|
HEIM_TAILQ_INSERT_TAIL(lst, f, objfields);
|
|
}
|
|
return lst;
|
|
}
|
|
|
|
static struct fieldhead *
|
|
add_field_spec(struct fieldhead *lst, Field *f)
|
|
{
|
|
if (lst == NULL) {
|
|
lst = emalloc(sizeof(*lst));
|
|
HEIM_TAILQ_INIT(lst);
|
|
HEIM_TAILQ_INSERT_HEAD(lst, f, fields);
|
|
} else {
|
|
HEIM_TAILQ_INSERT_TAIL(lst, f, fields);
|
|
}
|
|
return lst;
|
|
}
|
|
|
|
static ObjectField *
|
|
new_field_setting(char *n, Type *t, struct value *v)
|
|
{
|
|
ObjectField *of;
|
|
|
|
of = ecalloc(1, sizeof(*of));
|
|
of->value = v;
|
|
of->type = t;
|
|
of->name = n;
|
|
return of;
|
|
}
|
|
|
|
static Field *
|
|
new_type_field(char *n, int optional, Type *t)
|
|
{
|
|
Field *f;
|
|
|
|
f = ecalloc(1, sizeof(*f));
|
|
f->optional = optional;
|
|
f->unique = 0;
|
|
f->defval = 0;
|
|
f->type = t;
|
|
f->name = n;
|
|
return f;
|
|
}
|
|
|
|
static Field *
|
|
new_fixed_type_value_field(char *n, Type *t, int unique, int optional, struct value *defval)
|
|
{
|
|
Field *f;
|
|
|
|
f = ecalloc(1, sizeof(*f));
|
|
f->optional = optional;
|
|
f->unique = unique;
|
|
f->defval = defval;
|
|
f->type = t;
|
|
f->name = n;
|
|
return f;
|
|
}
|
|
|
|
static Type *
|
|
parametrize_type(Type *t, IOSClass *c)
|
|
{
|
|
Type *type;
|
|
|
|
type = new_type(TType);
|
|
*type = *t; /* XXX Copy, or use subtype; this only works as long as we don't cleanup! */
|
|
type->formal_parameter = c;
|
|
return type;
|
|
}
|
|
|
|
static Type *
|
|
type_from_class_field(IOSClass *c, const char *n)
|
|
{
|
|
Field *f;
|
|
Type *t;
|
|
|
|
HEIM_TAILQ_FOREACH(f, c->fields, fields) {
|
|
if (strcmp(f->name, n) == 0) {
|
|
t = new_type(TType);
|
|
if (f->type) {
|
|
*t = *f->type;
|
|
} else {
|
|
Symbol *s = addsym("HEIM_ANY");
|
|
if(s->stype != Stype && s->stype != SUndefined)
|
|
errx(1, "Do not define HEIM_ANY, only import it\n");
|
|
s->stype = Stype;
|
|
t->symbol = s;
|
|
}
|
|
t->typeref.iosclass = c;
|
|
t->typeref.field = f;
|
|
return t;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
validate_object_set(IOSObjectSet *os)
|
|
{
|
|
IOSObject **objects;
|
|
ObjectField *of;
|
|
IOSObject *o;
|
|
Field *cf;
|
|
size_t nobjs, i;
|
|
|
|
/* Check unique fields */
|
|
HEIM_TAILQ_FOREACH(cf, os->iosclass->fields, fields) {
|
|
if (!cf->unique)
|
|
continue;
|
|
if (!cf->type)
|
|
errx(1, "Type fields of classes can't be UNIQUE (%s)",
|
|
os->iosclass->symbol->name);
|
|
sort_object_set(os, cf, &objects, &nobjs);
|
|
for (i = 0; i < nobjs; i++) {
|
|
HEIM_TAILQ_FOREACH(of, objects[i]->objfields, objfields) {
|
|
if (strcmp(cf->name, of->name) != 0)
|
|
continue;
|
|
if (!of->value)
|
|
errx(1, "Value not specified for required UNIQUE field %s of object %s",
|
|
cf->name, objects[i]->symbol->name);
|
|
break;
|
|
}
|
|
if (i == 0)
|
|
continue;
|
|
if (object_cmp(&objects[i - 1], &objects[i]) == 0)
|
|
errx(1, "Duplicate values of UNIQUE field %s of objects %s and %s",
|
|
cf->name, objects[i - 1]->symbol->name,
|
|
objects[i]->symbol->name);
|
|
}
|
|
free(objects);
|
|
}
|
|
|
|
/* Check required fields */
|
|
HEIM_TAILQ_FOREACH(cf, os->iosclass->fields, fields) {
|
|
if (cf->optional || cf->defval || !cf->type)
|
|
continue;
|
|
HEIM_TAILQ_FOREACH(o, os->objects, objects) {
|
|
int specified = 0;
|
|
|
|
HEIM_TAILQ_FOREACH(of, o->objfields, objfields) {
|
|
if (strcmp(of->name, cf->name) != 0)
|
|
continue;
|
|
if (of->value)
|
|
specified = 1;
|
|
break;
|
|
}
|
|
if (!specified)
|
|
errx(1, "Value not specified for required non-UNIQUE field %s of object %s",
|
|
cf->name, o->symbol->name);
|
|
}
|
|
}
|
|
}
|