better parsing information object class sets
diff --git a/libasn1parser/asn1p_class.c b/libasn1parser/asn1p_class.c
index 4603d18..666e0a2 100644
--- a/libasn1parser/asn1p_class.c
+++ b/libasn1parser/asn1p_class.c
@@ -62,6 +62,36 @@
 	}
 }
 
+int
+asn1p_ioc_row_match(const asn1p_ioc_row_t *a, const asn1p_ioc_row_t *b) {
+    assert(a && b);
+
+    if(a->columns != b->columns)
+        return -1;  /* Bad! */
+
+    for(size_t i = 0; i < a->columns; i++) {
+        assert(a->column[i].field);
+        assert(b->column[i].field);
+        if(strcmp(a->column[i].field->Identifier,
+                  b->column[i].field->Identifier)
+           != 0) {
+            return -1;  /* Bad! */
+        }
+        if((a->column[i].value && !b->column[i].value)
+           || (!a->column[i].value && b->column[i].value)) {
+            return 1;   /* Not match */
+        }
+        if(a->column[i].value && b->column[i].value) {
+            if(asn1p_expr_compare(a->column[i].value, b->column[i].value)
+               != 0) {
+                return 1;   /* Not match */
+            }
+        }
+    }
+
+    return 0;
+}
+
 struct asn1p_ioc_cell_s *
 asn1p_ioc_row_cell_fetch(asn1p_ioc_row_t *row, const char *fieldname) {
 	int i;
diff --git a/libasn1parser/asn1p_class.h b/libasn1parser/asn1p_class.h
index 893dec3..08fd374 100644
--- a/libasn1parser/asn1p_class.h
+++ b/libasn1parser/asn1p_class.h
@@ -20,6 +20,13 @@
 
 asn1p_ioc_row_t *asn1p_ioc_row_new(struct asn1p_expr_s *oclass);
 void asn1p_ioc_row_delete(asn1p_ioc_row_t *);
+
+/*
+ * Match is similar to a comparison,
+ * but -1 means error and 1 means not equal. 0 is OK
+ */
+int asn1p_ioc_row_match(const asn1p_ioc_row_t *, const asn1p_ioc_row_t *);
+
 struct asn1p_ioc_cell_s *asn1p_ioc_row_cell_fetch(asn1p_ioc_row_t *,
 		const char *fieldname);
 
diff --git a/libasn1parser/asn1p_constr.c b/libasn1parser/asn1p_constr.c
index d471684..a9e939d 100644
--- a/libasn1parser/asn1p_constr.c
+++ b/libasn1parser/asn1p_constr.c
@@ -6,6 +6,28 @@
 
 #include "asn1parser.h"
 
+void
+asn1p_constraint_set_source(asn1p_constraint_t *ct,
+                            struct asn1p_module_s *module, int lineno) {
+    if(ct) {
+        ct->module = module;
+        ct->_lineno = lineno;
+        asn1p_value_set_source(ct->containedSubtype,module,lineno);
+        asn1p_value_set_source(ct->value,module,lineno);
+        asn1p_value_set_source(ct->range_start,module,lineno);
+        asn1p_value_set_source(ct->range_stop,module,lineno);
+        for(size_t i = 0; i < ct->el_count; i++) {
+            asn1p_constraint_set_source(ct->elements[i], module, lineno);
+        }
+    }
+}
+
+int asn1p_constraint_compare(const asn1p_constraint_t *a,
+                             const asn1p_constraint_t *b) {
+    assert(!"Constraint comparison is not implemented");
+    return -1;
+}
+
 asn1p_constraint_t *
 asn1p_constraint_new(int _lineno, asn1p_module_t *mod) {
 	asn1p_constraint_t *ct;
@@ -189,3 +211,20 @@
 	}
 	return "UNKNOWN";
 }
+
+const asn1p_constraint_t *
+asn1p_get_component_relation_constraint(asn1p_constraint_t *ct) {
+    if(ct) {
+        if(ct->type == ACT_CA_CRC)
+            return ct;
+        if(ct->type == ACT_CA_SET) {
+            for(size_t i = 0; i < ct->el_count; i++) {
+                const asn1p_constraint_t *tmp =
+                    asn1p_get_component_relation_constraint(ct->elements[i]);
+                if(tmp) return tmp;
+            }
+        }
+    }
+    return NULL;
+}
+
diff --git a/libasn1parser/asn1p_constr.h b/libasn1parser/asn1p_constr.h
index 7edd63f..2351628 100644
--- a/libasn1parser/asn1p_constr.h
+++ b/libasn1parser/asn1p_constr.h
@@ -71,6 +71,14 @@
 /* Human-readable constraint type description */
 const char *asn1p_constraint_type2str(enum asn1p_constraint_type_e);
 
