Initial version of asn1c
[com/asn1c.git] / libasn1fix / asn1fix_compat.c
diff --git a/libasn1fix/asn1fix_compat.c b/libasn1fix/asn1fix_compat.c
new file mode 100644 (file)
index 0000000..064e38e
--- /dev/null
@@ -0,0 +1,149 @@
+#include "asn1fix_internal.h"
+
+static int asn1f_check_same_children(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b);
+
+/*
+ * Check that the expressions given are compatible in their type.
+ * ORDER DOES MATTER! (See .h).
+ */
+int
+asn1f_check_type_compatibility(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b) {
+       asn1p_expr_type_e atype, btype;
+
+       atype = a->expr_type;
+       btype = b->expr_type;
+
+       DEBUG("(%s:%x@%d, %s:%x@%d)",
+               a->Identifier, atype, a->_lineno,
+               b->Identifier, btype, b->_lineno);
+
+       /*
+        * Expected terminal type!
+        */
+       assert(atype != A1TC_REFERENCE);
+       assert(btype != A1TC_REFERENCE);
+
+       if(a == b)
+               return 0;       /* Fairly obviously */
+
+       if(atype != btype) {
+               /*
+                * Limited cross-compatibility of integer types.
+                */
+               if((atype == A1TC_UNIVERVAL && btype == ASN_BASIC_INTEGER)
+               || (atype == A1TC_UNIVERVAL && btype == ASN_BASIC_ENUMERATED)
+               )
+                       return 0;
+
+               /* Limited cross-compatibility of string types */
+               if((atype & ASN_STRING_MASK)
+               && (btype & ASN_STRING_MASK)) {
+                       /* X.680, B.5 */
+                       int akm = (atype & ASN_STRING_KM_MASK)
+                               || atype == ASN_STRING_UTF8String;
+                       int bkm = (btype & ASN_STRING_KM_MASK)
+                               || btype == ASN_STRING_UTF8String;
+                       return (akm == bkm) ? 0 : -1;
+               }
+
+               DEBUG("\t%s and %s are not compatible",
+                       a->Identifier, b->Identifier);
+               return -1;      /* Fairly obviously */
+       }
+
+       switch(atype) {
+       case ASN_BASIC_INTEGER:
+               /* All integers are compatible, X.680, B.4.5 */
+               return 0;
+       case ASN_BASIC_ENUMERATED:
+               /*
+                * Enumerations are not compatible
+                * unless their definitions are the same.
+                */
+               if(asn1f_check_same_children(arg, a, b)) {
+                       DEBUG("\tEnumerations are different %s and %s",
+                               a->Identifier, b->Identifier);
+                       return -1;
+               }
+               return 0;
+       default:
+               if((atype & ASN_STRING_MASK)
+               && (btype & ASN_STRING_MASK)) {
+                       /* String type is compatible with the same type */
+                       return 0;
+               }
+               /* Compatibility is not defined yet */
+               DEBUG("\tCompatibility rule is not defined for %s and %s",
+                       a->Identifier, b->Identifier);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Check that the children are exactly same.
+ */
+static int
+asn1f_check_same_children(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b) {
+       asn1p_expr_t *achild;
+       asn1p_expr_t *bchild;
+
+       achild = TQ_FIRST(&(a->members));
+       bchild = TQ_FIRST(&(b->members));
+
+       while(1) {
+               if(achild->expr_type != bchild->expr_type)
+                       return -1;
+
+               if(achild->Identifier && bchild->Identifier) {
+                       if(strcmp(achild->Identifier, bchild->Identifier))
+                               return -1;
+               } else if(!(!achild->Identifier && !bchild->Identifier)) {
+                       return -1;
+               }
+
+               if(achild->value && bchild->value) {
+                       if(achild->value->type != bchild->value->type)
+                               return -1;
+                       switch(achild->value->type) {
+                       case ATV_INTEGER:
+                               if(achild->value->value.v_integer
+                               != bchild->value->value.v_integer)
+                                       return -1;
+                               break;
+                       case ATV_REFERENCED:
+                       default:
+                               DEBUG("Value %s at lines %d and "
+                                       "%d cannot be used in "
+                                       "semantical equality check",
+                                       asn1f_printable_value(achild->value),
+                                       achild->value->value.reference->_lineno,
+                                       bchild->value->value.reference->_lineno
+                               );
+                               return -1;
+                       }
+               } else if(!(!achild->value && !bchild->value)) {
+                       /* One of values is defined, and another is not */
+                       return -1;
+               }
+
+               achild = TQ_NEXT(achild, next);
+               bchild = TQ_NEXT(bchild, next);
+
+               if(achild && bchild)
+                       continue;
+               else if(!achild && !bchild)
+                       break;
+               else
+                       return -1;
+       }
+
+       DEBUG("\t%s:%x@%d and %s:%x@%d are semantically equivalent",
+               a->Identifier, a->expr_type, a->_lineno,
+               b->Identifier, b->expr_type, b->_lineno);
+
+       return 0;
+}
+
+