mirror of https://gerrit.osmocom.org/asn1c
193 lines
4.1 KiB
C
193 lines
4.1 KiB
C
#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;
|
|
}
|
|
|