+const asn1p_constraint_t *asn1p_get_component_relation_constraint(
+    asn1p_constraint_t *);
+
+int asn1p_constraint_compare(const asn1p_constraint_t *,
+                             const asn1p_constraint_t *);
+void asn1p_constraint_set_source(asn1p_constraint_t *,
+                                 struct asn1p_module_s *module, int lineno);
+
 /*
  * Constructors and a recursive destructor.
  */
diff --git a/libasn1parser/asn1p_expr.c b/libasn1parser/asn1p_expr.c
index 7a24d06..4383ea1 100644
--- a/libasn1parser/asn1p_expr.c
+++ b/libasn1parser/asn1p_expr.c
@@ -9,6 +9,82 @@
 static asn1p_expr_t *asn1p_expr_clone_impl(asn1p_expr_t *expr, int skip_extensions, asn1p_expr_t *(*)(asn1p_expr_t *, void *), void *);
 static asn1p_value_t *value_resolver(asn1p_value_t *, void *arg);
 
+void
+asn1p_expr_set_source(asn1p_expr_t *expr, asn1p_module_t *module, int lineno) {
+    if(expr) {
+        expr->module = module;
+        expr->_lineno = lineno;
+        asn1p_ref_set_source(expr->reference, module, lineno);
+        asn1p_value_set_source(expr->value, module, lineno);
+        asn1p_constraint_set_source(expr->constraints, module, lineno);
+        asn1p_constraint_set_source(expr->combined_constraints, module, lineno);
+        asn1p_expr_set_source(expr->rhs_pspecs, module, lineno);
+
+        asn1p_expr_t *memb;
+
+        TQ_FOR(memb, &(expr->members), next) {
+            asn1p_expr_set_source(memb, module, lineno);
+        }
+    }
+}
+
+int
+asn1p_expr_compare(const asn1p_expr_t *a, const asn1p_expr_t *b) {
+    if(a->meta_type != b->meta_type || a->expr_type != b->expr_type) {
+        return -1;
+    }
+
+    if((!a->Identifier && b->Identifier) || (a->Identifier && !b->Identifier)) {
+        return -1;
+    } else if(a->Identifier && strcmp(a->Identifier, b->Identifier)) {
+        return -1;
+    }
+
+    if((!a->reference && b->reference) || (a->reference && !b->reference)) {
+        return -1;
+    } else if(a->reference
+              && asn1p_ref_compare(a->reference, b->reference) != 0) {
+        return -1;
+    }
+
+    if((!a->value && b->value) || (a->value && !b->value)) {
+        return -1;
+    } else if(a->value && asn1p_value_compare(a->value, b->value)) {
+        return -1;
+    }
+
+    if((a->tag.tag_class != b->tag.tag_class)
+       || (a->tag.tag_mode != b->tag.tag_mode)
+       || (a->tag.tag_value != b->tag.tag_value)) {
+        return -1;
+    }
+
+    if((a->marker.flags != b->marker.flags)
+       || (a->marker.default_value && !b->marker.default_value)
+       || (!a->marker.default_value && b->marker.default_value)
+       || (a->marker.default_value
+           && asn1p_value_compare(a->marker.default_value,
+                                  b->marker.default_value))) {
+        return -1;
+    }
+
+    if(a->unique != b->unique) {
+        return -1;
+    }
+
+    const asn1p_expr_t *am = TQ_FIRST(&a->members);
+    const asn1p_expr_t *bm = TQ_FIRST(&b->members);
+    for(; am || bm; am = TQ_NEXT(am, next), bm = TQ_NEXT(bm, next)) {
+        if((am && !bm) || (!am && bm)) {
+            return -1;
+        } else if(asn1p_expr_compare(am, bm) != 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
 /*
  * Construct a new empty types collection.
  */
diff --git a/libasn1parser/asn1p_expr.h b/libasn1parser/asn1p_expr.h
index 6f2503b..524f532 100644
--- a/libasn1parser/asn1p_expr.h
+++ b/libasn1parser/asn1p_expr.h
@@ -281,7 +281,10 @@
 	void *resolver_arg);
 void asn1p_expr_add(asn1p_expr_t *to, asn1p_expr_t *what);
 void asn1p_expr_add_many(asn1p_expr_t *to, asn1p_expr_t *from_what);
+int asn1p_expr_compare(const asn1p_expr_t *, const asn1p_expr_t *);
 void asn1p_expr_free(asn1p_expr_t *expr);
+void asn1p_expr_set_source(asn1p_expr_t *, asn1p_module_t *, int lineno);
+
 
 #define	TAG2STRING_BUFFER_SIZE	64	/* buf should be at least this big */
 char *asn1p_tag2string(struct asn1p_type_tag_s *tag, char *opt_buf);
diff --git a/libasn1parser/asn1p_oid.c b/libasn1parser/asn1p_oid.c
index 14b96aa..a71c8c4 100644
--- a/libasn1parser/asn1p_oid.c
+++ b/libasn1parser/asn1p_oid.c
@@ -81,7 +81,7 @@
 }
 
 int
-asn1p_oid_compare(asn1p_oid_t *a, asn1p_oid_t *b) {
+asn1p_oid_compare(const asn1p_oid_t *a, const asn1p_oid_t *b) {
 	int i;
 
 	for(i = 0; ; i++) {
diff --git a/libasn1parser/asn1p_oid.h b/libasn1parser/asn1p_oid.h
index 489a3c5..1b7ba52 100644
--- a/libasn1parser/asn1p_oid.h
+++ b/libasn1parser/asn1p_oid.h
@@ -61,7 +61,7 @@
  *	0: The specified OIDs are equal.
  *	-1 or 1 otherwise.
  */
-int asn1p_oid_compare(asn1p_oid_t *a, asn1p_oid_t *b);
+int asn1p_oid_compare(const asn1p_oid_t *a, const asn1p_oid_t *b);
 
 
 #endif	/* ASN1_PARSER_OID_H */
diff --git a/libasn1parser/asn1p_ref.c b/libasn1parser/asn1p_ref.c
index 3ea9b58..64f51b4 100644
--- a/libasn1parser/asn1p_ref.c
+++ b/libasn1parser/asn1p_ref.c
@@ -14,10 +14,8 @@
 	asn1p_ref_t *ref;
 
 	ref = calloc(1, sizeof *ref);
-	if(ref) {
-		ref->_lineno = _lineno;
-		ref->module = mod;
-	}
+    assert(ref);
+    asn1p_ref_set_source(ref, mod, _lineno);
 
 	return ref;
 }
@@ -39,6 +37,14 @@
 	}
 }
 
+void
+asn1p_ref_set_source(asn1p_ref_t *ref, asn1p_module_t *module, int lineno) {
+    if(ref) {
+        ref->module = module;
+        ref->_lineno = lineno;
+    }
+}
+
 static enum asn1p_ref_lex_type_e
 asn1p_ref_name2lextype(const char *name) {
 	enum asn1p_ref_lex_type_e lex_type;
@@ -136,3 +142,51 @@
 
 	return newref;
 }
+
+int
+asn1p_ref_compare(const asn1p_ref_t *a, const asn1p_ref_t *b) {
+    if(a->comp_count != b->comp_count)
+        return -1;
+    if(a->module != b->module)
+        return -1;
+
+    for(size_t i = 0; i < a->comp_count; i++) {
+        if(a->components[i].lex_type != b->components[i].lex_type
+           || strcmp(a->components[i].name, b->components[i].name) != 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+const char *
+asn1p_ref_string(const asn1p_ref_t *ref) {
+    static char static_buf[32];
+    static char *buf = static_buf;
+    static size_t buf_size = sizeof(static_buf);
+    char *p = buf;
+
+    for(size_t i = 0; i < ref->comp_count; i++) {
+        size_t space = buf_size - (p - buf);
+        int ret =
+            snprintf(p, space, "%s%s", i ? "." : "", ref->components[i].name);
+        if(ret < 0 || (size_t)ret >= space) {
+            i--;
+            char *tmp = malloc(buf_size * 2 + 1);
+            assert(tmp);
+            size_t p_offset = p - buf;
+            memcpy(tmp, buf, (p - buf));
+            if(buf != static_buf) free(buf);
+            buf_size *= 2;
+            buf = tmp;
+            p = tmp + p_offset;
+        } else {
+            p += ret;
+        }
+    }
+
+    *p = '\0';
+    return buf;
+}
+
diff --git a/libasn1parser/asn1p_ref.h b/libasn1parser/asn1p_ref.h
index c4d8b5c..776289b 100644
--- a/libasn1parser/asn1p_ref.h
+++ b/libasn1parser/asn1p_ref.h
@@ -49,6 +49,20 @@
 
 asn1p_ref_t *asn1p_ref_clone(asn1p_ref_t *ref);
 
+void asn1p_ref_set_source(asn1p_ref_t *, asn1p_module_t *module, int lineno);
+
+
+/*
+ * Lexicographically compare references.
+ */
+int asn1p_ref_compare(const asn1p_ref_t *, const asn1p_ref_t *);
+
+/*
+ * Return a pointer to a statically allocated buffer representing the
+ * complete reference.
+ */
+const char *asn1p_ref_string(const asn1p_ref_t *);
+
 /*
  * Add a new reference component to the existing reference structure.
  *
diff --git a/libasn1parser/asn1p_value.c b/libasn1parser/asn1p_value.c
index fa3c1f4..9bd7563 100644
--- a/libasn1parser/asn1p_value.c
+++ b/libasn1parser/asn1p_value.c
@@ -4,7 +4,85 @@
 #include <assert.h>
 
 #include "asn1parser.h"
+#include "asn1p_expr.h"
 
+void
+asn1p_value_set_source(asn1p_value_t *value, asn1p_module_t *module,
+                       int lineno) {
+    if(value) {
+        switch(value->type) {
+        case ATV_TYPE:
+            asn1p_expr_set_source(value->value.v_type, module, lineno);
+            break;
+        case ATV_REFERENCED:
+            asn1p_ref_set_source(value->value.reference, module, lineno);
+            break;
+        case ATV_VALUESET:
+            asn1p_constraint_set_source(value->value.constraint, module,
+                                        lineno);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+int
+asn1p_value_compare(const asn1p_value_t *a, const asn1p_value_t *b) {
+    if(a->type != b->type) {
+        return -1;
+    }
+
+    switch(a->type) {
+    case ATV_NULL:
+    case ATV_NOVALUE:
+    case ATV_MAX:
+    case ATV_MIN:
+    case ATV_FALSE:
+    case ATV_TRUE:
+        break;
+    case ATV_TYPE:
+        return asn1p_expr_compare(a->value.v_type, b->value.v_type);
+    case ATV_REAL:
+        return (a->value.v_double == b->value.v_double) ? 0 : -1;
+    case ATV_INTEGER:
+    case ATV_TUPLE:
+    case ATV_QUADRUPLE:
+        return (a->value.v_integer == b->value.v_integer) ? 0 : -1;
+    case ATV_STRING:
+    case ATV_UNPARSED:
+        if(a->value.string.size != b->value.string.size
+           || memcmp(a->value.string.buf, b->value.string.buf,
+                     a->value.string.size)
+                  != 0) {
+            return -1;
+        }
+        return 0;
+    case ATV_BITVECTOR:
+        if(a->value.binary_vector.size_in_bits
+               != b->value.binary_vector.size_in_bits
+           || memcmp(a->value.binary_vector.bits, b->value.binary_vector.bits,
+                     (a->value.binary_vector.size_in_bits+7) >> 3)
+                  != 0) {
+            return -1;
+        }
+    case ATV_VALUESET:
+        return asn1p_constraint_compare(a->value.constraint,
+                                        b->value.constraint);
+    case ATV_REFERENCED:
+        return asn1p_ref_compare(a->value.reference, b->value.reference);
+    case ATV_CHOICE_IDENTIFIER:
+        if(strcmp(a->value.choice_identifier.identifier,
+                  b->value.choice_identifier.identifier)
+           != 0) {
+            return -1;
+        }
+        return asn1p_value_compare(a->value.choice_identifier.value,
+                                   b->value.choice_identifier.value);
+    }
+
+    return 0;
+}
 
 asn1p_value_t *
 asn1p_value_fromref(asn1p_ref_t *ref, int do_copy) {
diff --git a/libasn1parser/asn1p_value.h b/libasn1parser/asn1p_value.h
index c260c27..c1167f3 100644
--- a/libasn1parser/asn1p_value.h
+++ b/libasn1parser/asn1p_value.h
@@ -5,6 +5,7 @@
 #define	ASN1_PARSER_VALUE_H
 
 struct asn1p_constraint_s;	/* Forward declaration */
+struct asn1p_module_s;
 struct asn1p_expr_s;
 
 /*
@@ -76,5 +77,7 @@
 asn1p_value_t *asn1p_value_clone_with_resolver(asn1p_value_t *,
 		asn1p_value_t *(*resolver)(asn1p_value_t *, void *rarg),
 		void *rarg);
+int asn1p_value_compare(const asn1p_value_t *, const asn1p_value_t *);
+void asn1p_value_set_source(asn1p_value_t *, struct asn1p_module_s *, int line);
 
 #endif	/* ASN1_PARSER_VALUE_H */