Initial revision

diff --git a/libasn1compiler/Makefile.am b/libasn1compiler/Makefile.am
new file mode 100644
index 0000000..6b2babd
--- /dev/null
+++ b/libasn1compiler/Makefile.am
@@ -0,0 +1,28 @@
+
+AM_CPPFLAGS =	\
+	-I${top_srcdir}/libasn1parser	\
+	-I${top_srcdir}/libasn1fix
+
+noinst_LTLIBRARIES = libasn1compiler.la
+
+libasn1compiler_la_LDFLAGS = -all-static
+libasn1compiler_la_SOURCES =			\
+	asn1compiler.c asn1compiler.h		\
+	asn1c_misc.c asn1c_misc.h		\
+	asn1c_out.c asn1c_out.h			\
+	asn1c_lang.c asn1c_lang.h		\
+	asn1c_save.c asn1c_save.h		\
+	asn1c_C.c asn1c_C.h			\
+	asn1c_internal.h
+
+libasn1compiler_la_LIBADD =				\
+	${top_builddir}/libasn1parser/libasn1parser.la	\
+	${top_builddir}/libasn1fix/libasn1fix.la
+
+check_PROGRAMS = check_compiler
+
+TESTS = ${check_PROGRAMS}
+
+LDADD = ${noinst_LTLIBRARIES} ${libasn1compiler_la_LIBADD}
+DEPENDENCIES = ${LDADD} 
+
diff --git a/libasn1compiler/Makefile.in b/libasn1compiler/Makefile.in
new file mode 100644
index 0000000..3869df3
--- /dev/null
+++ b/libasn1compiler/Makefile.in
@@ -0,0 +1,424 @@
+# Makefile.in generated automatically by automake 1.5 from Makefile.am.
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_alias = @build_alias@
+build_triplet = @build@
+host_alias = @host_alias@
+host_triplet = @host@
+target_alias = @target_alias@
+target_triplet = @target@
+ADD_CFLAGS = @ADD_CFLAGS@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AWK = @AWK@
+CC = @CC@
+CONFIGURE_DEPENDS = @CONFIGURE_DEPENDS@
+CPP = @CPP@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+EXEEXT = @EXEEXT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LEX = @LEX@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+MAINT = @MAINT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PATH = @PATH@
+RANLIB = @RANLIB@
+VERSION = @VERSION@
+YACC = @YACC@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
+
+AM_CPPFLAGS = \
+	-I${top_srcdir}/libasn1parser	\
+	-I${top_srcdir}/libasn1fix
+
+
+noinst_LTLIBRARIES = libasn1compiler.la
+
+libasn1compiler_la_LDFLAGS = -all-static
+libasn1compiler_la_SOURCES = \
+	asn1compiler.c asn1compiler.h		\
+	asn1c_misc.c asn1c_misc.h		\
+	asn1c_out.c asn1c_out.h			\
+	asn1c_lang.c asn1c_lang.h		\
+	asn1c_save.c asn1c_save.h		\
+	asn1c_C.c asn1c_C.h			\
+	asn1c_internal.h
+
+
+libasn1compiler_la_LIBADD = \
+	${top_builddir}/libasn1parser/libasn1parser.la	\
+	${top_builddir}/libasn1fix/libasn1fix.la
+
+
+check_PROGRAMS = check_compiler
+
+TESTS = ${check_PROGRAMS}
+
+LDADD = ${noinst_LTLIBRARIES} ${libasn1compiler_la_LIBADD}
+DEPENDENCIES = ${LDADD} 
+subdir = libasn1compiler
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+
+libasn1compiler_la_DEPENDENCIES = \
+	${top_builddir}/libasn1parser/libasn1parser.la \
+	${top_builddir}/libasn1fix/libasn1fix.la
+am_libasn1compiler_la_OBJECTS = asn1compiler.lo asn1c_misc.lo \
+	asn1c_out.lo asn1c_lang.lo asn1c_save.lo asn1c_C.lo
+libasn1compiler_la_OBJECTS = $(am_libasn1compiler_la_OBJECTS)
+check_PROGRAMS = check_compiler$(EXEEXT)
+check_compiler_SOURCES = check_compiler.c
+check_compiler_OBJECTS = check_compiler.$(OBJEXT)
+check_compiler_LDADD = $(LDADD)
+check_compiler_DEPENDENCIES = libasn1compiler.la \
+	${top_builddir}/libasn1parser/libasn1parser.la \
+	${top_builddir}/libasn1fix/libasn1fix.la
+check_compiler_LDFLAGS =
+
+DEFS = @DEFS@
+DEFAULT_INCLUDES =  -I. -I$(srcdir) -I$(top_builddir)
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/asn1c_C.Plo $(DEPDIR)/asn1c_lang.Plo \
+@AMDEP_TRUE@	$(DEPDIR)/asn1c_misc.Plo $(DEPDIR)/asn1c_out.Plo \
+@AMDEP_TRUE@	$(DEPDIR)/asn1c_save.Plo $(DEPDIR)/asn1compiler.Plo \
+@AMDEP_TRUE@	$(DEPDIR)/check_compiler.Po
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \
+	$(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+CFLAGS = @CFLAGS@
+DIST_SOURCES = $(libasn1compiler_la_SOURCES) check_compiler.c
+DIST_COMMON = Makefile.am Makefile.in
+SOURCES = $(libasn1compiler_la_SOURCES) check_compiler.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am  $(top_srcdir)/configure.in $(ACLOCAL_M4)
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu  libasn1compiler/Makefile
+Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) && \
+	  CONFIG_HEADERS= CONFIG_LINKS= \
+	  CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status
+
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+libasn1compiler.la: $(libasn1compiler_la_OBJECTS) $(libasn1compiler_la_DEPENDENCIES) 
+	$(LINK)  $(libasn1compiler_la_LDFLAGS) $(libasn1compiler_la_OBJECTS) $(libasn1compiler_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+	-test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS)
+check_compiler$(EXEEXT): $(check_compiler_OBJECTS) $(check_compiler_DEPENDENCIES) 
+	@rm -f check_compiler$(EXEEXT)
+	$(LINK) $(check_compiler_LDFLAGS) $(check_compiler_OBJECTS) $(check_compiler_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT) core *.core
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asn1c_C.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asn1c_lang.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asn1c_misc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asn1c_out.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asn1c_save.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asn1compiler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/check_compiler.Po@am__quote@
+
+distclean-depend:
+	-rm -rf $(DEPDIR)
+
+.c.o:
+@AMDEP_TRUE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@	depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@	$(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+	$(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$<
+
+.c.obj:
+@AMDEP_TRUE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@	depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@	$(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+	$(COMPILE) -c `cygpath -w $<`
+
+.c.lo:
+@AMDEP_TRUE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@	depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@	$(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+	$(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$<
+CCDEPMODE = @CCDEPMODE@
+uninstall-info-am:
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique $(LISP)
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+	  || etags $(ETAGS_ARGS) $$tags  $$unique $(LISP)
+
+GTAGS:
+	here=`CDPATH=: && cd $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
+
+check-TESTS: $(TESTS)
+	@failed=0; all=0; xfail=0; xpass=0; \
+	srcdir=$(srcdir); export srcdir; \
+	list='$(TESTS)'; \
+	if test -n "$$list"; then \
+	  for tst in $$list; do \
+	    if test -f ./$$tst; then dir=./; \
+	    elif test -f $$tst; then dir=; \
+	    else dir="$(srcdir)/"; fi; \
+	    if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *" $$tst "*) \
+	        xpass=`expr $$xpass + 1`; \
+	        failed=`expr $$failed + 1`; \
+	        echo "XPASS: $$tst"; \
+	      ;; \
+	      *) \
+	        echo "PASS: $$tst"; \
+	      ;; \
+	      esac; \
+	    elif test $$? -ne 77; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *" $$tst "*) \
+	        xfail=`expr $$xfail + 1`; \
+	        echo "XFAIL: $$tst"; \
+	      ;; \
+	      *) \
+	        failed=`expr $$failed + 1`; \
+	        echo "FAIL: $$tst"; \
+	      ;; \
+	      esac; \
+	    fi; \
+	  done; \
+	  if test "$$failed" -eq 0; then \
+	    if test "$$xfail" -eq 0; then \
+	      banner="All $$all tests passed"; \
+	    else \
+	      banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+	    fi; \
+	  else \
+	    if test "$$xpass" -eq 0; then \
+	      banner="$$failed of $$all tests failed"; \
+	    else \
+	      banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+	    fi; \
+	  fi; \
+	  dashes=`echo "$$banner" | sed s/./=/g`; \
+	  echo "$$dashes"; \
+	  echo "$$banner"; \
+	  echo "$$dashes"; \
+	  test "$$failed" -eq 0; \
+	fi
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+	@for file in $(DISTFILES); do \
+	  if test -f $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    $(mkinstalldirs) "$(distdir)/$$dir"; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    cp -pR $$d/$$file $(distdir) \
+	    || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+
+installdirs:
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+	clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-compile distclean-depend \
+	distclean-generic distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+uninstall-am: uninstall-info-am
+
+.PHONY: GTAGS all all-am check check-TESTS check-am clean \
+	clean-checkPROGRAMS clean-generic clean-libtool \
+	clean-noinstLTLIBRARIES distclean distclean-compile \
+	distclean-depend distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am info info-am install \
+	install-am install-data install-data-am install-exec \
+	install-exec-am install-info install-info-am install-man \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	tags uninstall uninstall-am uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
new file mode 100644
index 0000000..8ec476c
--- /dev/null
+++ b/libasn1compiler/asn1c_C.c
@@ -0,0 +1,2021 @@
+/*
+ * Don't look into this file. First, because it's a mess, and second, because
+ * it's a brain of the compiler, and you don't wanna mess with brains do you? ;)
+ */
+#include "asn1c_internal.h"
+#include "asn1c_C.h"
+#include <asn1fix_export.h>	/* exportable stuff from libasn1fix */
+
+static int asn1c_lang_C_type_SEQUENCE_def(arg_t *arg);
+static int asn1c_lang_C_type_SET_def(arg_t *arg);
+static int asn1c_lang_C_type_CHOICE_def(arg_t *arg);
+static int asn1c_lang_C_type_SEx_OF_def(arg_t *arg, int seq_of);
+static int _print_tag(arg_t *arg, asn1p_expr_t *expr, struct asn1p_type_tag_s *tag_p);
+static int emit_constraint_checking_code(arg_t *arg);
+static int emit_single_constraint_check(arg_t *arg, asn1p_constraint_t *ct, int mode);
+static int emit_alphabet_tables(arg_t *arg, asn1p_constraint_t *ct, int *table);
+static int emit_alphabet_check_cycle(arg_t *arg);
+static int check_constraint_type_presence(asn1p_constraint_t *ct, enum asn1p_constraint_type_e type);
+static asn1p_expr_type_e _find_terminal_type(arg_t *arg);
+static int emit_value_determination_code(arg_t *arg);
+static int emit_size_determination_code(arg_t *arg);
+static long compute_min_size(arg_t *arg);
+static long compute_max_size(arg_t *arg);
+static long compute_xxx_size(arg_t *arg, int _max);
+
+typedef struct tag2el_s {
+	struct asn1p_type_tag_s el_tag;
+	int el_no;
+	asn1p_expr_t *from_expr;
+} tag2el_t;
+
+static int _fill_tag2el_map(arg_t *arg, tag2el_t **tag2el, int *count, int el_no);
+static int _add_tag2el_member(arg_t *arg, tag2el_t **tag2el, int *count, int el_no);
+
+#define	C99_MODE	(arg->flags & A1C_NO_C99)
+#define	UNNAMED_UNIONS	(arg->flags & A1C_UNNAMED_UNIONS)
+
+#define	PCTX_DEF INDENTED(		\
+	OUT("\n");			\
+	OUT("/* Context for parsing across buffer boundaries */\n");	\
+	OUT("ber_dec_ctx_t _ber_dec_ctx;\n"));
+
+#define	DEPENDENCIES	do {			\
+	int saved_target = arg->target->target;	\
+	int saved_indent = arg->indent_level;	\
+	int comment_printed = 0;		\
+	REDIR(OT_DEPS);				\
+	arg->indent_level = 0;			\
+	TQ_FOR(v, &(expr->members), next) {	\
+		if((!(v->expr_type & ASN_CONSTR_MASK)	\
+		&& v->expr_type > ASN_CONSTR_MASK)	\
+		|| v->meta_type == AMT_TYPEREF) {	\
+			if(!comment_printed++)		\
+				OUT("/* Dependencies for %s */\n",	\
+				asn1c_type_name(arg, expr, TNF_UNMODIFIED));	\
+			OUT("#include <%s.h>\n",			\
+				asn1c_type_name(arg, v, TNF_INCLUDE));	\
+		}					\
+	}						\
+	if(expr->expr_type == ASN_CONSTR_SET_OF)	\
+		OUT("#include <asn_SET_OF.h>\n");	\
+	if(expr->expr_type == ASN_CONSTR_SEQUENCE_OF)	\
+		OUT("#include <asn_SEQUENCE_OF.h>\n");	\
+	OUT("\n");				\
+	REDIR(saved_target);			\
+	INDENT(saved_indent);			\
+	} while(0)
+
+#define	MKID(id)	asn1c_make_identifier(0, (id), 0)
+
+int
+asn1c_lang_C_type_ENUMERATED(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+
+	REDIR(OT_DEPS);
+
+	OUT("typedef enum %s {\n", MKID(expr->Identifier));
+	TQ_FOR(v, &(expr->members), next) {
+		switch(v->expr_type) {
+		case A1TC_UNIVERVAL:
+			OUT("\t%s\t= %lld,\n",
+				asn1c_make_identifier(0,
+					expr->Identifier,
+					v->Identifier, 0),
+				v->value->value.v_integer);
+			break;
+		case A1TC_EXTENSIBLE:
+			OUT("\t/*\n");
+			OUT("\t * Enumeration is extensible\n");
+			OUT("\t */\n");
+			break;
+		default:
+			return -1;
+		}
+	}
+	OUT("} %s_e;\n", MKID(expr->Identifier));
+
+	return asn1c_lang_C_type_SIMPLE_TYPE(arg);
+}
+
+
+int
+asn1c_lang_C_type_INTEGER(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+
+	REDIR(OT_DEPS);
+
+	if(TQ_FIRST(&(expr->members))) {
+		OUT("typedef enum %s {\n", MKID(expr->Identifier));
+		TQ_FOR(v, &(expr->members), next) {
+			switch(v->expr_type) {
+			case A1TC_UNIVERVAL:
+				OUT("\t%s\t= %lld,\n",
+					asn1c_make_identifier(0,
+						expr->Identifier,
+						v->Identifier, 0),
+					v->value->value.v_integer);
+				break;
+			default:
+				return -1;
+			}
+		}
+		OUT("} %s_e;\n", MKID(expr->Identifier));
+	}
+
+	return asn1c_lang_C_type_SIMPLE_TYPE(arg);
+}
+
+int
+asn1c_lang_C_type_SEQUENCE(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	int comp_mode = 0;	/* {root,ext=1,root,root,...} */
+
+	DEPENDENCIES;
+
+	if(arg->embed) {
+		OUT("struct %s {\n",
+			MKID(expr->Identifier));
+	} else {
+		OUT("typedef struct %s {\n",
+			MKID(expr->Identifier));
+	}
+
+	TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type == A1TC_EXTENSIBLE) {
+			if(comp_mode < 3) comp_mode++;
+		}
+		if(comp_mode == 1 && !v->marker)
+			v->marker = EM_OPTIONAL;
+		EMBED(v);
+	}
+
+	PCTX_DEF;
+	OUT("} %s%s", expr->marker?"*":"",
+		MKID(expr->Identifier));
+	if(arg->embed) OUT(";\n"); else OUT("_t;\n");
+
+	return asn1c_lang_C_type_SEQUENCE_def(arg);
+}
+
+static int
+asn1c_lang_C_type_SEQUENCE_def(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	int elements;	/* Number of elements */
+	int tags_impl_skip = 0;
+	int comp_mode = 0;	/* {root,ext=1,root,root,...} */
+	int ext_start = -1;
+	int ext_stop = -1;
+	char *p;
+
+	REDIR(OT_STAT_DEFS);
+
+	OUT("#include <constr_SEQUENCE.h>\n");
+	OUT("\n");
+
+	/*
+	 * Print out the table according to which the parsing is performed.
+	 */
+	p = MKID(expr->Identifier);
+	OUT("static asn1_SEQUENCE_element_t asn1_DEF_%s_elements[] = {\n", p);
+
+	elements = 0;
+	INDENTED(TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type == A1TC_EXTENSIBLE) {
+			if((++comp_mode) == 1)
+				ext_start = elements - 1;
+			else
+				ext_stop = elements - 1;
+			continue;
+		}
+	OUT("{ ");
+		elements++;
+		OUT("offsetof(struct %s, ", MKID(expr->Identifier));
+		OUT("%s), ", MKID(v->Identifier));
+		if(v->marker) {
+			asn1p_expr_t *tv;
+			int opts = 0;
+			for(tv = v; tv && tv->marker;
+				tv = TQ_NEXT(tv, next), opts++) {
+				if(tv->expr_type == A1TC_EXTENSIBLE)
+					opts--;
+			}
+			OUT("%d,", opts);
+		} else {
+			OUT("0,");
+		}
+		OUT("\n");
+		INDENT(+1);
+		if(C99_MODE) OUT(".tag = ");
+		_print_tag(arg, v, NULL);
+		OUT(",\n");
+		if(C99_MODE) OUT(".tag_mode = ");
+		if(v->tag.tag_class) {
+			if(v->tag.tag_mode == TM_IMPLICIT)
+			OUT("-1,\t/* IMPLICIT tag at current level */\n");
+			else
+			OUT("+1,\t/* EXPLICIT tag at current level */\n");
+		} else {
+			OUT("0,\n");
+		}
+		if(C99_MODE) OUT(".type = ");
+		OUT("(void *)&asn1_DEF_%s,\n",
+			asn1c_type_name(arg, v, TNF_SAFE));
+		if(C99_MODE) OUT(".name = ");
+		OUT("\"%s\"\n", v->Identifier);
+		OUT("},\n");
+		INDENT(-1);
+	});
+	OUT("};\n");
+
+	p = MKID(expr->Identifier);
+	OUT("static ber_tlv_tag_t asn1_DEF_%s_tags[] = {\n", p);
+	INDENTED(
+		if(expr->tag.tag_class) {
+			_print_tag(arg, expr, &expr->tag);
+			if(expr->tag.tag_mode != TM_EXPLICIT)
+				tags_impl_skip++;
+		}
+		if(!expr->tag.tag_class
+		|| (expr->meta_type == AMT_TYPE
+			&& expr->tag.tag_mode == TM_EXPLICIT)) {
+			struct asn1p_type_tag_s tag;
+			if(expr->tag.tag_class)
+				OUT(",\n");
+			tag.tag_class = TC_UNIVERSAL;
+			tag.tag_mode = TM_IMPLICIT;
+			tag.tag_value = expr_type2uclass_value[expr->expr_type];
+			_print_tag(arg, expr, &tag);
+		}
+		OUT("\n");
+	);
+	OUT("};\n");
+
+	OUT("static asn1_SEQUENCE_specifics_t asn1_DEF_%s_specs = {\n", p);
+	INDENTED(
+		OUT("sizeof(struct %s),\n", p);
+		OUT("offsetof(struct %s, _ber_dec_ctx),\n", p);
+		OUT("asn1_DEF_%s_elements,\n", p);
+		OUT("%d,\t/* Elements count */\n", elements);
+		OUT("%d,\t/* Start extensions */\n",
+			ext_start);
+		OUT("%d\t/* Stop extensions */\n",
+			(ext_stop<ext_start)?elements+1:ext_stop, ext_stop);
+	);
+	OUT("};\n");
+	OUT("asn1_TYPE_descriptor_t asn1_DEF_%s = {\n", p);
+	INDENTED(
+		OUT("\"%s\",\n", expr->Identifier);
+		OUT("SEQUENCE_constraint,\n");
+		OUT("SEQUENCE_decode_ber,\n");
+		OUT("SEQUENCE_encode_der,\n");
+		OUT("SEQUENCE_print,\n");
+		OUT("SEQUENCE_free,\n");
+		OUT("0,\t/* Use generic outmost tag fetcher */\n");
+		OUT("asn1_DEF_%s_tags,\n", p);
+		OUT("sizeof(asn1_DEF_%s_tags)\n", p);
+		OUT("\t/sizeof(asn1_DEF_%s_tags[0]),\n", p);
+		OUT("%d,\t/* Tags to skip */\n", tags_impl_skip);
+		OUT("%d,\t/* Whether CONSTRUCTED */\n", 1);
+		OUT("&asn1_DEF_%s_specs\t/* Additional specs */\n", p);
+	);
+	OUT("};\n");
+	OUT("\n");
+
+	REDIR(OT_DEPS);
+	OUT("#include <constr_SEQUENCE.h>\n");
+	OUT("\n");
+	if(!arg->embed)
+	OUT("extern asn1_TYPE_descriptor_t asn1_DEF_%s;\n", p);
+	REDIR(OT_TYPE_DECLS);
+
+	return 0;
+}
+
+int
+asn1c_lang_C_type_SEQUENCE_OF(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+
+	DEPENDENCIES;
+
+	if(arg->embed) {
+		OUT("struct %s {\n", MKID(expr->Identifier));
+	} else {
+		OUT("typedef struct %s {\n", MKID(expr->Identifier));
+	}
+
+	TQ_FOR(v, &(expr->members), next) {
+		INDENTED(OUT("A_SEQUENCE_OF(%s) list;\n",
+			asn1c_type_name(arg, v, TNF_RSAFE)));
+	}
+
+	PCTX_DEF;
+	OUT("} %s%s", expr->marker?"*":"", MKID(expr->Identifier));
+	if(arg->embed) OUT(";\n"); else OUT("_t;\n");
+
+	/*
+	 * SET OF/SEQUENCE OF definition, SEQUENCE OF mode.
+	 */
+	return asn1c_lang_C_type_SEx_OF_def(arg, 1);
+}
+
+int
+asn1c_lang_C_type_SET(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	long mcount;
+	char *id;
+	int comp_mode = 0;	/* {root,ext=1,root,root,...} */
+
+	DEPENDENCIES;
+
+	REDIR(OT_DEPS);
+
+	OUT("\n");
+	OUT("/*\n");
+	OUT(" * Method of determining the components presence\n");
+	OUT(" */\n");
+	mcount = 0;
+	OUT("enum %s_PR_e {\n", MKID(expr->Identifier));
+	TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type == A1TC_EXTENSIBLE) continue;
+		INDENTED(
+			id = MKID(expr->Identifier);
+			OUT("%s_PR_", id);
+			id = MKID(v->Identifier);
+			OUT("%s,\t/* Member %s is present */\n",
+				id, id)
+		);
+		mcount++;
+	}
+	OUT("};\n");
+
+	REDIR(OT_TYPE_DECLS);
+
+	if(arg->embed) {
+		OUT("struct %s {\n", MKID(expr->Identifier));
+	} else {
+		OUT("typedef struct %s {\n", MKID(expr->Identifier));
+	}
+
+	TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type == A1TC_EXTENSIBLE) {
+			if(comp_mode < 3) comp_mode++;
+		}
+		if(comp_mode == 1 && !v->marker)
+			v->marker = EM_OPTIONAL;
+		EMBED(v);
+	}
+
+	INDENTED(
+		id = MKID(expr->Identifier);
+		OUT("\n");
+		OUT("/* Presence bitmask: ASN_SET_ISPRESENT(p%s, %s_PR_x) */\n",
+			id, id);
+		OUT("unsigned int _presence_map\n");
+		OUT("\t[((%ld+(8*sizeof(unsigned int))-1)/(8*sizeof(unsigned int)))];\n", mcount);
+	);
+
+	PCTX_DEF;
+	OUT("} %s%s", expr->marker?"*":"", MKID(expr->Identifier));
+	if(arg->embed) OUT(";\n"); else OUT("_t;\n");
+
+	return asn1c_lang_C_type_SET_def(arg);
+}
+
+/*
+ * Compare tags according to their canonical order.
+ * Canonical order: [UNIVERSAL] [APPLICATION] [] [PRIVATE]
+ * As you see, the class is encoded using the two lowest bits.
+ */
+static arg_t *_ctc_arg;
+static int _canonical_tags_cmp(const void *ap, const void *bp)
+	__attribute__ ((unused));
+static int
+_canonical_tags_cmp(const void *ap, const void *bp) {
+	asn1p_expr_t *a, *b;
+	struct asn1p_type_tag_s ta, tb;
+
+	(const asn1p_expr_t *)a = *(const asn1p_expr_t * const *)ap;
+	(const asn1p_expr_t *)b = *(const asn1p_expr_t * const *)bp;
+
+	if(asn1f_fetch_tag(_ctc_arg->asn, _ctc_arg->mod, a, &ta)
+	|| asn1f_fetch_tag(_ctc_arg->asn, _ctc_arg->mod, b, &tb))
+		return 0;
+
+	if(ta.tag_class == tb.tag_class) {
+		if(ta.tag_value == tb.tag_value)
+			return 0;
+		else if(ta.tag_value < tb.tag_value)
+			return -1;
+		else
+			return 1;
+	} else if(ta.tag_class < tb.tag_class) {
+		return -1;
+	} else {
+		return 1;
+	}
+}
+
+static int
+_tag2el_cmp(const void *ap, const void *bp) {
+	const tag2el_t *a = ap;
+	const tag2el_t *b = bp;
+	const struct asn1p_type_tag_s *ta = &a->el_tag;
+	const struct asn1p_type_tag_s *tb = &b->el_tag;
+
+	if(ta->tag_class == tb->tag_class) {
+		if(ta->tag_value == tb->tag_value)
+			return 0;
+		else if(ta->tag_value < tb->tag_value)
+			return -1;
+		else
+			return 1;
+	} else if(ta->tag_class < tb->tag_class) {
+		return -1;
+	} else {
+		return 1;
+	}
+}
+
+static int
+asn1c_lang_C_type_SET_def(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	int elements;
+	int tags_impl_skip = 0;
+	int comp_mode = 0;	/* {root,ext=1,root,root,...} */
+	int extensible = 0;
+	tag2el_t *tag2el = NULL;
+	int tag2el_count = 0;
+	char *p;
+
+	/*
+	 * Fetch every inner tag from the tag to elements map.
+	 */
+	if(_fill_tag2el_map(arg, &tag2el, &tag2el_count, -1)) {
+		if(tag2el) free(tag2el);
+		return -1;
+	} else {
+		/*
+		 * Sort the map according to canonical order of their tags.
+		 */
+		_ctc_arg = arg;
+		qsort(tag2el, tag2el_count, sizeof(*tag2el), _tag2el_cmp);
+	}
+
+
+	REDIR(OT_STAT_DEFS);
+
+	OUT("#include <constr_SET.h>\n");
+	OUT("\n");
+
+	/*
+	 * Print out the table according to which the parsing is performed.
+	 */
+	p = MKID(expr->Identifier);
+	OUT("static asn1_SET_element_t asn1_DEF_%s_elements[] = {\n", p);
+
+	elements = 0;
+	INDENTED(TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type != A1TC_EXTENSIBLE) {
+			if(comp_mode == 1)
+				v->marker = EM_OPTIONAL;
+			elements++;
+		} else {
+			if(comp_mode < 3) comp_mode++;
+			continue;
+		}
+	OUT("{ ");
+		p = MKID(expr->Identifier);
+		OUT("offsetof(struct %s, ", p);
+		p = MKID(v->Identifier);
+		OUT("%s), ", p);
+		if(v->marker) {
+			OUT("1, /* Optional element */\n");
+		} else {
+			OUT("0,\n");
+		}
+		INDENT(+1);
+		if(C99_MODE) OUT(".tag = ");
+		_print_tag(arg, v, NULL);
+		OUT(",\n");
+		if(C99_MODE) OUT(".tag_mode = ");
+		if(v->tag.tag_class) {
+			if(v->tag.tag_mode == TM_IMPLICIT)
+			OUT("-1,\t/* IMPLICIT tag at current level */\n");
+			else
+			OUT("+1,\t/* EXPLICIT tag at current level */\n");
+		} else {
+			OUT("0,\n");
+		}
+		if(C99_MODE) OUT(".type = ");
+		OUT("(void *)&asn1_DEF_%s,\n",
+			asn1c_type_name(arg, v, TNF_SAFE));
+		if(C99_MODE) OUT(".name = ");
+		OUT("\"%s\"\n", v->Identifier);
+		OUT("},\n");
+		INDENT(-1);
+	});
+	OUT("};\n");
+
+	p = MKID(expr->Identifier);
+	OUT("static ber_tlv_tag_t asn1_DEF_%s_tags[] = {\n", p);
+	INDENTED(
+		if(expr->tag.tag_class) {
+			_print_tag(arg, expr, &expr->tag);
+			if(expr->tag.tag_mode != TM_EXPLICIT)
+				tags_impl_skip++;
+		}
+		if(!expr->tag.tag_class
+		|| (expr->meta_type == AMT_TYPE
+			&& expr->tag.tag_mode == TM_EXPLICIT)) {
+			struct asn1p_type_tag_s tag;
+			if(expr->tag.tag_class)
+				OUT(",\n");
+			tag.tag_class = TC_UNIVERSAL;
+			tag.tag_mode = TM_IMPLICIT;
+			tag.tag_value = expr_type2uclass_value[expr->expr_type];
+			_print_tag(arg, expr, &tag);
+		}
+		OUT("\n");
+	);
+	OUT("};\n");
+
+	/*
+	 * Tags to elements map.
+	 */
+	p = MKID(expr->Identifier);
+	OUT("static asn1_SET_tag2member_t asn1_DEF_%s_tag2el[] = {\n", p);
+	if(tag2el_count) {
+		int i;
+		for(i = 0; i < tag2el_count; i++) {
+			OUT("    { ");
+			_print_tag(arg, expr, &tag2el[i].el_tag);
+			OUT(", ");
+			OUT("%d ", tag2el[i].el_no);
+			OUT("}, /* %s at %d */\n",
+				tag2el[i].from_expr->Identifier,
+				tag2el[i].from_expr->_lineno
+			);
+		}
+	}
+	OUT("};\n");
+
+	/*
+	 * Emit a map of mandatory elements.
+	 */
+	OUT("static uint8_t asn1_DEF_%s_mmap", p);
+	OUT("[(%d + (8 * sizeof(unsigned int)) - 1) / 8]", elements);
+	OUT(" = {\n", p);
+	INDENTED(
+	if(elements) {
+		int delimit = 0;
+		int el = 0;
+		TQ_FOR(v, &(expr->members), next) {
+			if(v->expr_type == A1TC_EXTENSIBLE) continue;
+			if(delimit) {
+				OUT(",\n");
+				delimit = 0;
+			} else if(el) {
+				OUT(" | ");
+			}
+			OUT("(%d << %d)", v->marker?0:1, 7 - (el % 8));
+			if(el && (el % 8) == 0)
+				delimit = 1;
+			el++;
+		}
+	} else {
+		OUT("0");
+	}
+	);
+	OUT("\n");
+	OUT("};\n");
+
+	OUT("static asn1_SET_specifics_t asn1_DEF_%s_specs = {\n", p);
+	INDENTED(
+		OUT("sizeof(struct %s),\n", p);
+		OUT("offsetof(struct %s, _ber_dec_ctx),\n", p);
+		OUT("offsetof(struct %s, _presence_map),\n", p);
+		OUT("asn1_DEF_%s_elements,\n", p);
+		OUT("%d,\t/* Elements count */\n", elements);
+		OUT("asn1_DEF_%s_tag2el,\n", p);
+		OUT("%d,\t/* Count of tags in the map */\n", tag2el_count);
+		OUT("%d,\t/* Whether extensible */\n", extensible);
+		OUT("(unsigned int *)asn1_DEF_%s_mmap\t/* Mandatory elements map */\n", p);
+	);
+	OUT("};\n");
+	OUT("asn1_TYPE_descriptor_t asn1_DEF_%s = {\n", p);
+	INDENTED(
+		OUT("\"%s\",\n", expr->Identifier);
+		OUT("SET_constraint,\n");
+		OUT("SET_decode_ber,\n");
+		OUT("SET_encode_der,\n");
+		OUT("SET_print,\n");
+		OUT("SET_free,\n");
+		OUT("0,\t/* Use generic outmost tag fetcher */\n");
+		OUT("asn1_DEF_%s_tags,\n", p);
+		OUT("sizeof(asn1_DEF_%s_tags)\n", p);
+		OUT("\t/sizeof(asn1_DEF_%s_tags[0]),\n", p);
+		OUT("%d,\t/* Tags to skip */\n", tags_impl_skip);
+		OUT("%d,\t/* Whether CONSTRUCTED */\n", 1);
+		OUT("&asn1_DEF_%s_specs\t/* Additional specs */\n", p);
+	);
+	OUT("};\n");
+	OUT("\n");
+
+	REDIR(OT_DEPS);
+	OUT("#include <constr_SET.h>\n");
+	OUT("\n");
+	if(!arg->embed)
+	OUT("extern asn1_TYPE_descriptor_t asn1_DEF_%s;\n", p);
+	REDIR(OT_TYPE_DECLS);
+
+	return 0;
+}
+
+int
+asn1c_lang_C_type_SET_OF(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+
+	DEPENDENCIES;
+
+	if(arg->embed) {
+		OUT("struct %s {\n", MKID(expr->Identifier));
+	} else {
+		OUT("typedef struct %s {\n",
+			MKID(expr->Identifier));
+	}
+
+	TQ_FOR(v, &(expr->members), next) {
+		INDENTED(OUT("A_SET_OF(%s) list;\n",
+			asn1c_type_name(arg, v, TNF_RSAFE)));
+	}
+
+	PCTX_DEF;
+	OUT("} %s%s", expr->marker?"*":"", MKID(expr->Identifier));
+	if(arg->embed) OUT(";\n"); else OUT("_t;\n");
+
+	/*
+	 * SET OF/SEQUENCE OF definition, SET OF mode.
+	 */
+	return asn1c_lang_C_type_SEx_OF_def(arg, 0);
+}
+
+static int
+asn1c_lang_C_type_SEx_OF_def(arg_t *arg, int seq_of) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	int tags_impl_skip = 0;
+	char *p;
+
+	REDIR(OT_DEPS);
+
+	if(seq_of) {
+		OUT("#include <constr_SEQUENCE_OF.h>\n");
+	} else {
+		OUT("#include <constr_SET_OF.h>\n");
+		OUT("\n");
+	}
+
+	REDIR(OT_STAT_DEFS);
+
+	/*
+	 * Print out the table according to which the parsing is performed.
+	 */
+	p = MKID(expr->Identifier);
+	OUT("static asn1_SET_OF_element_t asn1_DEF_%s_elements[] = {\n", p);
+
+	INDENTED(OUT("{ ");
+		v = TQ_FIRST(&(expr->members));
+		INDENT(+1);
+		if(C99_MODE) OUT(".tag = ");
+		_print_tag(arg, v, NULL);
+		OUT(",\n");
+		if(C99_MODE) OUT(".type = ");
+		OUT("(void *)&asn1_DEF_%s",
+			asn1c_type_name(arg, v, TNF_SAFE));
+		OUT(" ");
+		OUT("},\n");
+		INDENT(-1);
+	);
+	OUT("};\n");
+
+	p = MKID(expr->Identifier);
+	OUT("static ber_tlv_tag_t asn1_DEF_%s_tags[] = {\n", p);
+	INDENTED(
+		if(expr->tag.tag_class) {
+			_print_tag(arg, expr, &expr->tag);
+			if(expr->tag.tag_mode != TM_EXPLICIT)
+				tags_impl_skip++;
+		}
+		if(!expr->tag.tag_class
+		|| (expr->meta_type == AMT_TYPE
+			&& expr->tag.tag_mode == TM_EXPLICIT)) {
+			struct asn1p_type_tag_s tag;
+			if(expr->tag.tag_class)
+				OUT(",\n");
+			tag.tag_class = TC_UNIVERSAL;
+			tag.tag_mode = TM_IMPLICIT;
+			tag.tag_value = expr_type2uclass_value[expr->expr_type];
+			_print_tag(arg, expr, &tag);
+		}
+		OUT("\n");
+	);
+	OUT("};\n");
+
+	OUT("static asn1_SET_OF_specifics_t asn1_DEF_%s_specs = {\n", p);
+	INDENTED(
+		OUT("sizeof(struct %s),\n", p);
+		OUT("offsetof(struct %s, _ber_dec_ctx),\n", p);
+		OUT("asn1_DEF_%s_elements\n", p);
+	);
+	OUT("};\n");
+	OUT("asn1_TYPE_descriptor_t asn1_DEF_%s = {\n", p);
+	INDENTED(
+		OUT("\"%s\",\n", expr->Identifier);
+		if(seq_of) {
+			OUT("SEQUENCE_OF_constraint,\n");
+			OUT("SEQUENCE_OF_decode_ber,\n");
+			OUT("SEQUENCE_OF_encode_der,\n");
+			OUT("SEQUENCE_OF_print,\n");
+			OUT("SEQUENCE_OF_free,\n");
+		} else {
+			OUT("SET_OF_constraint,\n");
+			OUT("SET_OF_decode_ber,\n");
+			OUT("SET_OF_encode_der,\n");
+			OUT("SET_OF_print,\n");
+			OUT("SET_OF_free,\n");
+		}
+		OUT("0,\t/* Use generic outmost tag fetcher */\n");
+		OUT("asn1_DEF_%s_tags,\n", p);
+		OUT("sizeof(asn1_DEF_%s_tags)\n", p);
+		OUT("\t/sizeof(asn1_DEF_%s_tags[0]),\n", p);
+		OUT("%d,\t/* Tags to skip */\n", tags_impl_skip);
+		OUT("%d,\t/* Whether CONSTRUCTED */\n", 1);
+		OUT("&asn1_DEF_%s_specs\t/* Additional specs */\n", p);
+	);
+	OUT("};\n");
+	OUT("\n");
+
+	REDIR(OT_DEPS);
+	if(!arg->embed)
+	OUT("extern asn1_TYPE_descriptor_t asn1_DEF_%s;\n", p);
+	REDIR(OT_TYPE_DECLS);
+
+	return 0;
+}
+
+int
+asn1c_lang_C_type_CHOICE(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	char *p;
+
+	DEPENDENCIES;
+
+	p = MKID(expr->Identifier);
+
+	if(arg->embed) {
+		OUT("struct %s {\n", p);
+	} else {
+		OUT("typedef struct %s {\n", p);
+	}
+
+	INDENTED(
+		OUT("enum {\n");
+		INDENTED(
+			OUT("%s_PR_NOTHING,\t"
+				"/* No components present */\n", p);
+			TQ_FOR(v, &(expr->members), next) {
+				if(v->expr_type == A1TC_EXTENSIBLE) continue;
+				p = MKID(expr->Identifier);
+				OUT("%s_PR_", p);
+				p = MKID(v->Identifier);
+				OUT("%s,\n", p, p);
+			}
+		);
+		OUT("} present;\n");
+
+		OUT("union {\n", p);
+		TQ_FOR(v, &(expr->members), next) {
+			EMBED(v);
+		}
+		if(UNNAMED_UNIONS)	OUT("};\n");
+		else			OUT("} choice;\n");
+	);
+
+	PCTX_DEF;
+	OUT("} %s%s", expr->marker?"*":"", MKID(expr->Identifier));
+	if(arg->embed) OUT(";\n"); else OUT("_t;\n");
+
+	return asn1c_lang_C_type_CHOICE_def(arg);
+}
+
+static int
+asn1c_lang_C_type_CHOICE_def(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *v;
+	int elements;	/* Number of elements */
+	int tags_impl_skip = 0;
+	int comp_mode = 0;	/* {root,ext=1,root,root,...} */
+	int extensible = 0;
+	tag2el_t *tag2el = NULL;
+	int tag2el_count = 0;
+	char *p;
+
+	/*
+	 * Fetch every inner tag from the tag to elements map.
+	 */
+	if(_fill_tag2el_map(arg, &tag2el, &tag2el_count, -1)) {
+		if(tag2el) free(tag2el);
+		return -1;
+	} else {
+		/*
+		 * Sort the map according to canonical order of their tags.
+		 */
+		_ctc_arg = arg;
+		qsort(tag2el, tag2el_count, sizeof(*tag2el), _tag2el_cmp);
+	}
+
+	REDIR(OT_STAT_DEFS);
+
+	OUT("#include <constr_CHOICE.h>\n");
+	OUT("\n");
+
+	/*
+	 * Print out the table according to which the parsing is performed.
+	 */
+	p = MKID(expr->Identifier);
+	OUT("static asn1_CHOICE_element_t asn1_DEF_%s_elements[] = {\n", p);
+
+	elements = 0;
+	INDENTED(TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type != A1TC_EXTENSIBLE) {
+			if(comp_mode == 1)
+				v->marker = EM_OPTIONAL;
+			elements++;
+		} else {
+			if(comp_mode < 3) comp_mode++;
+			continue;
+		}
+	OUT("{ ");
+		p = MKID(expr->Identifier);
+		OUT("offsetof(struct %s, ", p);
+		p = MKID(v->Identifier);
+		if(!UNNAMED_UNIONS) OUT("choice.");
+		OUT("%s), ", p);
+		if(v->marker) {
+			OUT("1, /* Optional element */\n");
+		} else {
+			OUT("0,\n");
+		}
+		INDENT(+1);
+		if(C99_MODE) OUT(".tag = ");
+		_print_tag(arg, v, NULL);
+		OUT(",\n");
+		if(C99_MODE) OUT(".tag_mode = ");
+		if(v->tag.tag_class) {
+			if(v->tag.tag_mode == TM_IMPLICIT)
+			OUT("-1,\t/* IMPLICIT tag at current level */\n");
+			else
+			OUT("+1,\t/* EXPLICIT tag at current level */\n");
+		} else {
+			OUT("0,\n");
+		}
+		if(C99_MODE) OUT(".type = ");
+		OUT("(void *)&asn1_DEF_%s,\n",
+			asn1c_type_name(arg, v, TNF_SAFE));
+		if(C99_MODE) OUT(".name = ");
+		OUT("\"%s\"\n", v->Identifier);
+		OUT("},\n");
+		INDENT(-1);
+	});
+	OUT("};\n");
+
+	p = MKID(expr->Identifier);
+	OUT("static ber_tlv_tag_t asn1_DEF_%s_tags[] = {\n", p);
+	if(arg->embed) {
+		/*
+		 * Our parent structure has already taken this into account.
+		 */
+	} else {
+	  INDENTED(
+		if(expr->tag.tag_class) {
+			_print_tag(arg, expr, &expr->tag);
+			if(expr->tag.tag_mode != TM_EXPLICIT)
+				tags_impl_skip++;
+		}
+		OUT("\n");
+	  );
+	}
+	OUT("};\n");
+
+	/*
+	 * Tags to elements map.
+	 */
+	p = MKID(expr->Identifier);
+	OUT("static asn1_CHOICE_tag2member_t asn1_DEF_%s_tag2el[] = {\n", p);
+	if(tag2el_count) {
+		int i;
+		for(i = 0; i < tag2el_count; i++) {
+			OUT("    { ");
+			_print_tag(arg, expr, &tag2el[i].el_tag);
+			OUT(", ");
+			OUT("%d ", tag2el[i].el_no);
+			OUT("}, /* %s at %d */\n",
+				tag2el[i].from_expr->Identifier,
+				tag2el[i].from_expr->_lineno
+			);
+		}
+	}
+	OUT("};\n");
+
+	OUT("static asn1_CHOICE_specifics_t asn1_DEF_%s_specs = {\n", p);
+	INDENTED(
+		OUT("sizeof(struct %s),\n", p);
+		OUT("offsetof(struct %s, _ber_dec_ctx),\n", p);
+		OUT("offsetof(struct %s, present),\n", p);
+		OUT("sizeof(((struct %s *)0)->present),\n", p);
+		OUT("asn1_DEF_%s_elements,\n", p);
+		OUT("%d,\t/* Elements count */\n", elements);
+		OUT("asn1_DEF_%s_tag2el,\n", p);
+		OUT("%d,\t/* Count of tags in the map */\n", tag2el_count);
+		OUT("%d\t/* Whether extensible */\n", extensible);
+	);
+	OUT("};\n");
+	OUT("asn1_TYPE_descriptor_t asn1_DEF_%s = {\n", p);
+	INDENTED(
+		OUT("\"%s\",\n", expr->Identifier);
+		OUT("CHOICE_constraint,\n");
+		OUT("CHOICE_decode_ber,\n");
+		OUT("CHOICE_encode_der,\n");
+		OUT("CHOICE_print,\n");
+		OUT("CHOICE_free,\n");
+		OUT("CHOICE_outmost_tag,\n");
+		OUT("asn1_DEF_%s_tags,\n", p);
+		OUT("sizeof(asn1_DEF_%s_tags)\n", p);
+		OUT("\t/sizeof(asn1_DEF_%s_tags[0]),\n", p);
+		OUT("%d,\t/* Tags to skip */\n", tags_impl_skip);
+		OUT("%d,\t/* Whether CONSTRUCTED */\n", 1);
+		OUT("&asn1_DEF_%s_specs\t/* Additional specs */\n", p);
+	);
+	OUT("};\n");
+	OUT("\n");
+
+	REDIR(OT_DEPS);
+	if(!arg->embed)
+	OUT("extern asn1_TYPE_descriptor_t asn1_DEF_%s;\n", p);
+	REDIR(OT_TYPE_DECLS);
+
+	return 0;
+}
+
+int
+asn1c_lang_C_type_REFERENCE(arg_t *arg) {
+	asn1p_ref_t *ref;
+
+	ref = arg->expr->reference;
+	if(ref->components[ref->comp_count-1].name[0] == '&') {
+		asn1p_module_t *mod;
+		asn1p_expr_t *extract;
+		arg_t tmp;
+		int ret;
+
+		extract = asn1f_class_access_ex(arg->asn, arg->mod, arg->expr,
+			ref, &mod);
+		if(extract == NULL)
+			return -1;
+
+		extract = asn1p_expr_clone(extract);
+		if(extract) {
+			if(extract->Identifier)
+				free(extract->Identifier);
+			extract->Identifier = strdup(arg->expr->Identifier);
+			if(extract->Identifier == NULL) {
+				asn1p_expr_free(extract);
+				return -1;
+			}
+		} else {
+			return -1;
+		}
+
+		tmp = *arg;
+		tmp.asn = arg->asn;
+		tmp.mod = mod;
+		tmp.expr = extract;
+
+		ret = arg->default_cb(&tmp);
+
+		asn1p_expr_free(extract);
+
+		return ret;
+	}
+
+
+	return asn1c_lang_C_type_SIMPLE_TYPE(arg);
+}
+
+int
+asn1c_lang_C_type_SIMPLE_TYPE(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	int tags_impl_skip = 0;
+	char *p;
+
+	if(arg->embed) {
+		REDIR(OT_TYPE_DECLS);
+
+		OUT("%s\t", asn1c_type_name(arg, arg->expr,
+			expr->marker?TNF_RSAFE:TNF_CTYPE));
+		OUT("%s", expr->marker?"*":" ");
+		OUT("%s;", MKID(expr->Identifier));
+		if(expr->marker) OUT("\t/* %s */",
+			(expr->marker==EM_OPTIONAL)?"OPTIONAL":"DEFAULT");
+		OUT("\n");
+		return 0;
+	}
+
+	REDIR(OT_DEPS);
+
+	OUT("#include <%s.h>\n", asn1c_type_name(arg, expr, TNF_INCLUDE));
+
+	REDIR(OT_TYPE_DECLS);
+
+	OUT("typedef %s\t", asn1c_type_name(arg, arg->expr, TNF_CTYPE));
+	OUT("%s", expr->marker?"*":" ");
+	OUT("%s_t;\n", MKID(expr->Identifier));
+	OUT("\n");
+
+	REDIR(OT_STAT_DEFS);
+
+	p = MKID(expr->Identifier);
+	OUT("static ber_tlv_tag_t asn1_DEF_%s_tags[] = {\n", p);
+	INDENTED(
+		if(expr->tag.tag_class) {
+			_print_tag(arg, expr, &expr->tag);
+			if(expr->tag.tag_mode != TM_EXPLICIT)
+				tags_impl_skip++;
+		}
+		if(!expr->tag.tag_class
+		|| (expr->meta_type == AMT_TYPE
+			&& expr->tag.tag_mode == TM_EXPLICIT)) {
+			struct asn1p_type_tag_s tag;
+			if(expr->tag.tag_class)
+				OUT(",\n");
+			tag.tag_class = TC_UNIVERSAL;
+			tag.tag_mode = TM_IMPLICIT;
+			tag.tag_value = expr_type2uclass_value[expr->expr_type];
+			_print_tag(arg, expr, &tag);
+		}
+		OUT("\n");
+	);
+	OUT("};\n");
+
+	OUT("asn1_TYPE_descriptor_t asn1_DEF_%s = {\n", p);
+	INDENTED(
+		OUT("\"%s\",\n", expr->Identifier);
+		OUT("%s_constraint,\n", p);
+		OUT("%s_decode_ber,\n", p);
+		OUT("%s_encode_der,\n", p);
+		OUT("%s_print,\n", p);
+		OUT("%s_free,\n", p);
+		OUT("0,\t/* Use generic outmost tag fetcher */\n");
+		OUT("asn1_DEF_%s_tags,\n", p);
+		OUT("sizeof(asn1_DEF_%s_tags)\n", p);
+		OUT("\t/sizeof(asn1_DEF_%s_tags[0]),\n", p);
+		OUT("%d,\t/* Tags to skip */\n", tags_impl_skip);
+		OUT("-0\t/* Unknown yet */\n");
+	);
+	OUT("};\n");
+	OUT("\n");
+
+	/*
+	 * Constraint checking.
+	 */
+	if(expr->constraints)	/* Emit tables with FROM() constraints */
+		emit_alphabet_tables(arg, expr->constraints, 0);
+	p = MKID(expr->Identifier);
+	OUT("int\n");
+	OUT("%s_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,\n", p);
+	INDENTED(
+	OUT("\t\tasn_app_consume_bytes_f *app_errlog, void *app_key) {\n");
+	OUT("\n");
+	if(expr->constraints) {
+
+		emit_constraint_checking_code(arg);
+
+		OUT("/* Check the constraints of the underlying type */\n");
+		OUT("return asn1_DEF_%s.check_constraints\n",
+			asn1c_type_name(arg, expr, TNF_SAFE));
+		OUT("\t(td, sptr, app_errlog, app_key);\n");
+	} else {
+		OUT("/* Make the underlying type checker permanent */\n");
+		OUT("td->check_constraints = asn1_DEF_%s.check_constraints;\n",
+			asn1c_type_name(arg, expr, TNF_SAFE));
+		OUT("return td->check_constraints\n");
+		OUT("\t(td, sptr, app_errlog, app_key);\n");
+	}
+	);
+	OUT("}\n");
+	OUT("\n");
+
+	/*
+	 * Emit suicidal functions.
+	 */
+
+	{
+	/*
+	 * This function replaces certain fields from the definition
+	 * of a type with the corresponding fields from the basic type
+	 * (from which the current type is inherited).
+	 */
+	char *type_name = asn1c_type_name(arg, expr, TNF_SAFE);
+	OUT("/*\n");
+	OUT(" * This type is implemented using %s,\n", type_name);
+	OUT(" * so adjust the DEF appropriately.\n");
+	OUT(" */\n");
+	OUT("static void\n");
+	OUT("inherit_TYPE_descriptor(asn1_TYPE_descriptor_t *td) {\n");
+	INDENT(+1);
+	OUT("td->ber_decoder = asn1_DEF_%s.ber_decoder;\n", type_name);
+	OUT("td->der_encoder = asn1_DEF_%s.der_encoder;\n", type_name);
+	OUT("td->free_struct = asn1_DEF_%s.free_struct;\n", type_name);
+	OUT("td->print_struct = asn1_DEF_%s.print_struct;\n", type_name);
+	OUT("td->last_tag_form = asn1_DEF_%s.last_tag_form;\n", type_name);
+	OUT("td->specifics = asn1_DEF_%s.specifics;\n", type_name);
+	INDENT(-1);
+	OUT("}\n");
+	OUT("\n");
+	}
+
+	p = MKID(expr->Identifier);
+	OUT("ber_dec_rval_t\n");
+	OUT("%s_decode_ber(asn1_TYPE_descriptor_t *td,\n", p);
+	INDENTED(
+	OUT("\tvoid **structure, void *bufptr, size_t size, int tag_mode) {\n");
+	OUT("inherit_TYPE_descriptor(td);\n");
+	OUT("return td->ber_decoder(td, structure,\n");
+	OUT("\tbufptr, size, tag_mode);\n");
+	);
+	OUT("}\n");
+	OUT("\n");
+
+	p = MKID(expr->Identifier);
+	OUT("der_enc_rval_t\n");
+	OUT("%s_encode_der(asn1_TYPE_descriptor_t *td,\n", p);
+	INDENTED(
+	OUT("\tvoid *structure, int tag_mode, ber_tlv_tag_t tag,\n");
+	OUT("\tasn_app_consume_bytes_f *cb, void *app_key) {\n");
+	OUT("inherit_TYPE_descriptor(td);\n");
+	OUT("return td->der_encoder(td, structure, tag_mode, tag, cb, app_key);\n");
+	);
+	OUT("}\n");
+	OUT("\n");
+
+	p = MKID(expr->Identifier);
+	OUT("int\n");
+	OUT("%s_print(asn1_TYPE_descriptor_t *td, const void *struct_ptr,\n", p);
+	INDENTED(
+	OUT("\tint ilevel, asn_app_consume_bytes_f *cb, void *app_key) {\n");
+	OUT("inherit_TYPE_descriptor(td);\n");
+	OUT("return td->print_struct(td, struct_ptr, ilevel, cb, app_key);\n");
+	);
+	OUT("}\n");
+	OUT("\n");
+
+	p = MKID(expr->Identifier);
+	OUT("void\n");
+	OUT("%s_free(asn1_TYPE_descriptor_t *td,\n", p);
+	INDENTED(
+	OUT("\tvoid *struct_ptr, int contents_only) {\n");
+	OUT("inherit_TYPE_descriptor(td);\n");
+	OUT("td->free_struct(td, struct_ptr, contents_only);\n");
+	);
+	OUT("}\n");
+	OUT("\n");
+
+	REDIR(OT_FUNC_DECLS);
+
+	p = MKID(expr->Identifier);
+	OUT("extern asn1_TYPE_descriptor_t asn1_DEF_%s;\n", p);
+	OUT("asn_constr_check_f %s_constraint;\n", p);
+	OUT("ber_type_decoder_f %s_decode_ber;\n", p);
+	OUT("der_type_encoder_f %s_encode_der;\n", p);
+	OUT("asn_struct_print_f %s_print;\n", p);
+	OUT("asn_struct_free_f %s_free;\n", p);
+
+	return 0;
+}
+
+int
+asn1c_lang_C_type_EXTENSIBLE(arg_t *arg) {
+
+	OUT("/*\n");
+	OUT(" * This type is extensible,\n");
+	OUT(" * possible extensions are below.\n");
+	OUT(" */\n");
+
+	return 0;
+}
+
+static int
+_print_tag(arg_t *arg, asn1p_expr_t *expr, struct asn1p_type_tag_s *tag_p) {
+	struct asn1p_type_tag_s tag;
+
+	if(tag_p) {
+		tag = *tag_p;
+	} else {
+		if(asn1f_fetch_tag(arg->asn, arg->mod, expr, &tag)) {
+			OUT("-1 /* Ambiguous tag (CHOICE?) */");
+			return 0;
+		}
+	}
+
+	OUT("(");
+	switch(tag.tag_class) {
+	case TC_UNIVERSAL:		OUT("ASN_TAG_CLASS_UNIVERSAL"); break;
+	case TC_APPLICATION:		OUT("ASN_TAG_CLASS_APPLICATION"); break;
+	case TC_CONTEXT_SPECIFIC:	OUT("ASN_TAG_CLASS_CONTEXT"); break;
+	case TC_PRIVATE:		OUT("ASN_TAG_CLASS_PRIVATE"); break;
+	case TC_NOCLASS:
+		break;
+	}
+	OUT(" | (%lld << 2))", tag.tag_value);
+
+	return 0;
+}
+
+/*
+ * For constructed types, number of external tags may be greater than
+ * number of elements in the type because of CHOICE type.
+ * T ::= SET {		-- Three possible tags:
+ *     a INTEGER,	-- One tag is here...
+ *     b Choice1	-- ... and two more tags are there.
+ * }
+ * Choice1 ::= CHOICE {
+ *     s1 IA5String,
+ *     s2 ObjectDescriptor
+ * }
+ */
+static int
+_fill_tag2el_map(arg_t *arg, tag2el_t **tag2el, int *count, int el_no) {
+	asn1p_expr_t *expr = arg->expr;
+	arg_t tmparg = *arg;
+	asn1p_expr_t *v;
+	int element = 0;
+
+	TQ_FOR(v, &(expr->members), next) {
+		if(v->expr_type == A1TC_EXTENSIBLE)
+			continue;
+
+		tmparg.expr = v;
+
+		if(_add_tag2el_member(&tmparg, tag2el, count,
+				(el_no==-1)?element:el_no)) {
+			return -1;
+		}
+
+		element++;
+	}
+
+	return 0;
+}
+
+static int
+_add_tag2el_member(arg_t *arg, tag2el_t **tag2el, int *count, int el_no) {
+	struct asn1p_type_tag_s tag;
+	int ret;
+
+	assert(el_no >= 0);
+
+	ret = asn1f_fetch_tag(arg->asn, arg->mod, arg->expr, &tag);
+	if(ret == 0) {
+		void *p;
+		p = realloc(*tag2el, sizeof(tag2el_t) * ((*count) + 1));
+		if(p)	*tag2el = p;
+		else	return -1;
+
+		DEBUG("Found tag for %s: %ld",
+			arg->expr->Identifier,
+			(long)tag.tag_value);
+
+		(*tag2el)[*count].el_tag = tag;
+		(*tag2el)[*count].el_no = el_no;
+		(*tag2el)[*count].from_expr = arg->expr;
+		(*count)++;
+		return 0;
+	}
+
+	DEBUG("Searching tag in complex expression %s:%x at line %d",
+		arg->expr->Identifier,
+		arg->expr->expr_type,
+		arg->expr->_lineno);
+
+	/*
+	 * Iterate over members of CHOICE type.
+	 */
+	if(arg->expr->expr_type == ASN_CONSTR_CHOICE) {
+		return _fill_tag2el_map(arg, tag2el, count, el_no);
+	}
+
+	if(arg->expr->expr_type == A1TC_REFERENCE) {
+		arg_t tmp = *arg;
+		asn1p_expr_t *expr;
+		expr = asn1f_lookup_symbol_ex(tmp.asn, &tmp.mod, tmp.expr,
+			arg->expr->reference);
+		if(expr) {
+			tmp.expr = expr;
+			return _add_tag2el_member(&tmp, tag2el, count, el_no);
+		} else {
+			FATAL("Cannot dereference %s at line %d",
+				arg->expr->Identifier,
+				arg->expr->_lineno);
+			return -1;
+		}
+	}
+
+	DEBUG("No tag for %s at line %d",
+		arg->expr->Identifier,
+		arg->expr->_lineno);
+
+	return -1;
+}
+
+static int
+emit_constraint_checking_code(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_type_e etype;
+	int size_present, value_present;
+
+	if(expr->constraints == NULL)
+		return 0;	/* No constraints defined */
+
+	etype = _find_terminal_type(arg);
+
+	size_present = check_constraint_type_presence(expr->constraints,
+		ACT_CT_SIZE);
+	value_present = check_constraint_type_presence(expr->constraints,
+		ACT_EL_VALUE);
+
+	if(size_present || value_present) {
+		OUT("%s_t *st = sptr;\n", MKID(arg->expr->Identifier));
+		if(size_present) {
+			OUT("size_t size;\n");
+			OUT("size_t min_size __attribute__ ((unused)) = %ld;\n",
+				compute_min_size(arg));
+			OUT("size_t max_size __attribute__ ((unused)) = %ld;\n",
+				compute_max_size(arg));
+		}
+		if(value_present)
+		switch(etype) {
+		case ASN_BASIC_INTEGER:
+		case ASN_BASIC_ENUMERATED:
+			OUT("long value;\n");
+			break;
+		case ASN_BASIC_BOOLEAN:
+			OUT("int value;\n");
+			break;
+		default:
+			break;
+		}
+		OUT("\n");
+	}
+
+	OUT("if(!sptr) {\n");
+	INDENT(+1);
+		OUT("_ASN_ERRLOG(\"%%s: value not given\", td->name);\n");
+		OUT("return -1;\n");
+	INDENT(-1);
+	OUT("}\n");
+	OUT("\n");
+
+	if(size_present)
+		emit_size_determination_code(arg);
+	if(value_present)
+		emit_value_determination_code(arg);
+
+	OUT("\n");
+	OUT("if(\n");
+	emit_single_constraint_check(arg, expr->constraints, 0);
+	OUT(") {\n");
+	INDENTED(OUT("/* Constraint check succeeded */\n"));
+	OUT("} else {\n");
+	INDENT(+1);
+		OUT("_ASN_ERRLOG(\"%%s: constraint failed\", td->name);\n");
+		OUT("return -1;\n");
+	INDENT(-1);
+	OUT("}\n");
+
+	return 0;
+}
+
+static int
+emit_single_constraint_check(arg_t *arg, asn1p_constraint_t *ct, int mode) {
+	char *s_v;
+	int el;
+
+	assert(arg && ct);
+
+	switch(ct->type) {
+	case ACT_INVALID:
+		assert(ct->type != ACT_INVALID);
+		OUT("-1 /* Invalid constraint at line %d */\n", ct->_lineno);
+		break;
+	case ACT_EL_VALUE:
+		OUT("(");
+		if(mode == ACT_CT_SIZE)	s_v = "size";
+		else			s_v = "value";
+		OUT("%s", s_v);
+		if(ct->value->type != ATV_TRUE)
+			OUT(" == ");
+		switch(ct->value->type) {
+		case ATV_INTEGER: OUT("%lld",
+			(long long)ct->value->value.v_integer); break;
+		case ATV_MIN:	OUT("min_%s", s_v);	break;
+		case ATV_MAX:	OUT("max_%s", s_v);	break;
+		case ATV_FALSE:	OUT("0");	break;
+		case ATV_TRUE:			break;
+		default:
+			break;
+		}
+		OUT(")\n");
+		break;
+	case ACT_EL_RANGE:
+	case ACT_EL_LLRANGE:
+	case ACT_EL_RLRANGE:
+	case ACT_EL_ULRANGE:
+		if(mode == ACT_CT_SIZE) {
+			s_v = "size";
+		} else {
+			s_v = "value";
+		}
+		OUT("((%s", s_v);
+		switch(ct->type) {
+		case ACT_EL_RANGE:
+		case ACT_EL_RLRANGE:
+			OUT(" >= ");	break;
+		case ACT_EL_LLRANGE:
+		case ACT_EL_ULRANGE:
+			OUT(" > ");	break;
+		default:	break;
+		}
+		switch(ct->range_start->type) {
+		case ATV_INTEGER: OUT("%lld",
+			(long long)ct->range_start->value.v_integer); break;
+		case ATV_MIN:	OUT("min_%s", s_v);	break;
+		case ATV_MAX:	OUT("max_%s", s_v);	break;
+		case ATV_FALSE:	OUT("0");	break;
+		case ATV_TRUE:			break;
+		default:
+			break;
+		}
+		OUT(") && (%s", s_v);
+		switch(ct->type) {
+		case ACT_EL_RANGE:
+		case ACT_EL_LLRANGE:
+			OUT(" <= ");	break;
+		case ACT_EL_RLRANGE:
+		case ACT_EL_ULRANGE:
+			OUT(" < ");	break;
+		default:	break;
+		}
+		switch(ct->range_stop->type) {
+		case ATV_INTEGER: OUT("%lld",
+			(long long)ct->range_stop->value.v_integer); break;
+		case ATV_MIN:	OUT("min_%s", s_v);	break;
+		case ATV_MAX:	OUT("max_%s", s_v);	break;
+		case ATV_FALSE:	OUT("0");	break;
+		case ATV_TRUE:			break;
+		default:
+			break;
+		}
+		OUT("))\n");
+		break;
+	case ACT_EL_EXT:
+		OUT("0 /* Extensible (...), but not defined herein */\n");
+		break;
+	case ACT_CT_SIZE:
+		if(mode) {
+			OUT("0 /* Invalid constraint at line %d */\n",
+				ct->_lineno);
+			return -1;
+		}
+		assert(ct->el_count == 1);
+		return emit_single_constraint_check(arg,
+			ct->elements[0], ACT_CT_SIZE);
+	case ACT_CT_FROM:
+		if(mode) {
+			OUT("0 /* Invalid constraint at line %d */\n",
+				ct->_lineno);
+			return -1;
+		}
+		OUT("check_alphabet_%x(sptr)\n", ct);
+		break;
+	case ACT_CT_WCOMP:
+	case ACT_CT_WCOMPS:
+		OUT("%d /* Unsupported constraint at line %d */\n",
+			ct->type, ct->_lineno);
+		return -1;
+		break;
+	case ACT_CA_SET:
+		OUT("(\n");
+		INDENT(+1);
+		for(el = 0; el < ct->el_count; el++) {
+			if(el) OUT("&& ");
+			emit_single_constraint_check(arg,
+				ct->elements[el], mode);
+		}
+		INDENT(-1);
+		OUT(")\n");
+		break;
+	case ACT_CA_CSV:
+		OUT("(\n");
+		INDENT(+1);
+		for(el = 0; el < ct->el_count; el++) {
+			if(el) OUT("|| ");
+			emit_single_constraint_check(arg,
+				ct->elements[el], mode);
+		}
+		INDENT(-1);
+		OUT(")\n");
+		break;
+	case ACT_CA_UNI:
+		OUT("(\n");
+		INDENT(+1);
+		for(el = 0; el < ct->el_count; el++) {
+			if(el) OUT("|| ");
+			emit_single_constraint_check(arg,
+				ct->elements[el], mode);
+		}
+		INDENT(-1);
+		OUT(")\n");
+		break;
+	case ACT_CA_INT:
+		OUT("(\n");
+		INDENT(+1);
+		for(el = 0; el < ct->el_count; el++) {
+			if(el) OUT("&& ");
+			emit_single_constraint_check(arg,
+				ct->elements[el], mode);
+		}
+		INDENT(-1);
+		OUT(")\n");
+		break;
+	case ACT_CA_CRC:
+		WARNING("Unsupported component relation constraint at line %d",
+			ct->_lineno);
+		OUT("%d /* Unsupported component relation constraint "
+			"at line %d */\n",
+			ct->type, ct->_lineno);
+		return -1;
+	case ACT_CA_EXC:
+		WARNING("Unsupported EXCEPT constraint at line %d",
+			ct->_lineno);
+		OUT("%d /* Unsupported EXCEPT constraint at line %d */\n",
+			ct->type, ct->_lineno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+check_constraint_type_presence(asn1p_constraint_t *ct, enum asn1p_constraint_type_e type) {
+	int el;
+
+	if(ct == NULL) return 0;
+
+	if(ct->type == type) return 1;
+
+	if(type == ACT_EL_VALUE) {
+		if(ct->type >= ACT_CT_SIZE
+		&& ct->type <= ACT_CT_WCOMPS)
+			/* Values defined further
+			 * are not really value's values */
+			return 0;
+		if(ct->type > ACT_EL_VALUE && ct->type < ACT_CT_SIZE)
+			return 1;	/* Also values */
+	}
+
+	for(el = 0; el < ct->el_count; el++) {
+		if(check_constraint_type_presence(ct->elements[el], type))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int
+emit_alphabet_tables(arg_t *arg, asn1p_constraint_t *ct, int *table) {
+	int ch = 0;
+	int ch_start = 0;
+	int ch_stop = 0;
+	int el = 0;
+
+	assert(arg && ct);
+
+	switch(ct->type) {
+	case ACT_INVALID:
+		break;
+	case ACT_EL_VALUE:
+		if(!table) break;
+
+		switch(ct->value->type) {
+		case ATV_INTEGER:
+			if(ct->value->value.v_integer < 0
+			|| ct->value->value.v_integer > 255) {
+				OUT("\n");
+				OUT("#error Value %lld out of range "
+				"for alphabet character at line %d\n",
+				(long long)ct->value->value.v_integer,
+					ct->_lineno);
+				break;
+			} else {
+				ch = ct->value->value.v_integer;
+				table[ch] = 1;
+			}
+			break;
+		case ATV_STRING:
+			for(ch = 0; ch < ct->value->value.string.size; ch++)
+				table[ct->value->value.string.buf[ch]] = 1;
+			break;
+		default:
+			OUT("\n");
+			WARNING("Invalid alphabet character specification "
+				"at line %d", ct->_lineno);
+			OUT("#error Invalid alphabet character specification "
+				"at line %d\n", ct->_lineno);
+			break;
+		}
+		break;
+	case ACT_EL_RANGE:
+	case ACT_EL_LLRANGE:
+	case ACT_EL_RLRANGE:
+	case ACT_EL_ULRANGE:
+		if(!table) break;
+
+		ch_start = 0;
+		ch_stop = 255;
+
+		switch(ct->range_start->type) {
+		case ATV_INTEGER:
+			ch_start = ct->range_start->value.v_integer; break;
+		case ATV_MIN:	ch_start = 0;	break;
+		case ATV_MAX:	ch_start = 255;	break;
+		case ATV_STRING:
+			if(ct->range_start->value.string.size == 1) {
+				ch_start = ct->range_start->value.string.buf[0];
+				break;
+			}
+			/* Fall through */
+		default:
+			OUT("\n");
+			FATAL("Invalid alphabet range constraint "
+				"at line %d\n", ct->_lineno);
+			OUT("#error Invalid alphabet range constraint "
+				"at line %d\n", ct->_lineno);
+			return -1;
+		}
+
+		switch(ct->range_stop->type) {
+		case ATV_INTEGER:
+			ch_stop = ct->range_stop->value.v_integer; break;
+		case ATV_MIN:	ch_stop = 0;	break;
+		case ATV_MAX:	ch_stop = 255;	break;
+		case ATV_STRING:
+			if(ct->range_stop->value.string.size == 1) {
+				ch_stop = ct->range_stop->value.string.buf[0];
+				break;
+			}
+			/* Fall through */
+		default:
+			OUT("\n");
+			FATAL("Invalid alphabet range constraint "
+				"at line %d\n", ct->_lineno);
+			OUT("#error Invalid alphabet range constraint "
+				"at line %d\n", ct->_lineno);
+			break;
+		}
+
+		switch(ct->type) {
+		case ACT_EL_RANGE:	break;
+		case ACT_EL_RLRANGE:	ch_stop--;	break;
+		case ACT_EL_LLRANGE:	ch_start++;	break;
+		case ACT_EL_ULRANGE:	ch_start++; ch_stop--; break;
+		default:	break;
+		}
+
+		if(ch_start > ch_stop) {
+			WARNING("Empty character range "
+			"alphabet constraint at line %d", ct->_lineno);
+			OUT("#warning Empty character range "
+			"alphabet constraint at line %d\n", ct->_lineno);
+			break;
+		}
+
+		for(ch = ch_start; ch <= ch_stop; ch++) {
+			if(ch < 0 || ch > 255) continue;
+			table[ch] = 1;
+		}
+
+		break;
+	case ACT_EL_EXT:
+		break;
+	case ACT_CT_SIZE:
+		break;
+	case ACT_CT_FROM:
+	  if(table) {
+			OUT("#error Nested FROM in subtype constraints\n");
+			return -1;
+	  } else {
+			table = alloca(256 * sizeof(table[0]));
+			memset(table, 0, 256 * sizeof(table[0]));
+
+			for(el = 0; el < ct->el_count; el++) {
+				emit_alphabet_tables(arg, ct->elements[el],
+					table);
+			}
+			OUT("static int alphabet_table_%x[256] = {\n", ct);
+			for(ch = 0; ch < 256; ch++) {
+				OUT("%d,", table[ch]?1:0);
+				if(!((ch+1) % 16)) {
+				  if(ch) {
+					int c;
+					OUT("\t/* ");
+					for(c = ch - 16; c < ch; c++) {
+						if(table[c]) {
+							if(c > 0x20
+								&& c < 0x80)
+								OUT("%c", c);
+							else
+								OUT(".", c);
+						} else {
+							OUT(" ");
+						}
+					}
+					OUT(" */");
+				  }
+				  OUT("\n");
+				}
+			}
+			OUT("};\n");
+			OUT("static int check_alphabet_%x(void *sptr) {\n", ct);
+			INDENT(+1);
+			OUT("int *table = alphabet_table_%x;\n", ct);
+			emit_alphabet_check_cycle(arg);
+			OUT("return 1;\n");
+			INDENT(-1);
+			OUT("};\n");
+	  }
+		break;
+	case ACT_CT_WCOMP:
+	case ACT_CT_WCOMPS:
+		break;
+	case ACT_CA_CRC:
+		break;
+	case ACT_CA_SET:
+	case ACT_CA_CSV:
+	case ACT_CA_UNI:
+		for(el = 0; el < ct->el_count; el++)
+			emit_alphabet_tables(arg, ct->elements[el], table);
+		break;
+	case ACT_CA_INT:
+		if(table) {
+			int table2[256];
+
+			assert(ct->el_count >= 1);
+			emit_alphabet_tables(arg, ct->elements[0], table);
+			for(el = 1; el < ct->el_count; el++) {
+				memset(table2, 0, sizeof(table2));
+				emit_alphabet_tables(arg,
+					ct->elements[el], table2);
+				/* Intersection */
+				for(ch = 0; ch < 256; ch++) {
+					if(table2[ch] == 0)
+						table[ch] = 0;
+				}
+			}
+		} else {
+			for(el = 0; el < ct->el_count; el++)
+				emit_alphabet_tables(arg, ct->elements[el], 0);
+		}
+
+		break;
+	case ACT_CA_EXC:
+		OUT("EXC\n");
+		if(table) {
+			int table2[256];
+
+			assert(ct->el_count >= 1);
+			emit_alphabet_tables(arg, ct->elements[0], table);
+			for(el = 1; el < ct->el_count; el++) {
+				memset(table2, 0, sizeof(table2));
+				emit_alphabet_tables(arg,
+					ct->elements[el], table2);
+				/* Exclusion */
+				for(ch = 0; ch < 256; ch++) {
+					if(table2[ch])
+						table[ch] = 0;
+				}
+			}
+		} else {
+			for(el = 0; el < ct->el_count; el++)
+				emit_alphabet_tables(arg, ct->elements[el], 0);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int
+emit_alphabet_check_cycle(arg_t *arg) {
+	asn1p_expr_type_e etype;
+
+	etype = _find_terminal_type(arg);
+	if(!(etype & ASN_STRING_MASK)
+	&& !(etype == ASN_BASIC_OCTET_STRING)) {
+		OUT("#error Cannot apply FROM constraint to ASN.1 type %s\n",
+			ASN_EXPR_TYPE2STR(etype));
+		return -1;
+	}
+
+	OUT("/* The underlying type is %s */\n",
+		ASN_EXPR_TYPE2STR(etype));
+	OUT("%s_t *st = sptr;\n", MKID(arg->expr->Identifier));
+
+	switch(etype) {
+	case ASN_STRING_UTF8String:
+		OUT("uint8_t *ch = st->buf;\n");
+		OUT("uint8_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("for(; ch < end; ch++) {\n");
+		INDENT(+1);
+		OUT("if(*ch >= 0x80 || !table[*ch]) return 0;\n");
+		INDENT(-1);
+		OUT("}\n");
+		break;
+	case ASN_STRING_UniversalString:
+		OUT("uint32_t *ch = st->buf;\n");
+		OUT("uint32_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("for(; ch < end; ch++) {\n");
+		INDENT(+1);
+		OUT("uint32_t wc = (((uint8_t *)ch)[0] << 24)\n");
+		OUT("\t\t| (((uint8_t *)ch)[1] << 16)\n");
+		OUT("\t\t| (((uint8_t *)ch)[2] << 8)\n");
+		OUT("\t\t|  ((uint8_t *)ch)[3]\n");
+		OUT("if(wc > 255 || !table[wc]) return 0;\n");
+		INDENT(-1);
+		OUT("}\n");
+		OUT("if(ch != end) return 0; /* (size%4)! */\n");
+		break;
+	case ASN_STRING_BMPString:
+		OUT("uint16_t *ch = st->buf;\n");
+		OUT("uint16_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("for(; ch < end; ch++) {\n");
+		INDENT(+1);
+		OUT("uint16_t wc = (((uint8_t *)ch)[0] << 8)\n");
+		OUT("\t\t| ((uint8_t *)ch)[1];\n");
+		OUT("if(wc > 255 || !table[wc]) return 0;\n");
+		INDENT(-1);
+		OUT("}\n");
+		OUT("if(ch != end) return 0; /* (size%2)! */\n");
+		break;
+	case ASN_BASIC_OCTET_STRING:
+	default:
+		OUT("uint8_t *ch = st->buf;\n");
+		OUT("uint8_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("for(; ch < end; ch++) {\n");
+		INDENT(+1);
+		OUT("if(!table[*ch]) return 0;\n");
+		INDENT(-1);
+		OUT("}\n");
+		break;
+	}
+
+	return 0;
+}
+
+static int
+emit_size_determination_code(arg_t *arg) {
+	asn1p_expr_type_e etype = _find_terminal_type(arg);
+
+	switch(etype) {
+	case ASN_BASIC_BIT_STRING:
+		OUT("if(st->size > 0) {\n");
+		OUT("\t/* Size in bits */\n");
+		OUT("\tsize = (st->size - 1) - (st->buf[0] & 0x7);\n");
+		OUT("} else {\n");
+		OUT("\tsize = 0;\n");
+		OUT("}\n");
+		break;
+	case ASN_STRING_UniversalString:
+		OUT("size = st->size >> 2;\t/* 4 byte per character */\n");
+		break;
+	case ASN_STRING_BMPString:
+		OUT("size = st->size >> 1;\t/* 2 byte per character */\n");
+		break;
+	case ASN_STRING_UTF8String:
+		OUT("size = UTF8String_length(st, td->name, app_errlog, app_key);\n");
+		OUT("if(size == (size_t)-1) return -1;\n");
+		break;
+	default:
+		if((etype & ASN_STRING_MASK)
+		|| etype == ASN_BASIC_OCTET_STRING) {
+			OUT("size = st->size;\n");
+			break;
+		} else {
+			WARNING("Size operation is not defined for %s",
+				ASN_EXPR_TYPE2STR(etype));
+			OUT("#warning Size operation not defined!\n");
+			OUT("size = st->size;\n");
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+emit_value_determination_code(arg_t *arg) {
+	asn1p_expr_type_e etype = _find_terminal_type(arg);
+
+	switch(etype) {
+	case ASN_BASIC_INTEGER:
+	case ASN_BASIC_ENUMERATED:
+		OUT("if(asn1_INTEGER2long(st, &value)) {\n");
+		INDENT(+1);
+		OUT("_ASN_ERRLOG(\"%%s: value too large\", td->name);\n");
+		OUT("return -1;\n");
+		INDENT(-1);
+		OUT("}\n");
+		break;
+	case ASN_BASIC_BOOLEAN:
+		OUT("value = st->value;\n");
+		break;
+	default:
+		WARNING("Value cannot be determined "
+			"for constraint check for %s at line %d\n",
+			arg->expr->Identifier, arg->expr->_lineno);
+		OUT("#error Value cannot be determined for %s at %d\n",
+			arg->expr->Identifier, arg->expr->_lineno);
+		break;
+	}
+
+	return 0;
+}
+
+static long compute_min_size(arg_t *arg) { return compute_xxx_size(arg, 0); }
+static long compute_max_size(arg_t *arg) { return compute_xxx_size(arg, 1); }
+
+static long compute_xxx_size(arg_t *arg, int _max) {
+	asn1p_expr_type_e etype;
+	long basic_max = 0x7fffffff;
+	long basic_min = 0x80000000;
+	long svalue = 0;
+
+	etype = _find_terminal_type(arg);
+	switch(etype) {
+	case ASN_BASIC_BIT_STRING:
+		svalue = _max?basic_max/8:0;
+		break;
+	case ASN_STRING_UTF8String:
+		svalue = _max?basic_max/6:0;
+		break;
+	case ASN_STRING_UniversalString:
+		svalue = _max?basic_max/4:0;
+		break;
+	case ASN_STRING_BMPString:
+		svalue = _max?basic_max/2:0;
+		break;
+	case ASN_BASIC_OCTET_STRING:
+		svalue = _max?basic_max:0;
+		break;
+	default:
+		if((etype & ASN_STRING_MASK)) {
+			svalue = _max?basic_max:0;
+			break;
+		}
+		svalue = _max?basic_max:basic_min;
+		break;
+	}
+
+	return svalue;
+}
+
+static asn1p_expr_type_e
+_find_terminal_type(arg_t *arg) {
+	asn1p_expr_t *expr;
+	expr = asn1f_find_terminal_type_ex(arg->asn, arg->mod, arg->expr, NULL);
+	assert(expr);
+	return expr->expr_type;
+}
diff --git a/libasn1compiler/asn1c_C.h b/libasn1compiler/asn1c_C.h
new file mode 100644
index 0000000..643d629
--- /dev/null
+++ b/libasn1compiler/asn1c_C.h
@@ -0,0 +1,69 @@
+#ifndef	ASN1_COMPILER_LANGUAGE_C_H
+#define	ASN1_COMPILER_LANGUAGE_C_H
+
+#include "asn1c_lang.h"
+
+int asn1c_lang_C_type_REFERENCE(arg_t *);
+int asn1c_lang_C_type_EXTENSIBLE(arg_t *);
+
+int asn1c_lang_C_type_SEQUENCE(arg_t *);
+int asn1c_lang_C_type_SEQUENCE_OF(arg_t *);
+int asn1c_lang_C_type_SET(arg_t *);
+int asn1c_lang_C_type_SET_OF(arg_t *);
+int asn1c_lang_C_type_CHOICE(arg_t *);
+
+int asn1c_lang_C_type_INTEGER(arg_t *);
+int asn1c_lang_C_type_ENUMERATED(arg_t *);
+int asn1c_lang_C_type_SIMPLE_TYPE(arg_t *);
+
+static asn1_language_map_t asn1_lang_C[] __attribute__ ((unused)) = {
+	{ AMT_TYPE, A1TC_REFERENCE,	asn1c_lang_C_type_REFERENCE },
+	{ AMT_TYPEREF, A1TC_REFERENCE,	asn1c_lang_C_type_REFERENCE },
+	{ AMT_TYPE, A1TC_EXTENSIBLE,	asn1c_lang_C_type_EXTENSIBLE },
+	/*
+	 * Constructed types
+	 */
+	{ AMT_TYPE, ASN_CONSTR_SEQUENCE,	asn1c_lang_C_type_SEQUENCE },
+	{ AMT_TYPE, ASN_CONSTR_SEQUENCE_OF,	asn1c_lang_C_type_SEQUENCE_OF },
+	{ AMT_TYPEREF, ASN_CONSTR_SEQUENCE_OF,	asn1c_lang_C_type_SEQUENCE_OF },
+	{ AMT_TYPE, ASN_CONSTR_SET,		asn1c_lang_C_type_SET },
+	{ AMT_TYPE, ASN_CONSTR_SET_OF,		asn1c_lang_C_type_SET_OF },
+	{ AMT_TYPEREF, ASN_CONSTR_SET_OF,	asn1c_lang_C_type_SET_OF },
+	{ AMT_TYPE, ASN_CONSTR_CHOICE,		asn1c_lang_C_type_CHOICE },
+	/*
+	 * Basic types
+	 */
+	{ AMT_TYPE, ASN_BASIC_BOOLEAN,	asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_NULL,	asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_INTEGER,	asn1c_lang_C_type_INTEGER },
+	/* [Skipped REAL] */
+	{ AMT_TYPE, ASN_BASIC_ENUMERATED,	asn1c_lang_C_type_ENUMERATED },
+	{ AMT_TYPE, ASN_BASIC_BIT_STRING,	asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_OCTET_STRING,	asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_OBJECT_IDENTIFIER,asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_RELATIVE_OID,	asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_CHARACTER_STRING,	asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_UTCTime,		asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_BASIC_GeneralizedTime,	asn1c_lang_C_type_SIMPLE_TYPE },
+	/*
+	 * String types
+	 */
+	{ AMT_TYPE, ASN_STRING_BMPString,     asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_GeneralString, asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_GraphicString, asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_IA5String,     asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_ISO646String,  asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_NumericString, asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_PrintableString,asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_TeletexString, asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_T61String,     asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_UniversalString,asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_UTF8String,    asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_VideotexString,asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_VisibleString, asn1c_lang_C_type_SIMPLE_TYPE },
+	{ AMT_TYPE, ASN_STRING_ObjectDescriptor,asn1c_lang_C_type_SIMPLE_TYPE },
+	{ 0, 0, 0 }
+};
+
+
+#endif	/* ASN1_COMPILER_LANGUAGE_C_H */
diff --git a/libasn1compiler/asn1c_internal.h b/libasn1compiler/asn1c_internal.h
new file mode 100644
index 0000000..91772cd
--- /dev/null
+++ b/libasn1compiler/asn1c_internal.h
@@ -0,0 +1,81 @@
+#ifndef	_ASN1_COMPILER_INTERNAL_H_
+#define	_ASN1_COMPILER_INTERNAL_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>		/* for fstat(2) */
+#include <unistd.h>		/* for unlink(2) */
+#include <fcntl.h>		/* for open(2) */
+#include <glob.h>		/* for glob(3) */
+#include <libgen.h>		/* for basename(3) */
+#include <string.h>
+#include <ctype.h>		/* for isalnum(3) */
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "asn1compiler.h"
+
+typedef struct arg_s {
+
+	enum asn1c_flags flags;
+
+	void (*logger_cb)(int _severity, const char *fmt, ...);
+
+	int (*default_cb)(struct arg_s *);
+
+	struct compiler_streams *target;
+
+	asn1p_t		*asn;
+	asn1p_module_t	*mod;
+	asn1p_expr_t	*expr;
+
+	int indent_level;
+	int indented;
+	int embed;
+} arg_t;
+
+#include "asn1c_lang.h"		/* Target language initialization */
+#include "asn1c_misc.h"		/* Miscellaneous functions */
+#include "asn1c_out.h"		/* Handle output during compilation */
+#include "asn1c_save.h"		/* Save compiled output */
+
+#define	INDENT(val)		arg->indent_level += (val)
+#define	INDENTED(code)	do {			\
+		INDENT(+1);			\
+		do { code; } while(0);		\
+		INDENT(-1);			\
+	} while(0)
+#define	FLAT(code)	do {			\
+		int _il = arg->indent_level;	\
+		arg->indent_level = 0;		\
+		do { code; } while(0);		\
+		arg->indent_level = _il;	\
+	} while(0)
+#define	EMBED(ev)	do {			\
+		REDIR(OT_TYPE_DECLS);		\
+		arg->embed++;			\
+		INDENTED(arg_t _tmp = *arg;	\
+			_tmp.expr = ev;		\
+			_tmp.default_cb(&_tmp);	\
+		);				\
+		arg->embed--;			\
+	} while(0)
+#define	OUT(fmt, args...)	asn1c_compiled_output(arg, fmt, ##args)
+
+#define	REDIR(foo)	do { arg->target->target = foo; } while(0)
+
+/*
+ * Logging.
+ */
+#define	LOG(ll, fmt, args...)	do {			\
+		arg->logger_cb(ll, fmt, ##args);	\
+	} while(0)
+#define	DEBUG(fmt, args...)	do {		\
+		if(arg->flags & A1C_DEBUG)	\
+			LOG(-1, fmt, ##args);	\
+	} while(0)
+#define	WARNING(fmt, args...)	LOG(0, fmt, ##args);
+#define	FATAL(fmt, args...)	LOG(1, fmt, ##args);
+
+#endif	/* _ASN1_COMPILER_INTERNAL_H_ */
diff --git a/libasn1compiler/asn1c_lang.c b/libasn1compiler/asn1c_lang.c
new file mode 100644
index 0000000..fa07540
--- /dev/null
+++ b/libasn1compiler/asn1c_lang.c
@@ -0,0 +1,32 @@
+#include "asn1c_internal.h"
+#include "asn1c_C.h"
+
+asn1_language_map_t asn1_lang_map[AMT_EXPR_META_MAX][ASN_EXPR_TYPE_MAX];
+
+int
+asn1c_with_language(asn1c_target_language_e lang) {
+	asn1_language_map_t *lptr;
+	int lsize;
+	if(lang != ASN1C_LANGUAGE_C) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	lptr = asn1_lang_C;
+	lsize = sizeof(asn1_lang_C)/sizeof(asn1_lang_C[0]);
+
+	memset(asn1_lang_map, 0, sizeof(asn1_lang_map));
+
+	for(; lsize && lptr->expr_match; lsize--, lptr++) {
+		assert(lptr->meta_match > 0);
+		assert(lptr->meta_match < AMT_EXPR_META_MAX);
+		assert(lptr->expr_match > 0);
+		assert(lptr->expr_match < ASN_EXPR_TYPE_MAX);
+
+		asn1_lang_map[lptr->meta_match][lptr->expr_match]
+			= *lptr;
+	}
+
+	return 0;
+}
+
diff --git a/libasn1compiler/asn1c_lang.h b/libasn1compiler/asn1c_lang.h
new file mode 100644
index 0000000..8c9a6ea
--- /dev/null
+++ b/libasn1compiler/asn1c_lang.h
@@ -0,0 +1,28 @@
+#ifndef	ASN1_COMPILER_LANGUAGE_H
+#define	ASN1_COMPILER_LANGUAGE_H
+
+struct arg_s;
+
+typedef struct asn1_language_map_s {
+	asn1p_expr_meta_e meta_match;		/* meta_type */
+	asn1p_expr_type_e expr_match;		/* expr_type */
+	/*
+	 * A callback that would create a language-specific type declaration.
+	 */
+	int (*type_cb)(struct arg_s *arg);
+} asn1_language_map_t;
+
+
+extern asn1_language_map_t asn1_lang_map[AMT_EXPR_META_MAX][ASN_EXPR_TYPE_MAX];
+
+
+typedef enum asn1c_target_language {
+	ASN1C_LANGUAGE_C,
+} asn1c_target_language_e;
+
+/*
+ * Initialize the compiler to generate specified target language.
+ */
+int asn1c_with_language(asn1c_target_language_e lang);
+
+#endif	/* ASN1_COMPILER_LANGUAGE_H */
diff --git a/libasn1compiler/asn1c_misc.c b/libasn1compiler/asn1c_misc.c
new file mode 100644
index 0000000..b464b64
--- /dev/null
+++ b/libasn1compiler/asn1c_misc.c
@@ -0,0 +1,232 @@
+#include "asn1c_internal.h"
+#include <asn1fix_export.h>
+
+#ifndef	DEFFILEMODE	/* Normally in <sys/stat.h> */
+#define	DEFFILEMODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#endif
+
+/*
+ * Construct identifier from multiple parts.
+ * Convert unsafe characters to underscores.
+ */
+char *
+asn1c_make_identifier(int unsafe_only_spaces, char *arg1, ...) {
+	static char *storage;
+	static int storage_size;
+	int nodelimiter = 0;
+	va_list ap;
+	char *str;
+	int size;
+	char *p;
+
+	if(arg1 == NULL)
+		return NULL;
+
+	/*
+	 * Estimate the necessary storage size
+	 */
+	size = strlen(arg1);
+	va_start(ap, arg1);
+	while((str = va_arg(ap, char *)))
+		size += 1 + strlen(str);
+	va_end(ap);
+
+	/*
+	 * Make sure we have this amount of storage.
+	 */
+	if(storage_size <= size) {
+		if(storage) free(storage);
+		storage = malloc(size + 1);
+		if(storage) {
+			storage_size = size;
+		} else {
+			storage_size = 0;
+			return NULL;
+		}
+	}
+
+	/*
+	 * Fill-in the storage.
+	 */
+	va_start(ap, arg1);
+	str = arg1;
+	p = storage;
+	for(str = arg1; str; str = va_arg(ap, char *)) {
+		int subst_made = 0;
+
+		if(str[0] == ' ' && str[1] == '\0') {
+			*p++ = ' ';
+			nodelimiter = 1;	/* No delimiter */
+			continue;
+		}
+
+		if(str != arg1 && !nodelimiter)
+			*p++ = '_';	/* Delimiter between tokens */
+		nodelimiter = 0;
+
+		for(; *str; str++) {
+			if(isalnum(*str)) {
+				*p++ = *str;
+				subst_made = 0;
+			} else if(!subst_made++) {
+				if(unsafe_only_spaces && !isspace(*str)) {
+					*p ++ = *str;
+				} else {
+					*p++ = '_';
+				}
+			}
+		}
+	}
+	va_end(ap);
+	*p = '\0';
+
+	assert((p - storage) <= storage_size);
+
+	return storage;
+}
+
+FILE *
+asn1c_open_file(arg_t *arg, const char *name, const char *ext) {
+	int created = 1;
+	struct stat sb;
+	char *fname;
+	int len;
+	FILE *fp;
+	int fd;
+
+	/*
+	 * Compute filenames.
+	 */
+	len = strlen(name) + strlen(ext) + 1;
+	fname = alloca(len);
+	snprintf(fname, len, "%s%s", name, ext);
+
+	/*
+	 * Create files.
+	 */
+	fd = open(fname, O_CREAT | O_EXCL | O_WRONLY, DEFFILEMODE);
+	if(fd == -1 && errno == EEXIST) {
+		fd = open(fname, O_WRONLY, DEFFILEMODE);
+		created = 0;
+	}
+	if(fd == -1) {
+		perror(fname);
+		return NULL;
+	}
+
+	/*
+	 * Check sanity.
+	 */
+	if(fstat(fd, &sb) || !S_ISREG(sb.st_mode)) {
+		fprintf(stderr, "%s: Not a regular file\n", fname);
+		if(created) unlink(fname);
+		close(fd);
+		return NULL;
+	}
+
+	(void)ftruncate(fd, 0);
+
+	/*
+	 * Convert file descriptor into file pointer.
+	 */
+	fp = fdopen(fd, "w");
+	if(fp == NULL) {
+		if(created) unlink(fname);
+		close(fd);
+	}
+	return fp;
+}
+
+char *
+asn1c_type_name(arg_t *arg, asn1p_expr_t *expr, enum tnfmt _format) {
+	char *typename;
+
+	switch(expr->expr_type) {
+	case A1TC_REFERENCE:
+		typename = expr->reference->components[
+			expr->reference->comp_count-1].name;
+		if(typename[0] == '&') {
+			arg_t tmp = *arg;
+
+			/*
+			 * This is a reference to a type defined in a class.
+			 * Resolve it and use instead.
+			 */
+			tmp.expr = asn1f_class_access_ex(arg->asn, arg->mod,
+				arg->expr, expr->reference, &tmp.mod);
+			if(tmp.expr) return NULL;
+
+			return asn1c_type_name(&tmp, tmp.expr, _format);
+		} else if(_format == TNF_RSAFE) {
+			/*
+			 * The recursion-safe format is requested.
+			 * The problem here is that only constructed types
+			 * might be referenced with "struct".
+			 * Change RSAFE to CTYPE if the terminal type
+			 * is primitive.
+			 */
+			asn1p_expr_t *terminal;
+			terminal = asn1f_find_terminal_type_ex(
+				arg->asn, arg->mod, arg->expr, NULL);
+			if(terminal
+			&& (terminal->expr_type
+				& (ASN_BASIC_MASK | ASN_STRING_MASK)))
+				_format = TNF_CTYPE;
+		}
+		break;
+	case ASN_CONSTR_SEQUENCE_OF:
+	case ASN_CONSTR_SET_OF:
+		if(expr->Identifier) {
+			typename = expr->Identifier;
+		} else {
+			asn1p_expr_t *child;
+			child = TQ_FIRST(&(expr->members));
+			typename = asn1c_type_name(arg, child, _format);
+			if(typename)
+				return typename;
+			_format = TNF_SAFE;
+			typename = child->Identifier;
+		}
+		break;
+	case ASN_BASIC_INTEGER:
+	case ASN_BASIC_ENUMERATED:
+		if((arg->flags & A1C_USE_NATIVE_INTEGERS)) {
+			switch(_format) {
+			case TNF_CTYPE:
+			case TNF_RSAFE:
+				return "int";
+			default:
+				if(expr->expr_type == ASN_BASIC_INTEGER)
+					return "NativeInteger";
+				else
+					return "NativeEnumerated";
+			}
+		}
+		/* Fall through */
+	default:
+		if(expr->expr_type & (ASN_BASIC_MASK | ASN_STRING_MASK)) {
+			if(_format == TNF_RSAFE)
+				_format = TNF_CTYPE;
+			typename = ASN_EXPR_TYPE2STR(expr->expr_type);
+		} else {
+			_format = TNF_SAFE;
+			typename = expr->Identifier;
+		}
+	}
+
+	switch(_format) {
+	case TNF_UNMODIFIED:
+	case TNF_INCLUDE:
+		return asn1c_make_identifier(1, typename, 0);
+	case TNF_SAFE:
+		return asn1c_make_identifier(0, typename, 0);
+	case TNF_CTYPE:
+		return asn1c_make_identifier(0, typename, "t", 0);
+	case TNF_RSAFE:
+		return asn1c_make_identifier(0, "struct", " ", typename, 0);
+	}
+
+	assert("!unreachable");
+	return typename;
+}
+
diff --git a/libasn1compiler/asn1c_misc.h b/libasn1compiler/asn1c_misc.h
new file mode 100644
index 0000000..2f82b0b
--- /dev/null
+++ b/libasn1compiler/asn1c_misc.h
@@ -0,0 +1,28 @@
+#ifndef	_ASN1_COMPILER_MISC_H_
+#define	_ASN1_COMPILER_MISC_H_
+
+/*
+ * Make the target language identifier out of one or more names.
+ * The function will concatenate the names and replace unsafe characters
+ * with safe ones.
+ */
+char *asn1c_make_identifier(int unsafe_only_spaces, char *arg1, ...);
+
+/*
+ * Return the type name of the specified expression.
+ */
+enum tnfmt {
+	TNF_UNMODIFIED,		/* Return unmodified type name */
+	TNF_INCLUDE,		/* Format for #include <> */
+	TNF_CTYPE,		/* Format as normal C-ish type (append "_t") */
+	TNF_SAFE,		/* Replace unsafe characters with _ */
+	TNF_RSAFE,		/* Recursion-safe C type format */
+};
+char *asn1c_type_name(arg_t *arg, asn1p_expr_t *expr, enum tnfmt _format);
+
+/*
+ * Open the arbitrary file by its base name and extension.
+ */
+FILE *asn1c_open_file(arg_t *arg, const char *base_part, const char *extension);
+
+#endif	/* _ASN1_COMPILER_MISC_H_ */
diff --git a/libasn1compiler/asn1c_out.c b/libasn1compiler/asn1c_out.c
new file mode 100644
index 0000000..a643241
--- /dev/null
+++ b/libasn1compiler/asn1c_out.c
@@ -0,0 +1,75 @@
+#include "asn1c_internal.h"
+
+/*
+ * Add an elementary chunk of target language text
+ * into appropriate output stream.
+ */
+int
+asn1c_compiled_output(arg_t *arg, const char *fmt, ...) {
+	const char *p;
+	int lf_found;
+	va_list ap;
+	out_chunk_t *m;
+	char *buf;
+	int ret;
+
+	/*
+	 * Make sure the output has a single LF and only at the end.
+	 */
+	for(lf_found = 0, p = fmt; *p; p++) {
+		if(*p == '\n') {
+			lf_found++;
+			assert(p[1] == '\0');
+		}
+	}
+	assert(lf_found <= 1);
+
+	/*
+	 * Print out the indentation.
+	 */
+	if(arg->indented == 0) {
+		int i = arg->indent_level;
+		arg->indented = 1;
+		while(i--) {
+			ret = asn1c_compiled_output(arg, "\t");
+			if(ret == -1) return -1;
+		}
+	}
+
+	/*
+	 * Estimate necessary size.
+	 */
+	buf = "";
+	va_start(ap, fmt);
+	ret = vsnprintf(buf, 0, fmt, ap);
+	va_end(ap);
+	assert(ret >= 0);
+
+	/*
+	 * Allocate buffer.
+	 */
+	m = calloc(1, sizeof(out_chunk_t));
+	if(m == NULL) return -1;
+	m->len = ret + 1;
+	m->buf = malloc(ret + 1);
+	if(m->buf == NULL) {
+		free(m);
+		return -1;
+	}
+
+	/*
+	 * Fill the buffer.
+	 */
+	va_start(ap, fmt);
+	ret = vsnprintf(m->buf, m->len, fmt, ap);
+	assert(ret < m->len);
+	m->len = ret;
+	va_end(ap);
+
+	TQ_ADD(&(arg->target->targets[arg->target->target]), m, next);
+
+	if(lf_found)
+		arg->indented = 0;
+
+	return 0;
+}
diff --git a/libasn1compiler/asn1c_out.h b/libasn1compiler/asn1c_out.h
new file mode 100644
index 0000000..2267d5d
--- /dev/null
+++ b/libasn1compiler/asn1c_out.h
@@ -0,0 +1,31 @@
+#ifndef	_ASN1_COMPILED_OUTPUT_H_
+#define	_ASN1_COMPILED_OUTPUT_H_
+
+/*
+ * An elementary chunk of target language text.
+ */
+typedef struct out_chunk {
+	char *buf;
+	int len;
+
+	TQ_ENTRY(struct out_chunk) next;
+} out_chunk_t;
+
+typedef struct compiler_streams {
+	enum {
+		OT_DEPS,	/* Dependencies */
+		OT_TYPE_DECLS,	/* Type declarations */
+		OT_FUNC_DECLS,	/* Function declarations */
+		OT_STAT_DEFS,	/* Static definitions */
+		OT_CODE,	/* Some code */
+		OT_MAX
+	} target;
+	TQ_HEAD(out_chunk_t) targets[OT_MAX];
+} compiler_streams_t;
+
+static char *_compiler_stream2str[] __attribute__ ((unused))
+	= { "DEPS", "TYPE-DECLS", "FUNC-DECLS", "STAT-DEFS", "CODE" };
+
+int asn1c_compiled_output(arg_t *arg, const char *fmt, ...);
+
+#endif	/* _ASN1_COMPILED_OUTPUT_H_ */
diff --git a/libasn1compiler/asn1c_save.c b/libasn1compiler/asn1c_save.c
new file mode 100644
index 0000000..0e5b0fb
--- /dev/null
+++ b/libasn1compiler/asn1c_save.c
@@ -0,0 +1,225 @@
+#include "asn1c_internal.h"
+
+static int asn1c_dump_streams(arg_t *arg);
+static int asn1c_print_streams(arg_t *arg);
+static int asn1c_save_streams(arg_t *arg);
+static int asn1c_copy_over(arg_t *arg, char *path);
+
+int
+asn1c_save_compiled_output(arg_t *arg, const char *datadir) {
+
+	(void)datadir;
+
+	TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+			if(asn1_lang_map[arg->expr->meta_type]
+				[arg->expr->expr_type].type_cb) {
+				if(asn1c_dump_streams(arg))
+					return -1;
+			}
+		}
+	}
+
+	/*
+	 * Dump out the Makefile template and the rest of the support code.
+	 */
+	if((arg->flags & A1C_PRINT_COMPILED) == 0
+	&& (arg->flags & A1C_OMIT_SUPPORT_CODE) == 0) {
+		glob_t pg;
+		FILE *mkf;
+		char *p;
+		int i;
+
+		i = strlen(datadir) + sizeof("/*.[ch]");
+		p = alloca(i);
+		snprintf(p, i, "%s/*.[ch]", datadir);
+
+		memset(&pg, 0, sizeof(pg));
+		if(glob(p, GLOB_ERR
+#ifdef	GLOB_TILDE
+			| GLOB_TILDE
+#endif	/* GLOB_TILDE */
+		, NULL, &pg)) {
+			fprintf(stderr,
+				"Bad skeletons directory (-S) %s: %s\n",
+				datadir, strerror(errno));
+			return -1;
+		}
+
+		mkf = asn1c_open_file(arg, "Makefile.am", ".sample");
+		if(mkf == NULL) {
+			globfree(&pg);
+			perror("Makefile.am.sample");
+			return -1;
+		}
+
+		fprintf(mkf, "ASN_SRCS=");
+		TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
+			TQ_FOR(arg->expr, &(arg->mod->members), next) {
+				if(asn1_lang_map[arg->expr->meta_type]
+					[arg->expr->expr_type].type_cb) {
+					fprintf(mkf, "\t\\\n\t%s.c %s.h",
+					arg->expr->Identifier,
+					arg->expr->Identifier);
+				}
+			}
+		}
+
+		for(i = 0; i < pg.gl_pathc; i++) {
+			if(asn1c_copy_over(arg, pg.gl_pathv[i])) {
+				fprintf(mkf, ">>>ABORTED<<<");
+				fclose(mkf);
+				globfree(&pg);
+				return -1;
+			} else {
+				fprintf(mkf, "\t\\\n\t%s",
+					basename(pg.gl_pathv[i]));
+			}
+		}
+
+		fprintf(mkf, "\n\n");
+		fprintf(mkf, "lib_LTLIBRARIES=libsomething.la\n");
+		fprintf(mkf, "libsomething_la_SOURCES=${ASN_SRCS}\n");
+		fclose(mkf);
+		fprintf(stderr, "Generated Makefile.am.sample\n");
+		globfree(&pg);
+	}
+
+	return 0;
+}
+
+/*
+ * Dump the streams.
+ */
+static int
+asn1c_dump_streams(arg_t *arg)  {
+	if(arg->flags & A1C_PRINT_COMPILED) {
+		return asn1c_print_streams(arg);
+	} else {
+		return asn1c_save_streams(arg);
+	}
+}
+
+static int
+asn1c_print_streams(arg_t *arg)  {
+	compiler_streams_t *cs = arg->expr->data;
+	asn1p_expr_t *expr = arg->expr;
+	int i;
+
+	for(i = 0; i < OT_MAX; i++) {
+		out_chunk_t *ot;
+		if(TQ_FIRST(&cs->targets[i]) == NULL)
+			continue;
+
+		printf("\n/*** <<< %s [%s] >>> ***/\n\n",
+			_compiler_stream2str[i],
+			expr->Identifier);
+
+		TQ_FOR(ot, &(cs->targets[i]), next) {
+			fwrite(ot->buf, ot->len, 1, stdout);
+		}
+	}
+
+	return 0;
+}
+
+static int
+asn1c_save_streams(arg_t *arg)  {
+	asn1p_expr_t *expr = arg->expr;
+	compiler_streams_t *cs = expr->data;
+	out_chunk_t *ot;
+	FILE *fp_c, *fp_h;
+	char *header_id;
+
+	if(cs == NULL) {
+		fprintf(stderr, "Cannot compile %s at line %d\n",
+			expr->Identifier, expr->_lineno);
+		return -1;
+	}
+
+	fp_c = asn1c_open_file(arg, expr->Identifier, ".c");
+	fp_h = asn1c_open_file(arg, expr->Identifier, ".h");
+	if(fp_c == NULL || fp_h == NULL) {
+		if(fp_c) fclose(fp_c);	/* lacks unlink() */
+		if(fp_h) fclose(fp_h);	/* lacks unlink() */
+		return -1;
+	}
+
+	header_id = alloca(strlen(expr->Identifier) + 1);
+	if(1) {
+		char *src, *dst;
+		for(src = expr->Identifier, dst = header_id;
+				(*dst=*src); src++, dst++)
+			if(!isalnum(*src)) *dst = '_';
+		*dst = '\0';
+	}
+
+	fprintf(fp_h,
+		"#ifndef\t_%s_H_\n"
+		"#define\t_%s_H_\n"
+		"\n", header_id, header_id);
+
+	fprintf(fp_h, "#include <constr_TYPE.h>\n\n");
+
+	TQ_FOR(ot, &(cs->targets[OT_DEPS]), next)
+		fwrite(ot->buf, ot->len, 1, fp_h);
+	fprintf(fp_h, "\n");
+	TQ_FOR(ot, &(cs->targets[OT_TYPE_DECLS]), next)
+		fwrite(ot->buf, ot->len, 1, fp_h);
+	fprintf(fp_h, "\n");
+	TQ_FOR(ot, &(cs->targets[OT_FUNC_DECLS]), next)
+		fwrite(ot->buf, ot->len, 1, fp_h);
+
+	fprintf(fp_c, "#include <%s.h>\n\n", expr->Identifier);
+	TQ_FOR(ot, &(cs->targets[OT_STAT_DEFS]), next)
+		fwrite(ot->buf, ot->len, 1, fp_c);
+	TQ_FOR(ot, &(cs->targets[OT_CODE]), next)
+		fwrite(ot->buf, ot->len, 1, fp_c);
+
+	assert(OT_MAX == 5);
+
+	fprintf(fp_h, "\n#endif\t/* _%s_H_ */\n", header_id);
+
+	fclose(fp_c);
+	fclose(fp_h);
+	fprintf(stderr, "Compiled %s.c\n", expr->Identifier);
+	fprintf(stderr, "Compiled %s.h\n", expr->Identifier);
+	return 0;
+}
+
+static int
+asn1c_copy_over(arg_t *arg, char *path) {
+	char *fname = basename(path);
+
+	if(symlink(path, fname)) {
+		if(errno == EEXIST) {
+			struct stat sb1, sb2;
+			if(stat(path, &sb1) == 0
+			&& stat(fname, &sb2) == 0
+			&& sb1.st_dev == sb2.st_dev
+			&& sb1.st_ino == sb2.st_ino) {
+				/*
+				 * Nothing to do.
+				 */
+				fprintf(stderr,
+					"File %s is already here as %s\n",
+					path, fname);
+				return 0;
+			} else {
+				fprintf(stderr,
+					"Retaining local %s (%s suggested)\n",
+					fname, path);
+				return 0;
+			}
+		} else {
+			fprintf(stderr, "Symlink %s -> %s failed: %s\n",
+				path, fname, strerror(errno));
+			return -1;
+		}
+	}
+
+	fprintf(stderr, "Symlinked %s\t-> %s\n", path, fname);
+
+	return 0;
+}
+
diff --git a/libasn1compiler/asn1c_save.h b/libasn1compiler/asn1c_save.h
new file mode 100644
index 0000000..487e625
--- /dev/null
+++ b/libasn1compiler/asn1c_save.h
@@ -0,0 +1,6 @@
+#ifndef	_ASN1_SAVE_H_
+#define	_ASN1_SAVE_H_
+
+int asn1c_save_compiled_output(arg_t *arg, const char *datadir);
+
+#endif	/* _ASN1_SAVE_H_ */
diff --git a/libasn1compiler/asn1compiler.c b/libasn1compiler/asn1compiler.c
new file mode 100644
index 0000000..d647c05
--- /dev/null
+++ b/libasn1compiler/asn1compiler.c
@@ -0,0 +1,160 @@
+#include "asn1c_internal.h"
+
+static void default_logger_cb(int, const char *fmt, ...);
+static int asn1c_compile_expr(arg_t *arg);
+static int asn1c_attach_streams(asn1p_expr_t *expr);
+
+int
+asn1_compile(asn1p_t *asn, const char *datadir, enum asn1c_flags flags) {
+	arg_t arg_s;
+	arg_t *arg = &arg_s;
+	int ret;
+
+	/*
+	 * Initialize target language.
+	 */
+	ret = asn1c_with_language(ASN1C_LANGUAGE_C);
+	assert(ret == 0);
+
+	memset(arg, 0, sizeof(*arg));
+	arg->default_cb = asn1c_compile_expr;
+	arg->logger_cb = default_logger_cb;
+	arg->flags = flags;
+	arg->asn = asn;
+
+	/*
+	 * Compile each individual top level structure.
+	 */
+	TQ_FOR(arg->mod, &(asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+			compiler_streams_t *cs = NULL;
+
+			if(asn1c_attach_streams(arg->expr))
+				return -1;
+
+			cs = arg->expr->data;
+			cs->target = OT_TYPE_DECLS;
+			arg->target = cs;
+
+			ret = asn1c_compile_expr(arg);
+			if(ret) {
+				FATAL("Cannot compile %s (%x:%x) at line %d",
+					arg->expr->Identifier,
+					arg->expr->expr_type,
+					arg->expr->meta_type,
+					arg->expr->_lineno);
+				return ret;
+			}
+		}
+	}
+
+	DEBUG("Saving compiled data");
+
+	/*
+	 * Save or print out the compiled result.
+	 */
+	if(asn1c_save_compiled_output(arg, datadir))
+		return -1;
+
+	return 0;
+}
+
+static int
+asn1c_compile_expr(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	int (*type_cb)(arg_t *);
+	int ret;
+
+	assert(expr->meta_type >= AMT_INVALID);
+	assert(expr->meta_type < AMT_EXPR_META_MAX);
+	assert(expr->expr_type >= A1TC_INVALID);
+	assert(expr->expr_type < ASN_EXPR_TYPE_MAX);
+
+	type_cb = asn1_lang_map[expr->meta_type][expr->expr_type].type_cb;
+	if(type_cb) {
+
+		if(arg->indent_level == 0)
+			OUT("\n");
+
+		DEBUG("Compiling %s at line %d",
+			expr->Identifier,
+			expr->_lineno);
+
+		ret = type_cb(arg);
+	} else {
+		ret = -1;
+		/*
+		 * Even if the target language compiler does not know
+		 * how to compile the given expression, we know that
+		 * certain expressions need not to be compiled at all.
+		 */
+		switch(expr->meta_type) {
+		case AMT_PARAMTYPE:
+		case AMT_OBJECT:
+		case AMT_OBJECTSET:
+		case AMT_VALUE:
+		case AMT_VALUESET:
+			ret = 0;
+			break;
+		default:
+			break;
+		}
+
+		switch(expr->expr_type) {
+		case A1TC_TYPEID:
+			ret = 0;	/* TYPE-IDENTIFIER is a CLASS */
+		default:
+			break;
+		}
+	}
+
+	if(ret == -1) {
+		OUT("#error Cannot compile \"%s\" (%x/%x) at line %d\n",
+			arg->expr->Identifier,
+			arg->expr->meta_type,
+			arg->expr->expr_type,
+			arg->expr->_lineno
+		);
+	}
+
+	return ret;
+}
+
+static int
+asn1c_attach_streams(asn1p_expr_t *expr) {
+	compiler_streams_t *cs;
+	int i;
+
+	if(expr->data)
+		return 0;	/* Already attached? */
+
+	expr->data = calloc(1, sizeof(compiler_streams_t));
+	if(expr->data == NULL)
+		return -1;
+
+	cs = expr->data;
+	for(i = 0; i < OT_MAX; i++) {
+		TQ_INIT(&(cs->targets[i]));
+	}
+
+	return 0;
+}
+
+static void
+default_logger_cb(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");
+}
+
diff --git a/libasn1compiler/asn1compiler.h b/libasn1compiler/asn1compiler.h
new file mode 100644
index 0000000..d104dc9
--- /dev/null
+++ b/libasn1compiler/asn1compiler.h
@@ -0,0 +1,41 @@
+#ifndef	ASN1_COMPILER_H
+#define	ASN1_COMPILER_H
+
+#include <asn1parser.h>
+
+enum asn1c_flags {
+	A1C_NOFLAGS,
+	/*
+	 * Debug the compiler.
+	 */
+	A1C_DEBUG		= 0x0001,
+	/*
+	 * Do not split the target output in several files, just print it.
+	 * (Note: the output is not likely to be compilable in this case).
+	 */
+	A1C_PRINT_COMPILED	= 0x0002,
+	/*
+	 * Generate only the tables for ASN.1 types,
+	 * do not emit ASN.1 parsing support code.
+	 */
+	A1C_OMIT_SUPPORT_CODE	= 0x0004,
+	/*
+	 * Use native integers instead of INTEGER_t and ENUMERATED_t types.
+	 */
+	A1C_USE_NATIVE_INTEGERS	= 0x0008,
+	/*
+	 * Do not use C99 extensions.
+	 */
+	A1C_NO_C99		= 0x0010,
+	/*
+	 * Enable use of unnamed unions (non-portable feature).
+	 */
+	A1C_UNNAMED_UNIONS	= 0x0020,
+};
+
+/*
+ * Compile the ASN.1 specification.
+ */
+int asn1_compile(asn1p_t *asn, const char *datadir, enum asn1c_flags);
+
+#endif	/* ASN1_COMPILER_H */
diff --git a/libasn1compiler/check_compiler.c b/libasn1compiler/check_compiler.c
new file mode 100644
index 0000000..1da6902
--- /dev/null
+++ b/libasn1compiler/check_compiler.c
@@ -0,0 +1,7 @@
+#include "asn1compiler.h"
+
+int
+main(int ac, char **av) {
+	return 0;
+}
+