Initial version of asn1c
[com/asn1c.git] / libasn1fix / asn1fix_enum.c
diff --git a/libasn1fix/asn1fix_enum.c b/libasn1fix/asn1fix_enum.c
new file mode 100644 (file)
index 0000000..ac543f2
--- /dev/null
@@ -0,0 +1,192 @@
+#include "asn1fix_internal.h"
+
+/*
+ * Check the validity of an enumeration.
+ */
+int
+asn1f_fix_enum(arg_t *arg) {
+       asn1p_expr_t *expr = arg->expr;
+       asn1p_expr_t *ev;
+       asn1c_integer_t max_value = -1;
+       asn1c_integer_t max_value_ext = -1;
+       int rvalue = 0;
+       asn1p_expr_t *ext_marker = NULL;        /* "..." position */
+       int ret;
+
+       /* Keep track of value collisions */
+       asn1c_integer_t *used_vals;
+       int used_vals_sz = 50;
+       int used_vals_next = 0;
+
+       if(expr->expr_type != ASN_BASIC_ENUMERATED)
+               return 0;       /* Just ignore it */
+
+       DEBUG("(%s)", expr->Identifier);
+
+       used_vals = (asn1c_integer_t *) malloc(sizeof(asn1c_integer_t) * used_vals_sz);
+       if (!used_vals) {
+               FATAL("Out of memory");
+               return -1;
+       }
+
+       /*
+        * 1. Scan the enumeration values in search for inconsistencies.
+        */
+       TQ_FOR(ev, &(expr->members), next) {
+               asn1c_integer_t eval;
+
+               if(ev->value)
+                       DEBUG("\tItem %s(%s)", ev->Identifier,
+                               asn1f_printable_value(ev->value));
+               else
+                       DEBUG("\tItem %s", ev->Identifier);
+
+               /*
+                * 1.1 Found an extension mark "...", check correctness.
+                */
+               if(ev->expr_type == A1TC_EXTENSIBLE) {
+                       if(ext_marker) {
+                               FATAL("Enumeration %s at line %d: "
+                               "Second extension marker is not allowed",
+                               expr->Identifier,
+                               ev->_lineno);
+                               rvalue = -1;
+                       } else {
+                               /*
+                                * Remember the marker's position.
+                                */
+                               ext_marker = ev;
+                       }
+                       continue;
+               } else if(ev->Identifier == NULL
+                       || ev->expr_type != A1TC_UNIVERVAL) {
+                       FATAL(
+                               "Enumeration %s at line %d: "
+                               "Unsupported enumeration element %s",
+                               expr->Identifier,
+                               ev->_lineno,
+                               ev->Identifier?ev->Identifier:"<anonymous>");
+                       rvalue = -1;
+                       continue;
+               }
+
+               /*
+                * 1.2 Compute the value of the enumeration element.
+                */
+               if(ev->value) {
+                       switch(ev->value->type) {
+                       case ATV_INTEGER:
+                               eval = ev->value->value.v_integer;
+                               break;
+                       case ATV_REFERENCED:
+                               FATAL("HERE HERE HERE", 1);
+                               rvalue = -1;
+                               continue;
+                               break;
+                       default:
+                               FATAL("ENUMERATED type %s at line %d "
+                                       "contain element %s(%s) at line %d",
+                                       expr->Identifier, expr->_lineno,
+                                       ev->Identifier,
+                                       asn1f_printable_value(ev->value),
+                                       ev->_lineno);
+                               rvalue = -1;
+                               continue;
+                       }
+               } else {
+                       eval = max_value + 1;
+                       ev->value = asn1p_value_fromint(eval);
+                       if(ev->value == NULL) {
+                               rvalue = -1;
+                               continue;
+                       }
+               }
+
+               /*
+                * 1.3 Check the applicability of this value.
+                */
+
+               /*
+                * Enumeration is allowed to be unordered
+                * before the first marker, but after the marker
+                * the values must be ordered.
+                */
+               if (ext_marker) {
+                       if (eval > max_value_ext) {
+                               max_value_ext = eval;
+                       } else {
+                char max_value_buf[128];
+                asn1p_itoa_s(max_value_buf, sizeof(max_value_buf),
+                             max_value_ext);
+                FATAL(
+                                       "Enumeration %s at line %d: "
+                                       "Explicit value \"%s(%s)\" "
+                                       "is not greater "
+                                       "than previous values (max %s)",
+                                       expr->Identifier,
+                                       ev->_lineno,
+                                       ev->Identifier,
+                                       asn1p_itoa(eval),
+                                       max_value_buf);
+                               rvalue = -1;
+                       }
+               }
+
+               if (eval > max_value) {
+                       max_value = eval;
+               }
+
+
+               /*
+                * 1.4 Check that all identifiers are unique
+                */
+               int unique = 1;
+               int uv_idx;
+               for (uv_idx = 0; uv_idx < used_vals_next; uv_idx++) {
+                       if (used_vals[uv_idx] == eval) {
+                               FATAL(
+                                       "Enumeration %s at line %d: "
+                                       "Explicit value \"%s(%s)\" "
+                                       "collides with previous values",
+                                       expr->Identifier,
+                                       ev->_lineno,
+                                       ev->Identifier,
+                                       asn1p_itoa(eval));
+                               rvalue = -1;
+                               unique = 0;
+                       }
+               }
+
+               if (unique) {
+                       /* Grow the array if needed */
+                       if (used_vals_next >= used_vals_sz) {
+                               asn1c_integer_t *temp;
+                               int new_sz = used_vals_sz + 50;
+                               temp = (asn1c_integer_t *) realloc(used_vals,
+                                                       sizeof(asn1c_integer_t) * new_sz);
+                               if (!temp) return -1;
+                               used_vals = temp;
+                               used_vals_sz = new_sz;
+                       }
+                       used_vals[used_vals_next++] = eval;
+               }
+
+               /*
+                * 1.5 Check that all identifiers before the current one
+                * differs from it.
+                */
+               ret = asn1f_check_unique_expr_child(arg, ev, 0, "identifier");
+               RET2RVAL(ret, rvalue);
+       }
+
+       free(used_vals);
+
+       /*
+        * 2. Reorder the first half (before optional "...") of the
+        * identifiers alphabetically.
+        */
+       // TODO
+
+       return rvalue;
+}
+