Initial revision

diff --git a/libasn1fix/asn1fix_class.c b/libasn1fix/asn1fix_class.c
new file mode 100644
index 0000000..c541fd6
--- /dev/null
+++ b/libasn1fix/asn1fix_class.c
@@ -0,0 +1,237 @@
+#include "asn1fix_internal.h"
+
+typedef enum field_category {
+	OFC_INVALID,		/* Invalid object field category */
+	OFC_TYPE,
+	OFC_FIXED_TYPE_VALUE,
+	OFC_VARIABLE_TYPE_VALUE,
+	OFC_FIXED_TYPE_VALUE_SET,
+	OFC_VARIABLE_TYPE_VALUE_SET,
+	OFC_INFORMATION_OBJECT,
+	OFC_INFORMATION_OBJECT_SET,
+} field_category_e;
+
+typedef enum object_category {
+	OC_INVALID,
+	OC_OBJECT,
+	OC_OBJECTSET,
+} object_category_e;
+
+static field_category_e  asn1f_class_field_category(asn1p_expr_t *ofield);
+static object_category_e asn1f_class_object_category(asn1p_expr_t *expr);
+static asn1p_expr_t *
+asn1f_class_dot_lookup(arg_t *arg, asn1p_expr_t *obj, asn1p_ref_t *ref);
+
+asn1p_expr_t *
+asn1f_class_access(arg_t *arg, asn1p_ref_t *ref, asn1p_module_t **mod_r) {
+	asn1p_expr_t *obj;		/* Information Object or Object Set */
+	object_category_e obj_cat;	/* Object category */
+	//field_category_e field_cat;	/* Field category */
+	asn1p_expr_t *result;
+	asn1p_ref_t tmpref;
+
+	assert(ref->comp_count > 1);
+
+	DEBUG("%s(%s) for line %d", __func__,
+		asn1f_printable_reference(ref),
+		ref->_lineno);
+
+	/*
+	 * Fetch the first part of the reference (OBJECT or ObjectSet).
+	 * OBJECT.&<something>...
+	 * ObjectSet.&<something>...
+	 */
+	assert(isupper(ref->components[0].name[0]));
+
+	tmpref = *ref;
+	tmpref.comp_count = 1;
+	obj = asn1f_lookup_symbol(arg, &tmpref, 0);
+	if(obj == NULL) {
+		errno = ESRCH;
+		return NULL;
+	}
+
+	/*
+	 * Make sure the symbol lexical property (upper-case, lower-case)
+	 * corresponds to the type of the expression returned by
+	 * lookup_symbol().
+	 */
+	obj_cat = asn1f_class_object_category(obj);
+	switch(obj_cat) {
+	case OC_OBJECT:
+	case OC_OBJECTSET:
+		if(ref->components[0].lex_type
+			== (obj_cat==OC_OBJECT)
+				? RLT_CAPITALS
+				: RLT_Uppercase)
+			break;
+		/* Fall through */
+	case OC_INVALID:
+		WARNING("Symbol \"%s\" is not compatible "
+			"with referenced expression \"%s\" at line %d",
+			ref->components[0].name,
+			obj->Identifier, obj->_lineno);
+		errno = EPERM;
+		return NULL;
+	}
+
+	/*
+	 * Find the specified field within the object.
+	 */
+	result = asn1f_class_dot_lookup(arg, obj, ref);
+	if(result == NULL) {
+		return NULL;
+	}
+
+	//field_cat = asn1f_class_field_category(result);
+
+	DEBUG("FILLME: %s", result->Identifier);
+
+	return result;
+}
+
+static object_category_e
+asn1f_class_object_category(asn1p_expr_t *expr) {
+
+	switch(expr->meta_type) {
+	case AMT_OBJECT:
+		return OC_OBJECT;
+	case AMT_OBJECTSET:
+		return OC_OBJECTSET;
+	case AMT_VALUESET:
+		if(expr->expr_type == A1TC_REFERENCE
+		&& expr->reference
+		&& expr->reference->comp_count == 1
+		&& expr->reference->components[0].lex_type == RLT_CAPITALS)
+		{
+			/* FIXME: use find_terminal_type instead! */
+			return OC_OBJECTSET;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return OC_INVALID;
+}
+
+static field_category_e
+asn1f_class_field_category(asn1p_expr_t *ofield) {
+	if(ofield->Identifier[0] != '&') {
+		assert(ofield->Identifier[0] == '&');
+		return OFC_INVALID;
+	}
+
+	if(isupper(ofield->Identifier[1])) {
+		if(ofield->reference) {
+			enum asn1p_ref_lex_type_e lex_type
+				= ofield->reference->components[0].lex_type;
+
+			switch(lex_type) {
+			case RLT_CAPITALS:
+				return OFC_INFORMATION_OBJECT_SET;
+			case RLT_Uppercase:
+				return OFC_FIXED_TYPE_VALUE_SET;
+			case RLT_AmpUppercase:
+				return OFC_VARIABLE_TYPE_VALUE_SET;
+			default:
+				break;
+			}
+		} else {
+			if(ofield->expr_type == A1TC_CLASSFIELD)
+				return OFC_TYPE;
+
+			switch(ofield->meta_type) {
+			case AMT_TYPE:
+			case AMT_TYPEREF:
+				return OFC_FIXED_TYPE_VALUE_SET;
+			default:
+				break;
+			}
+
+		}
+	} else {
+		if(ofield->reference) {
+			enum asn1p_ref_lex_type_e lex_type
+				= ofield->reference->components[0].lex_type;
+
+			switch(lex_type) {
+			case RLT_CAPITALS:
+				return OFC_INFORMATION_OBJECT;
+			case RLT_Uppercase:
+				return OFC_FIXED_TYPE_VALUE;
+			case RLT_AmpUppercase:
+				return OFC_VARIABLE_TYPE_VALUE;
+			default:
+				break;
+			}
+		} else {
+			switch(ofield->meta_type) {
+			case AMT_TYPE:
+			case AMT_TYPEREF:
+				return OFC_FIXED_TYPE_VALUE;
+			default:
+				break;
+			}
+		}
+	}
+
+	return OFC_INVALID;
+}
+
+
+static asn1p_expr_t *
+asn1f_class_dot_lookup(arg_t *arg, asn1p_expr_t *obj, asn1p_ref_t *ref) {
+	asn1p_expr_t *ofield = NULL;	/* Information Object's Field */
+	field_category_e field_cat;	/* Field category */
+	int comp;
+
+	assert(ref->comp_count >= 2);
+
+	for(comp = 1 /* sic! */; comp < ref->comp_count; comp++) {
+		int is_last_component = (comp + 1 == ref->comp_count);
+		char *comp_name = ref->components[comp].name;
+
+		ofield = asn1f_lookup_child(obj, comp_name);
+		if(ofield == NULL) {
+			DEBUG("Cannot find field \"%s\" in \"%s\" at line %d",
+				ref->components[1].name,
+				obj->Identifier,
+				obj->_lineno);
+		}
+
+		/*
+		 * Compute the category of the field of
+		 * the information object class.
+		 */
+		field_cat = asn1f_class_field_category(ofield);
+
+		switch(field_cat) {
+		case OFC_INVALID:
+			WARNING("Invalid field category of \"%s\" at line %d",
+				ofield->Identifier, ofield->_lineno);
+			errno = EPERM;
+			return NULL;
+		case OFC_TYPE:
+		case OFC_FIXED_TYPE_VALUE:
+		case OFC_VARIABLE_TYPE_VALUE:
+		case OFC_FIXED_TYPE_VALUE_SET:
+		case OFC_VARIABLE_TYPE_VALUE_SET:
+			if(!is_last_component) {
+				FATAL("Field name component \"%s\" at line %d "
+					"specifies non-dereferenceable thing",
+					comp_name, ref->_lineno);
+				errno = EPERM;
+				return NULL;
+			}
+			break;
+		case OFC_INFORMATION_OBJECT:
+		case OFC_INFORMATION_OBJECT_SET:
+			obj = ofield;
+			break;
+		}
+	}
+
+	assert(ofield);
+	return ofield;
+}