Initial revision

diff --git a/libasn1fix/asn1fix.c b/libasn1fix/asn1fix.c
new file mode 100644
index 0000000..af11099
--- /dev/null
+++ b/libasn1fix/asn1fix.c
@@ -0,0 +1,354 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "asn1fix.h"
+#include "asn1fix_internal.h"
+
+/* Print everything to stderr */
+static void _default_error_logger(int _severity, const char *fmt, ...);
+
+/*
+ * Internal check functions.
+ */
+static int asn1f_fix_module(arg_t *arg);
+static int asn1f_fix_simple(arg_t *arg);	/* For INTEGER/ENUMERATED */
+static int asn1f_fix_constructed(arg_t *arg);	/* For SEQUENCE/SET/CHOICE */
+static int asn1f_fix_constraints(arg_t *arg);	/* For subtype constraints */
+
+
+/*
+ * Scan every module defined here in search for inconsistences.
+ */
+int
+asn1f_process(asn1p_t *asn, enum asn1f_flags flags,
+		error_logger_f error_logger) {
+	arg_t arg;
+	int fatals = 0;
+	int warnings = 0;
+
+	/*
+	 * Check validity of arguments.
+	 */
+	if(asn == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/*
+	 * If errors handler is not specified, default to internal one.
+	 */
+	if(error_logger == 0) {
+		error_logger = _default_error_logger;
+	}
+
+	memset(&arg, 0, sizeof(arg));
+	arg.asn = asn;
+	arg.eh = error_logger;
+
+	if(flags & A1F_DEBUG) {
+		arg.debug = arg.eh;
+		arg.debug(-1, "Called %s() with flags %d", __func__, flags);
+		flags &= ~A1F_DEBUG;
+	}
+
+	/*
+	 * Check that we haven't missed an unknown flag.
+	 */
+	if(flags) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/*
+	 * Process each module in the list.
+	 */
+	TQ_FOR(arg.mod, &(asn->modules), mod_next) {
+		int ret = asn1f_fix_module(&arg);
+		/*
+		 * These lines are used for illustration purposes.
+		 * RET2RVAL() is used everywhere else.
+		 */
+		if(ret == -1) fatals++;
+		if(ret == 1) warnings++;
+	}
+
+	/*
+	 * Compute a return value.
+	 */
+	return fatals?-1:warnings?1:0;
+}
+
+/*
+ * Check the internals of a single module.
+ */
+static int
+asn1f_fix_module(arg_t *arg) {
+	asn1p_expr_t *expr;
+	int rvalue = 0;
+
+	switch((arg->mod->module_flags
+	& (MSF_EXPLICIT_TAGS | MSF_IMPLICIT_TAGS | MSF_AUTOMATIC_TAGS))) {
+	case MSF_NOFLAGS:
+	case MSF_EXPLICIT_TAGS:
+	case MSF_IMPLICIT_TAGS:
+	case MSF_AUTOMATIC_TAGS:
+		break;
+	default:
+		FATAL("Module %s defined with ambiguous global tagging mode",
+			arg->mod->Identifier);
+		RET2RVAL(-1, rvalue);
+	}
+
+	/*
+	 * Do various non-recursive transformations.
+	 * Order is not important.
+	 */
+	TQ_FOR(expr, &(arg->mod->members), next) {
+		int ret;
+		arg->expr = expr;
+
+		if(expr->meta_type == AMT_PARAMTYPE)
+			/* Do not process the parametrized type just yet */
+			continue;
+
+		DEBUG("=== Now processing \"%s\" at line %d ===",
+			expr->Identifier, expr->_lineno);
+		assert(expr->meta_type != AMT_INVALID);
+
+		/*
+		 * 2.1 Pre-process simple types (ENUMERATED, INTEGER, etc).
+		 */
+		ret = asn1f_recurse_expr(arg, asn1f_fix_simple);
+		RET2RVAL(ret, rvalue);
+
+		/*
+		 * 2.[234] Process SEQUENCE/SET/CHOICE types.
+		 */
+		ret = asn1f_recurse_expr(arg, asn1f_fix_constructed);
+		RET2RVAL(ret, rvalue);
+
+		/*
+		 * 2.5.4
+		 */
+		ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_types);
+		RET2RVAL(ret, rvalue);
+
+		/*
+		 * 2.5.5
+		 */
+		ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_values);
+		RET2RVAL(ret, rvalue);
+
+		/*
+		 * Resolve references in constraints.
+		 */
+		ret = asn1f_recurse_expr(arg, asn1f_fix_constraints);
+		RET2RVAL(ret, rvalue);
+
+		/*
+		 * 6. INTEGER value processed at 2.5.4.
+		 */
+
+		/*
+		 * Make sure everybody's behaving well.
+		 */
+		assert(arg->expr == expr);
+	}
+
+	/*
+	 * 5. Automatic tagging
+	 */
+	TQ_FOR(expr, &(arg->mod->members), next) {
+		int ret;
+
+		arg->expr = expr;
+
+		ret = asn1f_recurse_expr(arg, asn1f_fix_constr_autotag);
+		RET2RVAL(ret, rvalue);
+
+		assert(arg->expr == expr);
+	}
+
+	/*
+	 * 8. fix BIT STRING
+	 * 9. fix spaces in cstrings
+	 */
+	TQ_FOR(expr, &(arg->mod->members), next) {
+		int ret;
+		arg->expr = expr;
+
+		ret = asn1f_recurse_expr(arg, asn1f_fix_bit_string);
+		RET2RVAL(ret, rvalue);
+
+		ret = asn1f_recurse_expr(arg, asn1f_fix_cstring);
+		RET2RVAL(ret, rvalue);
+
+		assert(arg->expr == expr);
+	}
+
+	/*
+	 * ... Check for tags distinctness.
+	 */
+	TQ_FOR(expr, &(arg->mod->members), next) {
+		int ret;
+		arg->expr = expr;
+
+		ret = asn1f_recurse_expr(arg, asn1f_check_constr_tags_distinct);
+		RET2RVAL(ret, rvalue);
+
+		assert(arg->expr == expr);
+	}
+
+	return rvalue;
+}
+
+
+static int
+asn1f_fix_simple(arg_t *arg) {
+	int rvalue = 0;
+	int ret;
+
+	ret = asn1f_fix_enum(arg);
+	RET2RVAL(ret, rvalue);
+
+	ret = asn1f_fix_integer(arg);
+	RET2RVAL(ret, rvalue);
+
+	return rvalue;
+}
+
+static int
+asn1f_fix_constructed(arg_t *arg) {
+	int rvalue = 0;
+	int ret;
+
+	switch(arg->expr->expr_type) {
+	case ASN_CONSTR_SEQUENCE:
+	case ASN_CONSTR_SET:
+	case ASN_CONSTR_CHOICE:
+		break;
+	default:
+		return 0;
+	}
+
+	/* Check identifier distinctness */
+	ret = asn1f_check_unique_expr(arg, NULL);
+	RET2RVAL(ret, rvalue);
+
+	/* Fix extensibility */
+	ret = asn1f_fix_constr_ext(arg);
+	RET2RVAL(ret, rvalue);
+
+	/* Fix tagging */
+	ret = asn1f_fix_constr_tag(arg);
+	RET2RVAL(ret, rvalue);
+
+	return rvalue;
+}
+
+static int
+_constraint_value_resolve(arg_t *arg, asn1p_value_t **value) {
+	asn1p_expr_t expr;
+	asn1p_expr_t *tmp_expr;
+	asn1p_module_t *tmp_mod;
+	asn1p_module_t *mod_r = NULL;
+	int rvalue = 0;
+	int ret;
+
+	tmp_expr = asn1f_lookup_symbol(arg, (*value)->value.reference, &mod_r);
+	if(tmp_expr == NULL) {
+		FATAL("Cannot find symbol %s "
+			"used in %s subtype constraint at line %d",
+			asn1f_printable_reference((*value)->value.reference),
+			arg->expr->Identifier, arg->expr->_lineno);
+		assert((*value)->type == ATV_REFERENCED);
+		return -1;
+	}
+
+	memset(&expr, 0, sizeof(expr));
+	expr.meta_type = tmp_expr->meta_type;
+	expr.expr_type = tmp_expr->expr_type;
+	expr.Identifier = tmp_expr->Identifier;
+	expr.value = *value;
+	tmp_expr = arg->expr;
+	tmp_mod = arg->mod;
+	arg->expr = &expr;
+	arg->mod = mod_r;
+	ret = asn1f_fix_dereference_values(arg);
+	RET2RVAL(ret, rvalue);
+	arg->expr = tmp_expr;
+	arg->mod = tmp_mod;
+	assert(expr.value);
+	*value = expr.value;
+
+	return rvalue;
+}
+
+static int
+_resolve_constraints(arg_t *arg, asn1p_constraint_t *ct) {
+	int rvalue = 0;
+	int ret;
+	int el;
+
+	/* Don't touch information object classes */
+	if(ct->type == ACT_CT_WCOMP
+	|| ct->type == ACT_CT_WCOMPS
+	|| ct->type == ACT_CA_CRC)
+		return 0;
+
+	if(ct->value && ct->value->type == ATV_REFERENCED) {
+		ret = _constraint_value_resolve(arg, &ct->value);
+		RET2RVAL(ret, rvalue);
+	}
+	if(ct->range_start && ct->range_start->type == ATV_REFERENCED) {
+		ret = _constraint_value_resolve(arg, &ct->range_start);
+		RET2RVAL(ret, rvalue);
+	}
+	if(ct->range_stop && ct->range_stop->type == ATV_REFERENCED) {
+		ret = _constraint_value_resolve(arg, &ct->range_stop);
+		RET2RVAL(ret, rvalue);
+	}
+
+	for(el = 0; el < ct->el_count; el++) {
+		ret = _resolve_constraints(arg, ct->elements[el]);
+		RET2RVAL(ret, rvalue);
+	}
+
+	return rvalue;
+}
+
+static int
+asn1f_fix_constraints(arg_t *arg) {
+	int rvalue = 0;
+	int ret;
+
+	if(arg->expr->constraints) {
+		ret = _resolve_constraints(arg, arg->expr->constraints);
+		RET2RVAL(ret, rvalue);
+	}
+
+	return rvalue;
+}
+
+/*
+ * Print everything to stderr
+ */
+static void
+_default_error_logger(int _severity, const char *fmt, ...) {
+	va_list ap;
+	char *pfx = "";
+
+	switch(_severity) {
+	case -1: pfx = "DEBUG: "; break;
+	case 0: pfx = "WARNING: "; break;
+	case 1: pfx = "FATAL: "; break;
+	}
+	
+	fprintf(stderr, "%s", pfx);
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}