diff --git a/src/snc/gen_code.c b/src/snc/gen_code.c
index 79af07a5493b155597a52b58eed6622a2dee14ed..74665ab0fb6ef325715cbddcb22e56ceda86fd46 100644
--- a/src/snc/gen_code.c
+++ b/src/snc/gen_code.c
@@ -22,6 +22,7 @@ in the file LICENSE that is included with this distribution.
 #include "gen_code.h"
 
 static void gen_preamble(char *prog_name);
+static void gen_header_preamble(char *prog_name);
 static void gen_main(char *prog_name);
 static void gen_user_var(Program *p);
 static void gen_global_c_code(Expr *global_c_list);
@@ -41,7 +42,7 @@ static int assert_var_declared(Expr *ep, Expr *scope, void *parg)
 }
 
 /* Generate C code from parse tree. */
-void generate_code(Program *p)
+void generate_code(Program *p, const char *header_name)
 {
 	/* assume there have been no errors, so all vars are declared */
 	traverse_expr_tree(p->prog, 1<<E_VAR, 0, 0, assert_var_declared, 0);
@@ -50,24 +51,31 @@ void generate_code(Program *p)
 	report("-------------------- Code Generation --------------------\n");
 #endif
 
-#ifdef DEBUG
-	report("gen_tables:\n");
-	report(" num_event_flags = %d\n", p->num_event_flags);
-	report(" num_ss = %d\n", p->num_ss);
-#endif
-
-	/* Generate preamble code */
-	gen_preamble(p->name);
-
 	/* Initialize tables in gen_ss_code module */
 	/* TODO: find a better way to do this */
 	init_gen_ss_code(p);
 
+	set_gen_h();
+
+	gen_code("/* Generated with snc from %s */\n", p->prog->src_file);
+	gen_code("#ifndef INCL%sh\n", p->name);
+	gen_code("#define INCL%sh\n", p->name);
+
+	/* Generate preamble code */
+	gen_header_preamble(p->name);
+
 	/* Generate literal C code intermixed with global definitions */
 	gen_defn_c_code(p->prog, 0);
 
 	/* Generate global, state set, and state variable declarations */
 	gen_user_var(p);
+	gen_code("#endif /* INCL%sh */\n", p->name);
+
+	set_gen_c();
+
+	gen_code("/* Generated with snc from %s */\n", p->prog->src_file);
+	gen_preamble(p->name);
+	gen_code("#include \"%s\"\n", header_name);
 
 	/* Generate code for each state set */
 	gen_ss_code(p);
@@ -88,23 +96,32 @@ void generate_code(Program *p)
 /* Generate main program */
 static void gen_main(char *prog_name)
 {
-	printf("\n#define PROG_NAME %s\n", prog_name);
-	printf("#include \"seqMain.c\"\n");
+	gen_code("\n#define PROG_NAME %s\n", prog_name);
+	gen_code("#include \"seqMain.c\"\n");
+}
+
+/* Generate header preamble (includes, defines, etc.) */
+static void gen_header_preamble(char *prog_name)
+{
+	/* Program name (comment) */
+	gen_code("\n/* Header file for program \"%s\" */\n", prog_name);
+
+	/* Includes */
+	gen_code("#include \"epicsTypes.h\"\n");
+	gen_code("#include \"seqCom.h\"\n");
 }
 
 /* Generate preamble (includes, defines, etc.) */
 static void gen_preamble(char *prog_name)
 {
 	/* Program name (comment) */
-	printf("\n/* Program \"%s\" */\n", prog_name);
+	gen_code("\n/* C code for program \"%s\" */\n", prog_name);
 
 	/* Includes */
-	printf("#include <string.h>\n");
-	printf("#include <stddef.h>\n");
-	printf("#include <stdio.h>\n");
-	printf("#include <limits.h>\n");
-	printf("#include \"epicsTypes.h\"\n");
-	printf("#include \"seqCom.h\"\n");
+	gen_code("#include <string.h>\n");
+	gen_code("#include <stddef.h>\n");
+	gen_code("#include <stdio.h>\n");
+	gen_code("#include <limits.h>\n");
 }
 
 void gen_var_decl(Var *vp)
@@ -125,24 +142,24 @@ static void gen_user_var(Program *p)
 	Var	*vp;
 	Expr	*sp, *ssp;
 
-	printf("\n/* Variable declarations */\n");
+	gen_code("\n/* Variable declarations */\n");
 
-	if (opt_reent) printf("struct %s {\n", NM_VARS);
+	if (opt_reent) gen_code("struct %s {\n", NM_VARS);
 	/* Convert internal type to `C' type */
 	foreach (vp, p->prog->extra.e_prog->first)
 	{
 		if (vp->decl && vp->type->tag >= V_CHAR)
 		{
 			gen_line_marker(vp->decl);
-			if (!opt_reent) printf("static");
+			if (!opt_reent) gen_code("static");
 			indent(1);
 			gen_var_decl(vp);
 			if (!opt_reent)
 			{
-				printf(" = ");
+				gen_code(" = ");
 				gen_var_init(vp, 0);
 			}
-			printf(";\n");
+			gen_code(";\n");
 		}
 	}
 	foreach (ssp, p->prog->prog_statesets)
@@ -164,10 +181,10 @@ static void gen_user_var(Program *p)
 
 		if (!ss_empty)
 		{
-			indent(level); printf("struct %s_%s {\n", NM_VARS, ssp->value);
+			indent(level); gen_code("struct %s_%s {\n", NM_VARS, ssp->value);
 			foreach (vp, ssp->extra.e_ss->var_list->first)
 			{
-				indent(level+1); gen_var_decl(vp); printf(";\n");
+				indent(level+1); gen_var_decl(vp); gen_code(";\n");
 			}
 			foreach (sp, ssp->ss_states)
 			{
@@ -175,26 +192,26 @@ static void gen_user_var(Program *p)
 				if (!s_empty)
 				{
 					indent(level+1);
-					printf("struct {\n");
+					gen_code("struct {\n");
 					foreach (vp, sp->extra.e_state->var_list->first)
 					{
-						indent(level+2); gen_var_decl(vp); printf(";\n");
+						indent(level+2); gen_var_decl(vp); gen_code(";\n");
 					}
 					indent(level+1);
-					printf("} %s_%s;\n", NM_VARS, sp->value);
+					gen_code("} %s_%s;\n", NM_VARS, sp->value);
 				}
 			}
-			indent(level); printf("} %s_%s", NM_VARS, ssp->value);
+			indent(level); gen_code("} %s_%s", NM_VARS, ssp->value);
 			if (!opt_reent)
 			{
-				printf(" = ");
+				gen_code(" = ");
 				gen_ss_user_var_init(ssp, level);
 			}
-			printf(";\n");
+			gen_code(";\n");
 		}
 	}
-	if (opt_reent) printf("};\n");
-	printf("\n");
+	if (opt_reent) gen_code("};\n");
+	gen_code("\n");
 }
 
 /* Generate C code in definition section */
@@ -212,11 +229,11 @@ void gen_defn_c_code(Expr *scope, int level)
 			{
 				first = FALSE;
 				indent(level);
-				printf("/* C code definitions */\n");
+				gen_code("/* C code definitions */\n");
 			}
 			gen_line_marker(ep);
 			indent(level);
