support for a class of circular references


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@749 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
index 41ad21e..b8a7428 100644
--- a/libasn1compiler/asn1c_C.c
+++ b/libasn1compiler/asn1c_C.c
@@ -35,6 +35,7 @@
 static int expr_elements_count(arg_t *arg, asn1p_expr_t *expr);
 static int emit_member_table(arg_t *arg, asn1p_expr_t *expr);
 static int emit_tag2member_map(arg_t *arg, tag2el_t *tag2el, int tag2el_count, const char *opt_modifier);
+static int emit_include_dependencies(arg_t *arg);
 static int out_name_chain(arg_t *arg, int check_reserved_keywords);
 
 enum tvm_compat {
@@ -59,16 +60,9 @@
 	OUT("/* Context for parsing across buffer boundaries */\n");	\
 	OUT("asn_struct_ctx_t _asn_ctx;\n"));
 
+
 #define	DEPENDENCIES	do {						\
-	asn1p_expr_t *__m;						\
-	TQ_FOR(__m, &(expr->members), next) {				\
-		if((!(__m->expr_type & ASN_CONSTR_MASK)			\
-		&& __m->expr_type > ASN_CONSTR_MASK)			\
-		|| __m->meta_type == AMT_TYPEREF) {			\
-			GEN_INCLUDE(asn1c_type_name(arg,		\
-				__m, TNF_INCLUDE));	\
-		}							\
-	}								\
+	emit_include_dependencies(arg);					\
 	if(expr->expr_type == ASN_CONSTR_SET_OF)			\
 		GEN_INCLUDE("asn_SET_OF");				\
 	if(expr->expr_type == ASN_CONSTR_SEQUENCE_OF)			\
@@ -81,7 +75,6 @@
 
 int
 asn1c_lang_C_type_REAL(arg_t *arg) {
-	REDIR(OT_DEPS);
 	return asn1c_lang_C_type_SIMPLE_TYPE(arg);
 }
 
@@ -245,7 +238,7 @@
 	}
 
 	PCTX_DEF;
-	OUT("} %s%s%s", expr->marker.flags?"*":"",
+	OUT("} %s%s%s", (expr->marker.flags & EM_INDIRECT)?"*":"",
 		expr->_anonymous_type ? "" : MKID(expr->Identifier),
 		arg->embed ? "" : "_t");
 
@@ -406,7 +399,7 @@
 	);
 
 	PCTX_DEF;