-			printf("%s\n", ep->value);
+			gen_code("%s\n", ep->value);
 		}
 	}
 }
@@ -228,29 +245,29 @@ static void gen_global_c_code(Expr *global_c_list)
 
 	if (global_c_list != 0)
 	{
-		printf("\n/* Global C code */\n");
+		gen_code("\n/* Global C code */\n");
 		foreach (ep, global_c_list)
 		{
 			assert(ep->type == T_TEXT);
 			gen_line_marker(ep);
-			printf("%s\n", ep->value);
+			gen_code("%s\n", ep->value);
 		}
 	}
 }
 
 static void gen_init_reg(char *prog_name)
 {
-	printf("\n/* Register sequencer commands and program */\n");
-	printf("#include \"epicsExport.h\"\n");
-	printf("static void %sRegistrar (void) {\n", prog_name);
-	printf("    seqRegisterSequencerCommands();\n");
-	printf("    seqRegisterSequencerProgram (&%s);\n", prog_name);
-	printf("}\n");
-	printf("epicsExportRegistrar(%sRegistrar);\n", prog_name);
+	gen_code("\n/* Register sequencer commands and program */\n");
+	gen_code("#include \"epicsExport.h\"\n");
+	gen_code("static void %sRegistrar (void) {\n", prog_name);
+	gen_code("    seqRegisterSequencerCommands();\n");
+	gen_code("    seqRegisterSequencerProgram (&%s);\n", prog_name);
+	gen_code("}\n");
+	gen_code("epicsExportRegistrar(%sRegistrar);\n", prog_name);
 }
 
 void indent(int level)
 {
 	while (level-- > 0)
-		printf("\t");
+		gen_code("\t");
 }
diff --git a/src/snc/gen_code.h b/src/snc/gen_code.h
index 56a54992fb44317bd061120c8ea8f7397d4bf6fe..19634144384ad1473f75cf53b5bad486a75ab393 100644
--- a/src/snc/gen_code.h
+++ b/src/snc/gen_code.h
@@ -15,7 +15,7 @@ in the file LICENSE that is included with this distribution.
 
 #include "types.h"
 
-void generate_code(Program *p);
+void generate_code(Program *p, const char *header_name);
 void gen_defn_c_code(Expr *scope, int level);
 void gen_var_decl(Var *vp);
 void indent(int level);
diff --git a/src/snc/gen_ss_code.c b/src/snc/gen_ss_code.c
index 23f6b00ee40d6f584d5c378028bd1d6395263f92..cf991c7b616b1d8fc9fe7240af9acc16ef9f7e05 100644
--- a/src/snc/gen_ss_code.c
+++ b/src/snc/gen_ss_code.c
@@ -104,7 +104,7 @@ void gen_ss_code(Program *program)
 		/* For each state ... */
 		foreach (sp, ssp->ss_states)
 		{
-			printf("\n/****** Code for state \"%s\" in state set \"%s\" ******/\n",
+			gen_code("\n/****** Code for state \"%s\" in state set \"%s\" ******/\n",
 				sp->value, ssp->value);
 
 			/* Generate entry and exit functions */
@@ -160,10 +160,10 @@ static void gen_local_var_decls(Expr *scope, int level)
 			/* optional initialisation */
 			if (vp->init)
 			{
-				printf(" = ");
+				gen_code(" = ");
 				gen_expr(C_INIT, vp->init, level);
 			}
-			printf(";\n");
+			gen_code(";\n");
 		}
 	}
 }
@@ -176,19 +176,19 @@ static void gen_type_default(Type *type)
 	switch(type->tag)
 	{
 	case V_STRING:
-		printf("\"\"");
+		gen_code("\"\"");
 		break;
 	case T_ARRAY:
-		printf("{");
+		gen_code("{");
 		for (n=0; n<type->val.array.num_elems; n++)
 		{
 			gen_type_default(type->val.array.elem_type);
-			if (n+1<type->val.array.num_elems) printf(",");
+			if (n+1<type->val.array.num_elems) gen_code(",");
 		}
-		printf("}");
+		gen_code("}");
 		break;
 	default:
-		printf("0");
+		gen_code("0");
 	}
 }
 
@@ -214,12 +214,12 @@ static void gen_state_func(
 	const char *extra_args
 )
 {
-	printf("\n/* %s function for state \"%s\" in state set \"%s\" */\n",
+	gen_code("\n/* %s function for state \"%s\" in state set \"%s\" */\n",
 		title, state_name, ss_name);
-	printf("static %s %s_%s_%d_%s(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS "%s)\n{\n",
+	gen_code("static %s %s_%s_%d_%s(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS "%s)\n{\n",
 		rettype, prefix, ss_name, ss_num, state_name, extra_args);
 	gen_body(xp);
-	printf("}\n");
+	gen_code("}\n");
 }
 
 static void gen_entex_body(Expr *xp)
@@ -260,11 +260,11 @@ static int gen_delay(Expr *ep, Expr *scope, void *parg)
 	assert(ep->type == E_DELAY);
 	gen_line_marker(ep);
 	/* Generate 1-st part of function w/ 1-st 2 parameters */
-	indent(1); printf("seq_delayInit(" NM_SS ", %d, (", ep->extra.e_delay);
+	indent(1); gen_code("seq_delayInit(" NM_SS ", %d, (", ep->extra.e_delay);
 	/* generate the 3-rd parameter (an expression) */
 	gen_expr(C_COND, ep->delay_args, 0);
 	/* Complete the function call */
-	printf("));\n");
+	gen_code("));\n");
 	return FALSE;	/* no sense descending into children, as delay cannot be nested */
 }
 
@@ -278,8 +278,8 @@ static void gen_action_body(Expr *xp)
 	const int	level = 1;
 
 	/* "switch" statment based on the transition number */
-	indent(level); printf("switch(" NM_TRN ")\n");
-	indent(level); printf("{\n");
+	indent(level); gen_code("switch(" NM_TRN ")\n");
+	indent(level); gen_code("{\n");
 	trans_num = 0;
 
 	/* For each transition ("when" statement) ... */
@@ -289,29 +289,29 @@ static void gen_action_body(Expr *xp)
 
 		assert(tp->type == D_WHEN);
 		/* one case for each transition */
-		indent(level); printf("case %d:\n", trans_num);
+		indent(level); gen_code("case %d:\n", trans_num);
 
 		/* block within case permits local variables */
-		indent(level+1); printf("{\n");
+		indent(level+1); gen_code("{\n");
 		/* for each definition insert corresponding code */
 		gen_local_var_decls(tp, level+2);
 		gen_defn_c_code(tp, level+2);
 		if (tp->when_defns)
-			printf("\n");
+			gen_code("\n");
 		/* for each action statement insert action code */
 		foreach (ap, tp->when_stmts)
 		{
 			gen_expr(C_TRANS, ap, level+2);
 		}
 		/* end of block */
-		indent(level+1); printf("}\n");
+		indent(level+1); gen_code("}\n");
 
 		/* end of case */
-		indent(level+1); printf("return;\n");
+		indent(level+1); gen_code("return;\n");
 		trans_num++;
 	}
 	/* end of switch stmt */
-	indent(level); printf("}\n");
+	indent(level); gen_code("}\n");
 }
 
 /* Generate a C function that checks events for a particular state */
@@ -330,32 +330,32 @@ static void gen_event_body(Expr *xp)
 		assert(tp->type == D_WHEN);
 		if (tp->when_cond)
 			gen_line_marker(tp->when_cond);
-		indent(level); printf("if (");
+		indent(level); gen_code("if (");
 		if (tp->when_cond == 0)
-			printf("TRUE");
+			gen_code("TRUE");
 		else
 			gen_expr(C_COND, tp->when_cond, 0);
-		printf(")\n");
-		indent(level); printf("{\n");
+		gen_code(")\n");
+		indent(level); gen_code("{\n");
 
 		next_sp = tp->extra.e_when->next_state;
 		if (!next_sp)
 		{
 			/* "when(...) {...} exit" -> exit from program */
 			indent(level+1);
-			printf("seq_exit(" NM_SS ");\n");
+			gen_code("seq_exit(" NM_SS ");\n");
 		}
 		else
 		{
 			indent(level+1);
-			printf("*" NM_PNST " = %d;\n", next_sp->extra.e_state->index);
+			gen_code("*" NM_PNST " = %d;\n", next_sp->extra.e_state->index);
 		}
-		indent(level+1);printf("*" NM_PTRN " = %d;\n", trans_num);
-		indent(level+1); printf("return TRUE;\n");
-		indent(level); printf("}\n");
+		indent(level+1);gen_code("*" NM_PTRN " = %d;\n", trans_num);
+		indent(level+1); gen_code("return TRUE;\n");
+		indent(level); gen_code("}\n");
 		trans_num++;
 	}
-	indent(level); printf("return FALSE;\n");
+	indent(level); gen_code("return FALSE;\n");
 }
 
 static void gen_var_access(Var *vp)
@@ -373,29 +373,29 @@ static void gen_var_access(Var *vp)
 
 	if (vp->type->tag == T_EVFLAG)
 	{
-		printf("%d/*%s*/", vp->chan.evflag->index, vp->name);
+		gen_code("%d/*%s*/", vp->chan.evflag->index, vp->name);
 	}
 	else if (vp->type->tag == T_NONE)
 	{
-		printf("%s", vp->name);
+		gen_code("%s", vp->name);
 	}
 	else if (vp->scope->type == D_PROG)
 	{
-		printf("%s%s", pre, vp->name);
+		gen_code("%s%s", pre, vp->name);
 	}
 	else if (vp->scope->type == D_SS)
 	{
-		printf("%s%s_%s.%s", pre, NM_VARS, vp->scope->value, vp->name);
+		gen_code("%s%s_%s.%s", pre, NM_VARS, vp->scope->value, vp->name);
 	}
 	else if (vp->scope->type == D_STATE)
 	{
-		printf("%s%s_%s.%s_%s.%s", pre, NM_VARS,
+		gen_code("%s%s_%s.%s_%s.%s", pre, NM_VARS,
 			vp->scope->extra.e_state->var_list->parent_scope->value,
 			NM_VARS, vp->scope->value, vp->name);
 	}
 	else	/* compound or when stmt => generate a local C variable */
 	{
-		printf("%s", vp->name);
+		gen_code("%s", vp->name);
 	}
 }
 