-	OUT("} %s%s%s", expr->marker.flags?"*":"",
+	OUT("} %s%s%s", (expr->marker.flags & EM_INDIRECT)?"*":"",
 		expr->_anonymous_type ? "" : MKID(expr->Identifier),
 		arg->embed ? "" : "_t");
 
@@ -567,7 +560,7 @@
 int
 asn1c_lang_C_type_SEx_OF(arg_t *arg) {
 	asn1p_expr_t *expr = arg->expr;
-	asn1p_expr_t *memb;
+	asn1p_expr_t *memb = TQ_FIRST(&expr->members);
 
 	DEPENDENCIES;
 
@@ -579,8 +572,6 @@
 		OUT("typedef struct %s {\n", MKID(expr->Identifier));
 	}
 
-	memb = TQ_FIRST(&expr->members);
-
 	INDENT(+1);
 	OUT("A_%s_OF(",
 		(arg->expr->expr_type == ASN_CONSTR_SET_OF)
@@ -611,13 +602,13 @@
 		arg->embed--;
 		assert(arg->target->target == OT_TYPE_DECLS);
 	} else {
-		OUT("%s", asn1c_type_name(arg, memb, TNF_CTYPE | TNF_CHECK));
+		OUT("%s", asn1c_type_name(arg, memb, TNF_RSAFE));
 	}
 	OUT(") list;\n");
 	INDENT(-1);
 
 	PCTX_DEF;
-	OUT("} %s%s%s", expr->marker.flags?"*":"",
+	OUT("} %s%s%s", (expr->marker.flags & EM_INDIRECT)?"*":"",
 		expr->_anonymous_type ? "" : MKID(expr->Identifier),
 		arg->embed ? "" : "_t");
 
@@ -752,7 +743,7 @@
 	);
 
 	PCTX_DEF;
-	OUT("} %s%s%s", expr->marker.flags?"*":"",
+	OUT("} %s%s%s", (expr->marker.flags & EM_INDIRECT)?"*":"",
 		expr->_anonymous_type ? "" : MKID(expr->Identifier),
 		arg->embed ? "" : "_t");
 
@@ -918,24 +909,23 @@
 		 * refer it using "struct X" convention,
 		 * as it may recursively include the current structure.
 		 */
-		if(expr->marker.flags) {
+		if(expr->marker.flags & (EM_INDIRECT | EM_UNRECURSE)) {
 			asn1p_expr_t *terminal;
 			terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
 			if(terminal
 			&& (terminal->expr_type & ASN_CONSTR_MASK)) {
-				REDIR(OT_DEPS);
-			 	tnfmt = TNF_RSAFE;
-				OUT("\n");
+				tnfmt = TNF_RSAFE;
+				REDIR(OT_FWD_DECLS);
 				OUT("%s;\t/* Forward declaration */\n",
-					asn1c_type_name(arg, arg->expr, tnfmt | TNF_CHECK));
+					asn1c_type_name(arg, arg->expr, tnfmt));
 			}
 		}
 
 		REDIR(OT_TYPE_DECLS);
 
-		OUT("%s", asn1c_type_name(arg, arg->expr, tnfmt | TNF_CHECK));
+		OUT("%s", asn1c_type_name(arg, arg->expr, tnfmt));
 		if(!expr->_anonymous_type) {
-			OUT("%s", expr->marker.flags?"\t*":"\t ");
+			OUT("%s", (expr->marker.flags&EM_INDIRECT)?"\t*":"\t ");
 			OUT("%s", MKID(expr->Identifier));
 			if((expr->marker.flags & EM_DEFAULT) == EM_DEFAULT)
 				OUT("\t/* DEFAULT %s */",
@@ -951,11 +941,10 @@
 		REDIR(OT_TYPE_DECLS);
 
 		OUT("typedef %s\t",
-			asn1c_type_name(arg, arg->expr, TNF_CTYPE | TNF_CHECK));
+			asn1c_type_name(arg, arg->expr, TNF_CTYPE));
 		OUT("%s%s_t",
-			expr->marker.flags?"*":" ",
-			MKID(expr->Identifier));
-		OUT_DEBUG("\t/* %s:%d */", __FILE__, __LINE__);
+			(expr->marker.flags & EM_INDIRECT)?"*":" ",
+			MKID_nc(expr->Identifier));
 	}
 
 	if((expr->expr_type == ASN_BASIC_ENUMERATED)
@@ -1557,6 +1546,47 @@
 }
 
 static int
+emit_include_dependencies(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *memb;
+
+	TQ_FOR(memb, &(expr->members), next) {
+
+		if((memb->meta_type == AMT_TYPEREF
+		&& (memb->marker.flags & EM_INDIRECT))
+		|| expr->expr_type == ASN_CONSTR_SET_OF
+		|| expr->expr_type == ASN_CONSTR_SEQUENCE_OF
+		) {
+			asn1p_expr_t *terminal;
+			terminal = asn1f_find_terminal_type_ex(arg->asn, memb);
+			if(terminal && !terminal->parent_expr
+				&& (terminal->expr_type & ASN_CONSTR_MASK)) {
+				int saved_target = arg->target->target;
+				REDIR(OT_FWD_DECLS);
+				OUT("%s;\t/* Forward declaration */\n",
+					asn1c_type_name(arg, memb, TNF_RSAFE));
+				REDIR(saved_target);
+				memb->marker.flags |= EM_UNRECURSE;
+			}
+		}
+
+		if((!(memb->expr_type & ASN_CONSTR_MASK)
+			&& memb->expr_type > ASN_CONSTR_MASK)
+		|| memb->meta_type == AMT_TYPEREF) {
+			if(memb->marker.flags & EM_UNRECURSE) {
+				GEN_POSTINCLUDE(asn1c_type_name(arg,
+					memb, TNF_INCLUDE));
+			} else {
+				GEN_INCLUDE(asn1c_type_name(arg,
+					memb, TNF_INCLUDE));
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int
 emit_member_table(arg_t *arg, asn1p_expr_t *expr) {
 	int save_target;
 	arg_t tmp_arg;
@@ -1576,7 +1606,8 @@
 
 	if(outmost_tag && outmost_tag->tag_value == -1)
 		OUT("ATF_OPEN_TYPE | ");
-	OUT("%s, ", expr->marker.flags?"ATF_POINTER":"ATF_NOFLAGS");
+	OUT("%s, ",
+		(expr->marker.flags & EM_INDIRECT)?"ATF_POINTER":"ATF_NOFLAGS");
 	if((expr->marker.flags & EM_OPTIONAL) == EM_OPTIONAL) {
 		asn1p_expr_t *tv;
 		int opts = 0;
diff --git a/libasn1compiler/asn1c_misc.c b/libasn1compiler/asn1c_misc.c
index 94e4a63..8c5bb62 100644
--- a/libasn1compiler/asn1c_misc.c
+++ b/libasn1compiler/asn1c_misc.c
@@ -124,9 +124,6 @@
 asn1c_type_name(arg_t *arg, asn1p_expr_t *expr, enum tnfmt _format) {
 	asn1p_expr_t *top_parent;
 	char *typename;
-	enum ami_flags_e ami_flags = (_format & TNF_CHECK)
-		? AMI_CHECK_RESERVED : 0;
-	_format &= ~TNF_CHECK;
 
 	/* Rewind to the topmost parent expression */
 	if((top_parent = expr->parent_expr))
@@ -152,10 +149,18 @@
 			return asn1c_type_name(&tmp, tmp.expr, _format);
 		}
 
+		if(_format == TNF_RSAFE) {
+			asn1p_expr_t *terminal;
+			terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
+			if(terminal && terminal->expr_type & ASN_CONSTR_MASK) {
+				typename = terminal->Identifier;
+			}
+		}
+
 		if(_format == TNF_CTYPE) {
 			/*
 			 * If the component references the type itself,
-			 * switch to a recursion safe type representation
+			 * switch to a recursion-safe type naming
 			 * ("struct foo" instead of "foo_t").
 			 */
 			asn1p_expr_t *terminal;
@@ -220,19 +225,14 @@
 	switch(_format) {
 	case TNF_UNMODIFIED:
 	case TNF_INCLUDE:
-		assert(ami_flags == 0);	/* (TNF_INCLUDE | TNF_CHECK)?! */
-		ami_flags |= AMI_MASK_ONLY_SPACES;
-		return asn1c_make_identifier(ami_flags, typename, 0);
+		return asn1c_make_identifier(AMI_MASK_ONLY_SPACES, typename, 0);
 	case TNF_SAFE:
-		return asn1c_make_identifier(ami_flags, typename, 0);
-	case TNF_CTYPE:
-		return asn1c_make_identifier(ami_flags, typename, "t", 0);
-	case TNF_RSAFE:
-		return asn1c_make_identifier(ami_flags, "struct", " ", typename, 0);
-	case TNF_NORCHECK:
-	case TNF_CHECK:
-		assert(_format != TNF_NORCHECK);
-		assert(_format != TNF_CHECK);
+		return asn1c_make_identifier(0, typename, 0);
+	case TNF_CTYPE:	/* C type */
+		return asn1c_make_identifier(0, typename, "t", 0);
+	case TNF_RSAFE:	/* Recursion-safe type */
+		return asn1c_make_identifier(AMI_CHECK_RESERVED,
+			"struct", " ", typename, 0);
 	}
 
 	assert(!"unreachable");
diff --git a/libasn1compiler/asn1c_misc.h b/libasn1compiler/asn1c_misc.h
index c653fa8..eab7567 100644
--- a/libasn1compiler/asn1c_misc.h
+++ b/libasn1compiler/asn1c_misc.h
@@ -16,8 +16,6 @@
  * Return the type name of the specified expression.
  */
 enum tnfmt {
-	TNF_NORCHECK	= 0x00,
-	TNF_CHECK	= 0x01,
 	TNF_UNMODIFIED	= 0x10,	/* Return unmodified type name */
 	TNF_INCLUDE	= 0x20,	/* Format for #include <> */
 	TNF_CTYPE	= 0x30,	/* Format as normal C-ish type (append "_t") */
diff --git a/libasn1compiler/asn1c_out.c b/libasn1compiler/asn1c_out.c
index 14b1f18..46029f7 100644
--- a/libasn1compiler/asn1c_out.c
+++ b/libasn1compiler/asn1c_out.c
@@ -72,7 +72,9 @@
 
 	m->len = ret;
 
-	if(arg->target->target == OT_INCLUDES) {
+	if(arg->target->target == OT_INCLUDES
+	|| arg->target->target == OT_FWD_DECLS
+	|| arg->target->target == OT_POST_INCLUDE) {
 		out_chunk_t *v;
 		TQ_FOR(v, &dst->chunks, next) {
 			if(m->len == v->len
diff --git a/libasn1compiler/asn1c_out.h b/libasn1compiler/asn1c_out.h
index 5a0a1f2..e500a54 100644
--- a/libasn1compiler/asn1c_out.h
+++ b/libasn1compiler/asn1c_out.h
@@ -16,8 +16,10 @@
 		OT_IGNORE,	/* Ignore this output */
 		OT_INCLUDES,	/* #include files */
 		OT_DEPS,	/* Dependencies (other than #includes) */
+		OT_FWD_DECLS,	/* Forward declarations */
 		OT_TYPE_DECLS,	/* Type declarations */
 		OT_FUNC_DECLS,	/* Function declarations */
+		OT_POST_INCLUDE,/* #include after type definition */
 		OT_CTABLES,	/* Constraint tables */
 		OT_CODE,	/* Some code */
 		OT_STAT_DEFS,	/* Static definitions */
@@ -32,7 +34,7 @@
 } compiler_streams_t;
 
 static char *_compiler_stream2str[] __attribute__ ((unused))
-    = { "IGNORE", "INCLUDES", "DEPS", "TYPE-DECLS", "FUNC-DECLS", "CTABLES", "CODE", "STAT-DEFS" };
+    = { "IGNORE", "INCLUDES", "DEPS", "FWD-DECLS", "TYPE-DECLS", "FUNC-DECLS", "POST-INCLUDE", "CTABLES", "CODE", "STAT-DEFS" };
 
 int asn1c_compiled_output(arg_t *arg, const char *fmt, ...);
 
@@ -86,14 +88,20 @@
 	OUT_NOINDENT("#include <%s.h>\n", filename);		\
 	REDIR(saved_target);					\
 } while(0)
+#define	GEN_POSTINCLUDE(filename)	do {			\
+	int saved_target = arg->target->target;			\
+	REDIR(OT_POST_INCLUDE);					\
+	OUT_NOINDENT("#include <%s.h>\n", filename);		\
+	REDIR(saved_target);					\
+} while(0)
 
 /* Generate ASN.1 type declaration */
 #define	GEN_DECLARE(expr)	do {				\
 	int saved_target = arg->target->target;			\
-	REDIR(OT_DEPS);						\
+	REDIR(OT_FUNC_DECLS);					\
 	OUT_NOINDENT("extern asn_TYPE_descriptor_t "		\
 			"asn_DEF_%s;\n",			\
-			MKID(expr->Identifier));		\
+			MKID_nc(expr->Identifier));		\
 	REDIR(saved_target);					\
 } while(0)
 
diff --git a/libasn1compiler/asn1c_save.c b/libasn1compiler/asn1c_save.c
index 2d0410f..0f3ed04 100644
--- a/libasn1compiler/asn1c_save.c
+++ b/libasn1compiler/asn1c_save.c
@@ -222,21 +222,23 @@
 
 	fprintf(fp_h, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
 
-	fprintf(fp_h, "#include <asn_application.h>\n\n");
+	fprintf(fp_h, "#include <asn_application.h>\n");
 
-	TQ_FOR(ot, &(cs->destination[OT_INCLUDES].chunks), next) {
-		asn1c_activate_dependency(deps, 0, ot->buf);
-		fwrite(ot->buf, ot->len, 1, fp_h);
-	}
-	fprintf(fp_h, "\n");
-	TQ_FOR(ot, &(cs->destination[OT_DEPS].chunks), next)
-		fwrite(ot->buf, ot->len, 1, fp_h);
-	fprintf(fp_h, "\n");
-	TQ_FOR(ot, &(cs->destination[OT_TYPE_DECLS].chunks), next)
-		fwrite(ot->buf, ot->len, 1, fp_h);
-	fprintf(fp_h, "\n");
-	TQ_FOR(ot, &(cs->destination[OT_FUNC_DECLS].chunks), next)
-		fwrite(ot->buf, ot->len, 1, fp_h);
+#define	SAVE_STREAM(idx, msg, actdep)	do {				\
+	if(TQ_FIRST(&(cs->destination[idx].chunks)))			\
+		fprintf(fp_h, "\n/* %s */\n", msg);			\
+	TQ_FOR(ot, &(cs->destination[idx].chunks), next) {		\
+		if(actdep) asn1c_activate_dependency(deps, 0, ot->buf);	\
+		fwrite(ot->buf, ot->len, 1, fp_h);			\
+	}								\
+} while(0)
+
+	SAVE_STREAM(OT_INCLUDES, "Including external dependencies", 1);
+	SAVE_STREAM(OT_DEPS, "Dependencies", 0);
+	SAVE_STREAM(OT_FWD_DECLS, "Forward declarations", 0);
+	SAVE_STREAM(OT_TYPE_DECLS, expr->Identifier, 0);
+	SAVE_STREAM(OT_FUNC_DECLS, "Implementation", 0);
+	SAVE_STREAM(OT_POST_INCLUDE, "Referred external types", 1);
 
 	fprintf(fp_h, "\n#ifdef __cplusplus\n}\n#endif\n\n"
 			"#endif\t/* _%s_H_ */\n",
@@ -251,7 +253,7 @@
 	TQ_FOR(ot, &(cs->destination[OT_STAT_DEFS].chunks), next)
 		fwrite(ot->buf, ot->len, 1, fp_c);
 
-	assert(OT_MAX == 8);
+	assert(OT_MAX == 10);	/* Protection from reckless changes */
 
 	fclose(fp_c);
 	fclose(fp_h);
diff --git a/libasn1compiler/asn1compiler.c b/libasn1compiler/asn1compiler.c
index 0159da1..107303d 100644
--- a/libasn1compiler/asn1compiler.c
+++ b/libasn1compiler/asn1compiler.c
@@ -76,9 +76,6 @@
 	type_cb = asn1_lang_map[expr->meta_type][expr->expr_type].type_cb;
 	if(type_cb) {
 
-		if(arg->target->destination[OT_TYPE_DECLS].indent_level == 0)
-			OUT("\n");
-
 		DEBUG("Compiling %s at line %d",
 			expr->Identifier,
 			expr->_lineno);