@@ -420,7 +420,7 @@ static void gen_expr(
 	/* Statements */
 	case S_CMPND:
 		indent(level);
-		printf("{\n");
+		gen_code("{\n");
 		gen_local_var_decls(ep, level+1);
 		gen_defn_c_code(ep, level+1);
 		foreach (cep, ep->cmpnd_stmts)
@@ -428,26 +428,26 @@ static void gen_expr(
 			gen_expr(context, cep, level+1);
 		}
 		indent(level);
-		printf("}\n");
+		gen_code("}\n");
 		break;
 	case S_STMT:
 		gen_line_marker(ep);
 		indent(level);
 		gen_expr(context, ep->stmt_expr, 0);
-		printf(";\n");
+		gen_code(";\n");
 		break;
 	case S_IF:
 		gen_line_marker(ep);
 		indent(level);
-		printf("if (");
+		gen_code("if (");
 		gen_expr(context, ep->if_cond, 0);
-		printf(")\n");
+		gen_code(")\n");
 		cep = ep->if_then;
 		gen_expr(context, cep, cep->type == S_CMPND ? level : level+1);
 		if (ep->if_else)
 		{
 			indent(level);
-			printf("else\n");
+			gen_code("else\n");
 			cep = ep->if_else;
 			gen_expr(context, cep, cep->type == S_CMPND ? level : level+1);
 		}
@@ -455,28 +455,28 @@ static void gen_expr(
 	case S_WHILE:
 		gen_line_marker(ep);
 		indent(level);
-		printf("while (");
+		gen_code("while (");
 		gen_expr(context, ep->while_cond, 0);
-		printf(")\n");
+		gen_code(")\n");
 		cep = ep->while_stmt;
 		gen_expr(context, cep, cep->type == S_CMPND ? level : level+1);
 		break;
 	case S_FOR:
 		gen_line_marker(ep);
 		indent(level);
-		printf("for (");
+		gen_code("for (");
 		gen_expr(context, ep->for_init, 0);
-		printf("; ");
+		gen_code("; ");
 		gen_expr(context, ep->for_cond, 0);
-		printf("; ");
+		gen_code("; ");
 		gen_expr(context, ep->for_iter, 0);
-		printf(")\n");
+		gen_code(")\n");
 		cep = ep->for_stmt;
 		gen_expr(context, cep, cep->type == S_CMPND ? level : level+1);
 		break;
 	case S_JUMP:
 		indent(level);
-		printf("%s;\n", ep->value);
+		gen_code("%s;\n", ep->value);
 		break;
 	case S_CHANGE:
 		if (context != C_TRANS)
@@ -485,7 +485,7 @@ static void gen_expr(
 			break;
 		}
 		indent(level);
-		printf("{*" NM_PNST " = %d; return;}\n", ep->extra.e_change->extra.e_state->index);
+		gen_code("{*" NM_PNST " = %d; return;}\n", ep->extra.e_change->extra.e_state->index);
 		break;
 	/* Expressions */
 	case E_VAR:
@@ -493,78 +493,78 @@ static void gen_expr(
 		break;
 	case E_SUBSCR:
 		gen_expr(context, ep->subscr_operand, 0);
-		printf("[");
+		gen_code("[");
 		gen_expr(context, ep->subscr_index, 0);
-		printf("]");
+		gen_code("]");
 		break;
 	case E_CONST:
 		if (gen_builtin_const(ep))
 			break;
-		printf("%s", ep->value);
+		gen_code("%s", ep->value);
 		break;
 	case E_STRING:
-		printf("\"%s\"", ep->value);
+		gen_code("\"%s\"", ep->value);
 		break;
 	case E_DELAY:
 	case E_FUNC:
 		if (gen_builtin_func(context, ep))
 			break;
-		printf("%s(", ep->value);
+		gen_code("%s(", ep->value);
 		foreach (cep, ep->func_args)
 		{
 			gen_expr(context, cep, 0);
 			if (cep->next)
-				printf(", ");
+				gen_code(", ");
 		}
-		printf(")");
+		gen_code(")");
 		break;
 	case E_INIT:
-		printf("{");
+		gen_code("{");
 		foreach (cep, ep->init_elems)
 		{
 			gen_expr(context, cep, 0);
 			if (cep->next)
-				printf(", ");
+				gen_code(", ");
 		}
-		printf("}");
+		gen_code("}");
 		break;
 	case E_TERNOP:
 		gen_expr(context, ep->ternop_cond, 0);
-		printf(" ? ");
+		gen_code(" ? ");
 		gen_expr(context, ep->ternop_then, 0);
-		printf(" : ");
+		gen_code(" : ");
 		gen_expr(context, ep->ternop_else, 0);
 		break;
 	case E_BINOP:
 		gen_expr(context, ep->binop_left, 0);
-		printf(" %s ", ep->value);
+		gen_code(" %s ", ep->value);
 		gen_expr(context, ep->binop_right, 0);
 		break;
 	case E_PAREN:
-		printf("(");
+		gen_code("(");
 		gen_expr(context, ep->paren_expr, 0);
-		printf(")");
+		gen_code(")");
 		break;
 	case E_CAST:
-		printf("(");
+		gen_code("(");
 		/* gen_type_expr(ep->cast_type); */
 		assert(ep->cast_type->type == D_DECL);
 		gen_var_decl(ep->cast_type->extra.e_decl);
-		printf(")");
+		gen_code(")");
 		gen_expr(context, ep->cast_operand, 0);
 		break;
 	case E_PRE:
-		printf("%s", ep->value);
+		gen_code("%s", ep->value);
 		gen_expr(context, ep->pre_operand, 0);
 		break;
 	case E_POST:
 		gen_expr(context, ep->post_operand, 0);
-		printf("%s", ep->value);
+		gen_code("%s", ep->value);
 		break;
 	/* C-code can be either definition, statement, or expression */
 	case T_TEXT:
 		indent(level);
-		printf("%s\n", ep->value);
+		gen_code("%s\n", ep->value);
 		break;
 	default:
 		assert(impossible);
@@ -582,7 +582,7 @@ static int gen_builtin_const(Expr *ep)
 
 	if (!sym)
 		return CT_NONE;
-	printf("%s", const_name);
+	gen_code("%s", const_name);
 	return sym->type;
 }
 
@@ -603,11 +603,11 @@ static int gen_builtin_func(int context, Expr *ep)
 		sym->ef_action_only, sym->ef_args);
 #endif
 	/* All builtin functions require ssId as 1st parameter */
-	printf("seq_%s(" NM_SS "", func_name);
+	gen_code("seq_%s(" NM_SS "", func_name);
 	switch (sym->type)
 	{
 	case FT_DELAY:
-		printf(", %d)", ep->extra.e_delay);
+		gen_code(", %d)", ep->extra.e_delay);
 		break;
 	case FT_EVENT:
 		/* Event flag functions */
@@ -621,10 +621,10 @@ static int gen_builtin_func(int context, Expr *ep)
 		/* just fill in user-supplied parameters */
 		foreach (ap, ep->func_args)
 		{
-			printf(", ");
+			gen_code(", ");
 			gen_expr(context, ap, 0);
 		}
-		printf(")");
+		gen_code(")");
 		break;
 	default:
 		assert(impossible);
@@ -684,9 +684,9 @@ static void gen_ef_func(
 		  "built-in function %s requires an argument\n", func_name);
 		return;
 	}
-	printf(", ");
+	gen_code(", ");
 	gen_ef_arg(func_name, ap, 1);
-	printf(")");
+	gen_code(")");
 }
 
 /* Generate code for pv functions requiring a database variable.
@@ -740,32 +740,32 @@ static void gen_pv_func(
 #ifdef	DEBUG
 	report("gen_pv_func: fun=%s, var=%s\n", ep->value, vp->name);
 #endif
-	printf(", ");
+	gen_code(", ");
 	if (vp->assign == M_NONE)
 	{
 		error_at_expr(ep,
 			"parameter 1 to '%s' was not assigned to a pv\n", func_name);
-		printf("?/*%s*/", vp->name);
+		gen_code("?/*%s*/", vp->name);
 	}
 	else if (ap->type == E_SUBSCR && vp->assign != M_MULTI)
 	{
 		error_at_expr(ep,
 			"parameter 1 to '%s' is subscripted but the variable "
 			"it refers to has not been assigned to multiple pvs\n", func_name);
-		printf("%d/*%s*/", vp->index, vp->name);
+		gen_code("%d/*%s*/", vp->index, vp->name);
 	}
 	else
 	{
-		printf("%d/*%s*/", vp->index, vp->name);
+		gen_code("%d/*%s*/", vp->index, vp->name);
 	}
 
 	if (ap->type == E_SUBSCR)
 	{
 		/* e.g. pvPut(xyz[i+2]); => seq_pvPut(ssId, 3 + (i+2)); */
-		printf(" + (VAR_ID)(");
+		gen_code(" + (VAR_ID)(");
 		/* generate the subscript expression */
 		gen_expr(context, subscr, 0);
-		printf(")");
+		gen_code(")");
 	}
 
 	/* If requested, add length parameter (if subscripted variable,
@@ -774,11 +774,11 @@ static void gen_pv_func(
 	{
 		if (ap->type != E_SUBSCR)
 		{
-			printf(", %d", type_array_length1(vp->type));
+			gen_code(", %d", type_array_length1(vp->type));
 		}
 		else
 		{
-			printf(", 1");
+			gen_code(", 1");
 		}
 	}
 
@@ -786,7 +786,7 @@ static void gen_pv_func(
 	foreach (ap, ap->next)
 	{
 		num_extra_parms++;
-		printf(", ");
+		gen_code(", ");
 		if (ef_args)
 		{
 			/* special case: constant NOEVFLAG */
@@ -810,12 +810,12 @@ static void gen_pv_func(
 	   extra zero parameters */
 	while (num_extra_parms < num_params)
 	{
-		printf(", 0");
+		gen_code(", 0");
 		num_extra_parms++;
 	}
 
 	/* Close the parameter list */
-	printf(")");
+	gen_code(")");
 #ifdef	DEBUG
 	report("gen_pv_func: done (fun=%s, var=%s)\n", ep->value, vp->name);
 #endif
@@ -828,11 +828,11 @@ void gen_ss_user_var_init(Expr *ssp, int level)
 	Expr *sp;
 
 	assert(ssp->type == D_SS);
-	printf("{\n");
+	gen_code("{\n");
 	foreach(vp, ssp->extra.e_ss->var_list->first)
 	{
 		if (vp->init) gen_line_marker(vp->init);
-		indent(level+1); gen_var_init(vp, level+1); printf(",\n");
+		indent(level+1); gen_var_init(vp, level+1); gen_code(",\n");
 	}
 	foreach (sp, ssp->ss_states)
 	{
@@ -842,18 +842,18 @@ void gen_ss_user_var_init(Expr *ssp, int level)
 		s_empty = !sp->extra.e_state->var_list->first;
 		if (!s_empty)
 		{
-			indent(level+1); printf("{\n");
+			indent(level+1); gen_code("{\n");
 			foreach (vp, sp->extra.e_state->var_list->first)
 			{
 				if (vp->init) gen_line_marker(vp->init);
 				indent(level+2); gen_var_init(vp, level+2);
-				printf("%s\n", vp->next ? "," : "");
+				gen_code("%s\n", vp->next ? "," : "");
 			}
 			indent(level+1);
-			printf("}%s\n", sp->next ? "," : "");
+			gen_code("}%s\n", sp->next ? "," : "");
 		}
 	}
-	indent(level); printf("}");
+	indent(level); gen_code("}");
 }
 
 /* Generate initializer for the UserVar structs. */
@@ -863,14 +863,14 @@ static void gen_user_var_init(Expr *prog, int level)
 	Expr *ssp;
 
 	assert(prog->type == D_PROG);
-	printf("{\n");
+	gen_code("{\n");
 	/* global variables */
 	foreach(vp, prog->extra.e_prog->first)
 	{
 		if (vp->type->tag >= V_CHAR)
 		{
 			if (vp->init) gen_line_marker(vp->init);
-			indent(level+1); gen_var_init(vp, level+1); printf(",\n");
+			indent(level+1); gen_var_init(vp, level+1); gen_code(",\n");
 		}
 	}
 
@@ -893,24 +893,24 @@ static void gen_user_var_init(Expr *prog, int level)
 		{
 			indent(level+1);
 			gen_ss_user_var_init(ssp, level+1);
-			printf("%s\n", ssp->next ? "," : "");
+			gen_code("%s\n", ssp->next ? "," : "");
 		}
 	}
-	indent(level); printf("}");
+	indent(level); gen_code("}");
 }
 
 static void gen_prog_init_func(Expr *prog, int opt_reent)
 {
 	assert(prog->type == D_PROG);
-	printf("\n/* Program init func */\n");
-	printf("static void " NM_INIT "(SEQ_VARS *const " NM_VARS ")\n{\n");
+	gen_code("\n/* Program init func */\n");
+	gen_code("static void " NM_INIT "(SEQ_VARS *const " NM_VARS ")\n{\n");
 	if (opt_reent)
 	{
-		indent(1); printf("static struct %s pVarInit = ", NM_VARS);
-		gen_user_var_init(prog, 1); printf(";\n");
-		indent(1); printf("*pVar = pVarInit;\n");
+		indent(1); gen_code("static struct %s pVarInit = ", NM_VARS);
+		gen_user_var_init(prog, 1); gen_code(";\n");
+		indent(1); gen_code("*pVar = pVarInit;\n");
 	}
-	printf("}\n");
+	gen_code("}\n");
 }
 
 static void gen_prog_func(
@@ -921,9 +921,9 @@ static void gen_prog_func(
 	void (*gen_body)(Expr *xp))
 {
 	assert(prog->type == D_PROG);
-	printf("\n/* Program %s func */\n", doc);
-	printf("static void %s(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS ")\n{\n",
+	gen_code("\n/* Program %s func */\n", doc);
+	gen_code("static void %s(SS_ID " NM_SS ", SEQ_VARS *const " NM_VARS ")\n{\n",
 		name);
 	if (xp && gen_body) gen_body(xp);
-	printf("}\n");
+	gen_code("}\n");
 }
diff --git a/src/snc/gen_tables.c b/src/snc/gen_tables.c
index 1ef6ba5de1cf722bbed966f5063aef011fe04545..d1685cb417da4b4a071b5caa5977cd38b4bcec64 100644
--- a/src/snc/gen_tables.c
+++ b/src/snc/gen_tables.c
@@ -50,7 +50,7 @@ static int iter_event_mask_array(Expr *ep, Expr *scope, void *parg);
 /* Generate all kinds of tables for a SNL program. */
 void gen_tables(Program *p)
 {
-	printf("\n/************************ Tables ************************/\n");
+	gen_code("\n/************************ Tables ************************/\n");
 	gen_channel_table(p->chan_list, p->num_event_flags, p->options.reent);
 	gen_state_table(p->prog->prog_statesets, p->num_event_flags, p->chan_list->num_elems);
 	gen_ss_table(p->prog->prog_statesets);
@@ -64,20 +64,20 @@ static void gen_channel_table(ChanList *chan_list, uint num_event_flags, int opt
 
 	if (chan_list->first)
 	{
-		printf("\n/* Channel table */\n");
-		printf("static seqChan " NM_CHANS "[] = {\n");
-		printf("\t/* chName, offset, varName, varType, count, eventNum, efId, monitored, queueSize, queueIndex */\n");
+		gen_code("\n/* Channel table */\n");
+		gen_code("static seqChan " NM_CHANS "[] = {\n");
+		gen_code("\t/* chName, offset, varName, varType, count, eventNum, efId, monitored, queueSize, queueIndex */\n");
 		foreach (cp, chan_list->first)
 		{
 			gen_channel(cp, num_event_flags, opt_reent);
-			printf(",\n");
+			gen_code(",\n");
 		}
-		printf("};\n");
+		gen_code("};\n");
 	}
 	else
 	{
-		printf("\n/* No channel definitions */\n");
-		printf("#define " NM_CHANS " 0\n");
+		gen_code("\n/* No channel definitions */\n");
+		gen_code("#define " NM_CHANS " 0\n");
 	}
 }
 
@@ -85,15 +85,15 @@ static void gen_var_name(Var *vp)
 {
 	if (vp->scope->type == D_PROG)
 	{
-		printf("%s", vp->name);
+		gen_code("%s", vp->name);
 	}
 	else if (vp->scope->type == D_SS)
 	{
-		printf("%s_%s.%s", NM_VARS, vp->scope->value, vp->name);
+		gen_code("%s_%s.%s", NM_VARS, vp->scope->value, vp->name);
 	}
 	else if (vp->scope->type == D_STATE)
 	{
-		printf("%s_%s.%s_%s.%s", NM_VARS,
+		gen_code("%s_%s.%s_%s.%s", NM_VARS,
 			vp->scope->extra.e_state->var_list->parent_scope->value,
 			NM_VARS, vp->scope->value, vp->name);
 	}
@@ -112,15 +112,15 @@ static void gen_channel(Chan *cp, uint num_event_flags, int opt_reent)
 		enum prim_type_tag type = basetype->val.prim;
 		if (type == P_LONG || type == P_ULONG)
 		{
-			printf(
+			gen_code(
 "#if LONG_MAX > 0x7fffffffL\n"
 			);
 			gen_line_marker(vp->decl);
-			printf(
+			gen_code(
 "#error variable '"
 			);
 			gen_var_decl(vp);
-			printf("'"
+			gen_code("'"
 " cannot be assigned to a PV (on the chosen target system)\\\n"
 " because Channel Access does not support integral types longer than 4 bytes.\\\n"
 " You can use '%s' instead, or the fixed size type '%s'.\n"
@@ -140,44 +140,44 @@ static void gen_channel(Chan *cp, uint num_event_flags, int opt_reent)
 		ef_num = 0;
 
 	if (!cp->name)
-		printf("\t{0, ");
+		gen_code("\t{0, ");
 	else
-		printf("\t{\"%s\", ", cp->name);
+		gen_code("\t{\"%s\", ", cp->name);
 
 	if (opt_reent)
 	{
-		printf("offsetof(struct %s, ", NM_VARS);
+		gen_code("offsetof(struct %s, ", NM_VARS);
 		gen_var_name(vp);
-		printf("%s), ", elem_str);
+		gen_code("%s), ", elem_str);
 	}
 	else
 	{
-		printf("(size_t)&");
+		gen_code("(size_t)&");
 		gen_var_name(vp);
-		printf("%s, ", elem_str);
+		gen_code("%s, ", elem_str);
 	}
 
 	/* variable name with optional elem num */
-	printf("\"%s%s\", ", vp->name, elem_str);
+	gen_code("\"%s%s\", ", vp->name, elem_str);
 	/* variable type */
 	assert(base_type(vp->type)->tag == T_PRIM);
-	printf("\"%s\", ", prim_type_name[base_type(vp->type)->val.prim]);
+	gen_code("\"%s\", ", prim_type_name[base_type(vp->type)->val.prim]);
 	/* count, for requests */
-	printf("%d, ", cp->count);
+	gen_code("%d, ", cp->count);
 	/* event number */
-	printf("%d, ", num_event_flags + vp->index + cp->index + 1);
+	gen_code("%d, ", num_event_flags + vp->index + cp->index + 1);
 	/* event flag number (or 0) */
-	printf("%d, ", ef_num);
+	gen_code("%d, ", ef_num);
 	/* monitor flag */
-	printf("%d, ", cp->monitor);
+	gen_code("%d, ", cp->monitor);
 	/* syncQ queue */
 	if (!cp->syncq)
-		printf("0, 0");
+		gen_code("0, 0");
 	else if (!cp->syncq->size)
-		printf("DEFAULT_QUEUE_SIZE, %d", cp->syncq->index);
+		gen_code("DEFAULT_QUEUE_SIZE, %d", cp->syncq->index);
 	else
-		printf("%d, %d", cp->syncq->size, cp->syncq->index);
-	printf("}");
+		gen_code("%d, %d", cp->syncq->size, cp->syncq->index);
+	gen_code("}");
 }
 
 /* Generate state event mask and table */
@@ -202,25 +202,25 @@ static void gen_state_table(Expr *ss_list, uint num_event_flags, uint num_channe
 	foreach (ssp, ss_list)
 	{
 		/* Generate event mask array */
-		printf("\n/* Event masks for state set \"%s\" */\n", ssp->value);
+		gen_code("\n/* Event masks for state set \"%s\" */\n", ssp->value);
 		foreach (sp, ssp->ss_states)
 		{
 			gen_state_event_mask(sp, num_event_flags, event_mask, num_event_words);
-			printf("static const seqMask " NM_MASK "_%s_%d_%s[] = {\n",
+			gen_code("static const seqMask " NM_MASK "_%s_%d_%s[] = {\n",
 				ssp->value, ss_num, sp->value);
 			for (n = 0; n < num_event_words; n++)
-				printf("\t0x%08x,\n", event_mask[n]);
-			printf("};\n");
+				gen_code("\t0x%08x,\n", event_mask[n]);
+			gen_code("};\n");
 		}
 
 		/* Generate table of state structures */
-		printf("\n/* State table for state set \"%s\" */\n", ssp->value);
-		printf("static seqState " NM_STATES "_%s[] = {\n", ssp->value);
+		gen_code("\n/* State table for state set \"%s\" */\n", ssp->value);
+		gen_code("static seqState " NM_STATES "_%s[] = {\n", ssp->value);
 		foreach (sp, ssp->ss_states)
 		{
 			fill_state_struct(sp, ssp->value, ss_num);
 		}
-		printf("};\n");
+		gen_code("};\n");
 		ss_num++;
 	}
 }
@@ -228,83 +228,83 @@ static void gen_state_table(Expr *ss_list, uint num_event_flags, uint num_channe
 /* Generate a state struct */
 static void fill_state_struct(Expr *sp, char *ss_name, uint ss_num)
 {
-	printf("\t{\n");
-	printf("\t/* state name */        \"%s\",\n", sp->value);
-	printf("\t/* action function */   " NM_ACTION "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
-	printf("\t/* event function */    " NM_EVENT "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
-	printf("\t/* delay function */    " NM_DELAY "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
-	printf("\t/* entry function */    ");
+	gen_code("\t{\n");
+	gen_code("\t/* state name */        \"%s\",\n", sp->value);
+	gen_code("\t/* action function */   " NM_ACTION "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
+	gen_code("\t/* event function */    " NM_EVENT "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
+	gen_code("\t/* delay function */    " NM_DELAY "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
+	gen_code("\t/* entry function */    ");
 	if (sp->state_entry)
-		printf(NM_ENTRY "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
+		gen_code(NM_ENTRY "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
 	else
-		printf("0,\n");
-	printf("\t/* exit function */     ");
+		gen_code("0,\n");
+	gen_code("\t/* exit function */     ");
 	if (sp->state_exit)
-		printf(NM_EXIT "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
+		gen_code(NM_EXIT "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
 	else
-		printf("0,\n");
-	printf("\t/* event mask array */  " NM_MASK "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
-	printf("\t/* state options */     ");
+		gen_code("0,\n");
+	gen_code("\t/* event mask array */  " NM_MASK "_%s_%d_%s,\n", ss_name, ss_num, sp->value);
+	gen_code("\t/* state options */     ");
 	encode_state_options(sp->extra.e_state->options);
-	printf("\n\t},\n");
+	gen_code("\n\t},\n");
 }
 
 /* Generate the state option bitmask */
 static void encode_state_options(StateOptions options)
 {
-	printf("(0");
+	gen_code("(0");
 	if (!options.do_reset_timers)
-		printf(" | OPT_NORESETTIMERS");
+		gen_code(" | OPT_NORESETTIMERS");
 	if (!options.no_entry_from_self)
-		printf(" | OPT_DOENTRYFROMSELF");
+		gen_code(" | OPT_DOENTRYFROMSELF");
 	if (!options.no_exit_to_self)
-		printf(" | OPT_DOEXITTOSELF");
-	printf(")");
+		gen_code(" | OPT_DOEXITTOSELF");
+	gen_code(")");
 } 
 
 /* Generate a single program structure ("seqProgram") */
 static void gen_prog_table(Program *p)
 {
-	printf("\n/* Program table (global) */\n");
-	printf("seqProgram %s = {\n", p->name);
-	printf("\t/* magic number */      %d,\n", MAGIC);
-	printf("\t/* program name */      \"%s\",\n", p->name);
-	printf("\t/* channels */          " NM_CHANS ",\n");
-	printf("\t/* num. channels */     %d,\n", p->chan_list->num_elems);
-	printf("\t/* state sets */        " NM_STATESETS ",\n");
-	printf("\t/* num. state sets */   %d,\n", p->num_ss);
+	gen_code("\n/* Program table (global) */\n");
+	gen_code("seqProgram %s = {\n", p->name);
+	gen_code("\t/* magic number */      %d,\n", MAGIC);
+	gen_code("\t/* program name */      \"%s\",\n", p->name);
+	gen_code("\t/* channels */          " NM_CHANS ",\n");
+	gen_code("\t/* num. channels */     %d,\n", p->chan_list->num_elems);
+	gen_code("\t/* state sets */        " NM_STATESETS ",\n");
+	gen_code("\t/* num. state sets */   %d,\n", p->num_ss);
 	if (p->options.reent)
-		printf("\t/* user var size */     sizeof(struct %s),\n", NM_VARS);
+		gen_code("\t/* user var size */     sizeof(struct %s),\n", NM_VARS);
 	else
-		printf("\t/* user var size */     0,\n");
-	printf("\t/* param */             \"%s\",\n", p->param);
-	printf("\t/* num. event flags */  %d,\n", p->num_event_flags);
-	printf("\t/* encoded options */   "); encode_options(p->options);
-	printf("\t/* init func */         " NM_INIT ",\n");
-	printf("\t/* entry func */        %s,\n", p->prog->prog_entry ? NM_ENTRY : "0");
-	printf("\t/* exit func */         %s,\n", p->prog->prog_exit ? NM_EXIT : "0");
-	printf("\t/* num. queues */       %d\n", p->syncq_list->num_elems);
-	printf("};\n");
+		gen_code("\t/* user var size */     0,\n");
+	gen_code("\t/* param */             \"%s\",\n", p->param);
+	gen_code("\t/* num. event flags */  %d,\n", p->num_event_flags);
+	gen_code("\t/* encoded options */   "); encode_options(p->options);
+	gen_code("\t/* init func */         " NM_INIT ",\n");
+	gen_code("\t/* entry func */        %s,\n", p->prog->prog_entry ? NM_ENTRY : "0");
+	gen_code("\t/* exit func */         %s,\n", p->prog->prog_exit ? NM_EXIT : "0");
+	gen_code("\t/* num. queues */       %d\n", p->syncq_list->num_elems);
+	gen_code("};\n");
 }
 
 static void encode_options(Options options)
 {
-	printf("(0");
+	gen_code("(0");
 	if (options.async)
-		printf(" | OPT_ASYNC");
+		gen_code(" | OPT_ASYNC");
 	if (options.conn)
-		printf(" | OPT_CONN");
+		gen_code(" | OPT_CONN");
 	if (options.debug)
-		printf(" | OPT_DEBUG");
+		gen_code(" | OPT_DEBUG");
 	if (options.newef)
-		printf(" | OPT_NEWEF");
+		gen_code(" | OPT_NEWEF");
 	if (options.reent)
-		printf(" | OPT_REENT");
+		gen_code(" | OPT_REENT");
 	if (options.safe)
-		printf(" | OPT_SAFE");
+		gen_code(" | OPT_SAFE");
 	if (options.main)
-		printf(" | OPT_MAIN");
-	printf("),\n");
+		gen_code(" | OPT_MAIN");
+	gen_code("),\n");
 }
 
 /* Generate state set table, one entry for each state set */
@@ -313,22 +313,22 @@ static void gen_ss_table(Expr *ss_list)
 	Expr	*ssp;
 	int	num_ss;
 
-	printf("\n/* State set table */\n");
-	printf("static seqSS " NM_STATESETS "[] = {\n");
+	gen_code("\n/* State set table */\n");
+	gen_code("static seqSS " NM_STATESETS "[] = {\n");
 	num_ss = 0;
 	foreach (ssp, ss_list)
 	{
 		if (num_ss > 0)
-			printf("\n");
+			gen_code("\n");
 		num_ss++;
-		printf("\t{\n");
-		printf("\t/* state set name */    \"%s\",\n", ssp->value);
-		printf("\t/* states */            " NM_STATES "_%s,\n", ssp->value);
-		printf("\t/* number of states */  %d,\n", ssp->extra.e_ss->num_states);
-		printf("\t/* number of delays */  %d\n", ssp->extra.e_ss->num_delays);
-		printf("\t},\n");
+		gen_code("\t{\n");
+		gen_code("\t/* state set name */    \"%s\",\n", ssp->value);
+		gen_code("\t/* states */            " NM_STATES "_%s,\n", ssp->value);
+		gen_code("\t/* number of states */  %d,\n", ssp->extra.e_ss->num_states);
+		gen_code("\t/* number of delays */  %d\n", ssp->extra.e_ss->num_delays);
+		gen_code("\t},\n");
 	}
-	printf("};\n");
+	gen_code("};\n");
 }
 
 /* Generate event mask for a single state. The event mask has a bit set for each
diff --git a/src/snc/main.c b/src/snc/main.c
index 264e3d242306dae360b83bacac0f78aff6683645..cf7bc13d4550d14a24fd202911cb041273fbec28 100644
--- a/src/snc/main.c
+++ b/src/snc/main.c
@@ -25,64 +25,65 @@ in the file LICENSE that is included with this distribution.
 
 static Options options = DEFAULT_OPTIONS;
 
-static char *in_file;	/* input file name */
-static char *out_file;	/* output file name */
+static char *input_name;	/* input file name */
+static char *c_output_name;	/* c output file name */
+static char *h_output_name;	/* h output file name */
+
+static FILE *c_out, *h_out;	/* output file handles */
+static FILE *out = NULL;	/* current output file handle */
 
 static int err_cnt;
 
 static void parse_args(int argc, char *argv[]);
 static void parse_option(char *s);
 static void print_usage(void);
+static char *replace_extension(const char *in, const char *ext);
 
 /* The streams stdin and stdout are redirected to files named in the
    command parameters. */
 int main(int argc, char *argv[])
 {
-	FILE	*infp, *outfp;
+	FILE	*in;
 	Program	*prg;
         Expr    *exp;
 
 	/* Get command arguments */
 	parse_args(argc, argv);
 
-	/* Redirect input stream from specified file */
-	infp = freopen(in_file, "r", stdin);
-	if (infp == NULL)
+	in = fopen(input_name, "r");
+	if (in == NULL)
 	{
-		perror(in_file);
+		perror(input_name);
 		return EXIT_FAILURE;
 	}
-
-	/* Redirect output stream to specified file */
-	outfp = freopen(out_file, "w", stdout);
-	if (outfp == NULL)
+	c_out = fopen(c_output_name, "w");
+	if (c_out == NULL)
+	{
+		perror(c_output_name);
+		return EXIT_FAILURE;
+	}
+	h_out = fopen(h_output_name, "w");
+	if (h_out == NULL)
 	{
-		perror(out_file);
+		perror(h_output_name);
 		return EXIT_FAILURE;
 	}
 
-	/* stdin: the input file should be unbuffered,
+	/* the input file should be unbuffered,
            since the lexer maintains its own buffer */
-	setvbuf(stdin, NULL, _IONBF, 0);
-	/* stdout: the generated C program should be
-           block buffered with standard buffer size */
-	setvbuf(stdout, NULL, _IOFBF, BUFSIZ);
-	/* stderr: messages should be output immediately */
-	setvbuf(stderr, NULL, _IONBF, 0);
-
-	printf("/* Generated with snc from %s */\n", in_file);
+	setvbuf(in, NULL, _IONBF, 0);
 
-	exp = parse_program(in_file);
+	exp = parse_program(in, input_name);
 
         prg = analyse_program(exp, options);
 
 	if (err_cnt == 0)
-		generate_code(prg);
+		generate_code(prg, h_output_name);
 
 	return err_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
 }
 
-/* Initialize options, in_file, and out_file from arguments. */
+/* Initialize options, input_name, c_output_name, and h_output_name from arguments. */
 static void parse_args(int argc, char *argv[])
 {
 	int i;
@@ -108,13 +109,13 @@ static void parse_args(int argc, char *argv[])
 			else
 			{
 				i++;
-				out_file = argv[i];
+				c_output_name = argv[i];
 				continue;
 			}
 		}
 		else if (s[0] != '+' && s[0] != '-')
 		{
-			in_file = s;
+			input_name = s;
 			continue;
 		}
 		else
@@ -126,30 +127,30 @@ static void parse_args(int argc, char *argv[])
 		options.reent = TRUE;
 	}
 
-	if (!in_file)
+	if (!input_name)
 	{
 		report("no input file argument given\n");
 		print_usage();
 		exit(EXIT_FAILURE);
 	}
 
-	if (!out_file)	/* no -o option given */
+	if (!c_output_name)	/* no -o option given */
 	{
-		unsigned l = strlen(in_file);
-		char *ext = strrchr(in_file, '.');
-
-		if (ext && strcmp(ext,".st") == 0)
-		{
-			out_file = (char*)malloc(l);
-			strcpy(out_file, in_file);
-			strcpy(out_file+(ext-in_file), ".c\n");
-		}
-		else
-		{
-			out_file = (char*)malloc(l+3);
-			sprintf(out_file, "%s.c", in_file);
-		}
+		c_output_name = replace_extension(input_name, ".c");
 	}
+	h_output_name = replace_extension(c_output_name, ".h");
+}
+
+static char *replace_extension(const char *in, const char *ext)
+{
+	char *in_ext = strrchr(in, '.');
+	size_t ext_len = strlen(ext);
+	size_t in_len = in_ext ? (in_ext - in) : strlen(in);
+	char *out = (char*)malloc(in_len+ext_len+1);
+
+	strncpy(out, in, in_len);
+	strcpy(out+in_len, ext);
+	return out;
 }
 
 static void parse_option(char *s)
@@ -212,10 +213,31 @@ static void print_usage(void)
 	report("example:\n snc +a -c vacuum.st\n");
 }
 
+/* Code generation support */
+
+void set_gen_c(void)
+{
+	out = c_out;
+}
+
+void set_gen_h(void)
+{
+	out = h_out;
+}
+
 void gen_line_marker_prim(int line_num, const char *src_file)
 {
 	if (options.line)
-		printf("# line %d \"%s\"\n", line_num, src_file);
+		fprintf(out, "# line %d \"%s\"\n", line_num, src_file);
+}
+
+void gen_code(const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	vfprintf(out, format, args);
+	va_end(args);
 }
 
 /* Errors and warnings */
diff --git a/src/snc/main.h b/src/snc/main.h
index 274515b72c196b706152e04155b47ef288bed4e4..e6d1fe3a7e1c6893a3cdf94f4393b0649c7e17ac 100644
--- a/src/snc/main.h
+++ b/src/snc/main.h
@@ -14,6 +14,12 @@ in the file LICENSE that is included with this distribution.
 #define __attribute__(x)
 #endif
 
+void gen_code(const char *format, ...)
+__attribute__((format(printf,1,2)));
+
+void set_gen_c(void);
+void set_gen_h(void);
+
 /* append '# <line_num> "<src_file>"\n' to output (if not disabled by cmd-line option) */
 void gen_line_marker_prim(int line_num, const char *src_file);
 
diff --git a/src/snc/parser.h b/src/snc/parser.h
index 53c619d45bca6c2b6e9e1120ca8ad63ce17ff0ee..91364088f1330a2e0efd0442c4a43beef8c27ed0 100644
--- a/src/snc/parser.h
+++ b/src/snc/parser.h
@@ -10,9 +10,11 @@ in the file LICENSE that is included with this distribution.
 #ifndef INCLparserh
 #define INCLparserh
 
+#include <stdio.h>
+
 #include "types.h"
 
-Expr *parse_program(const char *src_file);
+Expr *parse_program(FILE *in, const char *src_file);
 
 void snlParser(
 	void    *yyp,		/* the parser */
diff --git a/src/snc/snl.re b/src/snc/snl.re
index db3a2c2540e3f17356b2c1c50467a96cee334a10..d29843410f980fabb358f590ba49fcb6436ecc58 100644
--- a/src/snc/snl.re
+++ b/src/snc/snl.re
@@ -56,6 +56,7 @@ typedef struct Scanner {
 	uchar	*eof;	/* pointer to (one after) last char in file (or 0) */
 	const char *file;	/* source file name */
 	int	line;	/* line number */
+	FILE	*in;
 } Scanner;
 
 static void scan_report(Scanner *s, const char *format, ...)
@@ -134,12 +135,12 @@ static uchar *fill(Scanner *s, uchar *cursor) {
 		}
 		/* fill the buffer, starting at s->lim, by reading a chunk of
 		   BSIZE bytes (or less if eof is encountered) */
-		if ((read_cnt = fread(s->lim, sizeof(uchar), BSIZE, stdin)) != BSIZE) {
-			if (ferror(stdin)) {
+		if ((read_cnt = fread(s->lim, sizeof(uchar), BSIZE, s->in)) != BSIZE) {
+			if (ferror(s->in)) {
 				perror("error reading input");
 				exit(EXIT_FAILURE);
 			}
-			if (feof(stdin)) {
+			if (feof(s->in)) {
 				s->eof = &s->lim[read_cnt];
 				/* insert sentinel and increase s->eof */
 				*(s->eof)++ = '\n';
@@ -456,7 +457,7 @@ c_code_line:
 	DONE;		/* dead code, only here to make compiler warning go away */
 }
 
-Expr *parse_program(const char *src_file)
+Expr *parse_program(FILE *in, const char *src_file)
 {
 	Scanner	s;
 	int	tt;		/* token type */
@@ -467,6 +468,7 @@ Expr *parse_program(const char *src_file)
 	memset(&s, 0, sizeof(s));
 	s.file = src_file;
 	s.line = 1;
+	s.in = in;
 
 	parser = snlParserAlloc(malloc);
 	do
diff --git a/src/snc/var_types.c b/src/snc/var_types.c
index f0d7a6edff40a850457296c182647af8576d690d..f50028440352cc524bac00f844979445187d5634 100644
--- a/src/snc/var_types.c
+++ b/src/snc/var_types.c
@@ -231,19 +231,19 @@ static void gen_array_pointer(Type *t, enum type_tag last_tag, char *name)
     switch (t->tag) {
     case T_POINTER:
         if (paren)
-            printf("(");
-        printf("*");
+            gen_code("(");
+        gen_code("*");
         gen_array_pointer(t->parent, t->tag, name);
         if (paren)
-            printf(")");
+            gen_code(")");
         break;
     case T_ARRAY:
         gen_array_pointer(t->parent, t->tag, name);
-        printf("[%d]", t->val.array.num_elems);
+        gen_code("[%d]", t->val.array.num_elems);
         break;
     default:
         if (name)
-            printf("%s", name);
+            gen_code("%s", name);
         break;
     }
 }
@@ -254,13 +254,13 @@ void gen_type(Type *t, char *name)
 
     switch (bt->tag) {
     case T_EVFLAG:
-        printf("evflag ");
+        gen_code("evflag ");
         break;
     case T_PRIM:
-        printf("%s ", prim_type_name[bt->val.prim]);
+        gen_code("%s ", prim_type_name[bt->val.prim]);
         break;
     case T_FOREIGN:
-        printf("%s%s ", foreign_type_prefix[bt->val.foreign.tag], bt->val.foreign.name);
+        gen_code("%s%s ", foreign_type_prefix[bt->val.foreign.tag], bt->val.foreign.name);
         break;
     default:
         assert(impossible);