/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *    		 Crown Copyright (c) 1997, 1998
 *    
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *    
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *    
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *    
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *    
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/producers/common/main.c,v 1.13 2004/08/21 15:31:42 stefanf Exp $
 */


#include "config.h"
#include "producer.h"
#include <limits.h>

#include "cstring.h"
#include "fmm.h"
#include "msgcat.h"
#include "ostream.h"
#include "tenapp.h"

#include "system.h"
#include "version.h"
#include "c_types.h"
#include "error.h"
#include "catalog.h"
#include "basetype.h"
#include "buffer.h"
#include "capsule.h"
#include "compile.h"
#include "constant.h"
#include "debug.h"
#include "declare.h"
#include "exception.h"
#include "file.h"
#include "dump.h"
#include "hash.h"
#include "initialise.h"
#include "instance.h"
#include "inttype.h"
#include "lex.h"
#include "literal.h"
#include "load.h"
#include "macro.h"
#include "namespace.h"
#include "operator.h"
#include "option.h"
#include "overload.h"
#include "parse.h"
#include "predict.h"
#include "preproc.h"
#include "printf.h"
#include "redeclare.h"
#include "statement.h"
#include "syntax.h"
#include "table.h"
#include "template.h"
#include "tok.h"
#include "tokdef.h"
#include "typeid.h"
#include "unmangle.h"
#include "ustring.h"
#include "variable.h"


/*
 *    DEFAULT MACHINE OPTION
 *
 *    This macro is used to determine the machine dependent options to
 *    be used.
 */

#ifndef FS_MACHINE
#ifdef FS_DOS
#define FS_MACHINE		"dos"
#else
#define FS_MACHINE		"unix"
#endif
#endif


/*
 *    COMMAND-LINE FLAGS
 *
 *    These flags may be set by command-line options to indicate various
 *    actions to the compiler.
 */

static int builtin_asserts = 1;
static int builtin_macros = 1;
static int check_level = 0;
static int complete_program = 0;
static int have_startup = 0;
static int spec_linker = 0;
static int started_scope = 0;
static int output_last = 1;
static int unmangle_names = 0;
static int quit_immediately = 0;
static string dump_name = NULL;
static string dump_opt = NULL;
static string spec_name = NULL;
static string table_name = NULL;


/*
 *    TABLE OF COMMAND-LINE ARGUMENTS
 *
 *    This table describes all the command-line options.  Each is given by
 *    an option letter, followed by a flag indicating whether the option can
 *    be split into two components, and a description of the second option
 *    component.
 */

typedef struct {
    char opt;
    char space;
    const char *arg;
    const char *desc;
} PROGRAM_ARG;

static PROGRAM_ARG prog_args [] = {
    { 'A', 1, "<pre>(<tok>)", "assert a predicate" },
    { 'D', 1, "<mac>=<def>", "define a macro" },
    { 'E', 0, NULL, "preprocess input file only" },
    { 'F', 1, "<file>", "read list of options from file" },
    { 'H', 0, NULL, "report file inclusions" },
    { 'I', 1, "<dir>", "specify an include directory" },
    { 'M', 1, "<mach>", "machine dependent options" },
    { 'N', 1, "<name>:<dir>", "specify a named include directory" },
    { 'O', 0, NULL, "optimise output capsule" },
    { 'Q', 0, NULL, "quit immediately" },
    { 'R', 0, NULL, "reverse order of files" },
    { 'S', 0, NULL, "spec file linker action" },
    { 'U', 1, "<mac>", "undefine a macro" },
    { 'V', 0, NULL, "print version number" },
    { 'W', 1, "<opt>", "enable warnings option" },
    { 'X', 0, NULL, "disable exception handling" },
    { 'Z', 1, "<number>", "set maximum number of errors" },
    { 'a', 0, NULL, "apply complete program checks" },
    { 'c', 0, NULL, "disable TDF output" },
    { 'd', 1, "<opt>=<file>", "dump symbol table" },
    { 'e', 1, "<file>", "add an end-up file" },
    { 'f', 1, "<file>", "add a start-up file" },
    { 'g', 0, "<level>", "enable diagnostics mode" },
    { 'h', 0, NULL, "print this help page" },
    { 'i', 1, "<opts>", "print dependencies for make" },
    { 'j', 1, "<opts>", "set TDF output options" },
    { 'm', 1, "<opts>", "set error printing options" },
    { 'n', 1, "<file>", "specify portability table" },
    { 'o', 1, "<output>", "specify output file" },
    { 'r', 1, "<file>", "specify error marker" },
    { 's', 1, "<file>", "specify spec output file" },
    { 't', 0, NULL, "output TDF token declarations" },
    { 'u', 0, NULL, "unmangle identifier names" },
    { 'v', 0, NULL, "enable verbose mode" },
    { 'w', 0, NULL, "disable all warnings" },
#ifdef DEBUG
    { 'x', 1, "<debug>", "debugging options" },
#endif
    { 'z', 0, NULL, "ignore compilation errors" },
    { '-', 0, NULL, "specify end of options" }
};

#define no_prog_args	ARRAY_SIZE (prog_args)


/*
 *    PRINT PROGRAM USAGE
 *
 *    This routine prints the program usage to the file f.
 */

static void
report_usage(FILE *f)
{
    int i;
    fprintf_v (f, "Usage: %s [options] [input] [output]\n", progname);
    for (i = 0 ; i < no_prog_args ; i++) {
		char opt = prog_args [i].opt;
		const char *arg = prog_args [i].arg;
		const char *desc = prog_args [i].desc;
		if (arg == NULL) arg = "\t";
		if (desc == NULL) desc = "(not documented)";
		fprintf_v (f, " -%c%s\t%s\n", opt, arg, desc);
    }
    fputc_v ('\n', f);
    return;
}


/*
 *    SET MACHINE DEPENDENT FLAGS
 *
 *    This routine sets the machine dependent options for the machine
 *    described by mach.  The routine returns false for an unknown machine
 *    descriptor.
 */

static int
set_machine(const char *mach)
{
    if (streq (mach, "dos")) {
		allow_dos_newline = 1;
		good_fseek = 0;
		binary_mode = 1;
		file_sep = '\\';
		drive_sep = ':';
		return (1);
    }
    if (streq (mach, "unix")) {
		allow_dos_newline = 0;
		good_fseek = 1;
		binary_mode = 0;
		file_sep = '/';
		drive_sep = 0;
		return (1);
    }
    return (0);
}


/*
 *    PRE-SET A COMPILE-TIME OPTION
 *
 *    This routine pre-sets the value of option A to be B.
 */

#define preset(A, B)\
    OPT_CATALOG [A].def [0] = (B) ;\
    OPT_CATALOG [A].def [1] = (B)


/*
 *    READ AN ARGUMENT FROM A FILE
 *
 *    This routine reads a command-line argument from the file f into the
 *    buffer s of length n, returning the result.
 */

static char
*read_arg(FILE *f, char *s, int n)
{
    char *p = fgets (s, n, f);
    if (p) {
		char c = *p;
		if (c == '#' || c == '\n') {
			/* Ignore empty lines and comments */
			p = read_arg (f, s, n);
		} else {
			/* Remove terminal newline */
			char *q = strrchr (p, '\n');
			if (q) *q = 0;
		}
    }
    return (p);
}


/*
 *    PROCESS A LIST OF ARGUMENTS
 *
 *    This routine is called by main to process the command-line arguments
 *    given by argc and argv.  It returns a list of strings giving the
 *    input and output files.
 */

static LIST (string)
process_args(int argc, char **argv)
{
    char opt = 0;
    FILE *f = NULL;
    char buff [1000];
    int allow_opt = 1;
    string output = NULL;
    LIST (string) files = NULL_list (string);
	
    /* Set development default options */
#ifdef DEBUG
    output_tokdec = 1;
#endif
	
    /* Scan through the arguments */
    for (;;) {
		char *arg = NULL;
		if (f != NULL) {
			/* Get next option from file */
			arg = read_arg (f, buff, (int) sizeof (buff));
			if (arg == NULL) {
				/* End of file reached */
				fclose_v (f);
				f = NULL;
			}
		}
		if (arg == NULL && argc) {
			/* Get next option from array */
			arg = *(argv++);
			argc--;
		}
		if (arg == NULL) {
			/* No more options */
			break;
		}
		if (opt == 0) {
			/* Find option letter */
			if (arg [0] == '-' && allow_opt) {
				opt = arg [1];
				if (opt) arg += 2;
			}
		}
		if (opt == 0) {
			/* Input or output file 'arg' */
			string uarg = ustrlit (string_copy (arg));
			CONS_string (uarg, files, files);
			
		} else {
			/* Command-line options */
			int i;
			int known = 0;
			char next_opt = 0;
			
			/* Search through options */
			for (i = 0 ; i < no_prog_args ; i++) {
				if (opt == prog_args [i].opt) {
					if (prog_args [i].arg) {
						/* Complex options */
						if (arg [0] == 0 && prog_args [i].space) {
							next_opt = opt;
							known = 1;
						} else {
							known = 2;
						}
					} else {
						/* Simple options */
						if (arg [0] == 0) {
							known = 2;
							break;
						}
					}
				}
			}
			
			/* Deal with complete options */
			if (known == 2) {
				string uarg = ustrlit (arg);
				switch (opt) {
					
				case 'A' : {
					/* Assertion */
					BUFFER *bf = &internal_buff;
					if (streq (arg, "-")) {
						builtin_asserts = 0;
						break;
					}
					bfprintf (bf, "#assert %x\n", arg);
					preset (OPT_ppdir_assert, OPTION_ALLOW);
					preset (OPT_ppdir_unassert, OPTION_ALLOW);
					preset (OPT_ppdir_assert_ignore, OPTION_OFF);
					preset (OPT_ppdir_unassert_ignore, OPTION_OFF);
					have_startup = 1;
					break;
				}
					
				case 'D' : {
					/* Macro definition */
					char c;
					const char *def = "1";
					BUFFER *bf = &internal_buff;
					bfprintf (bf, "#define ");
					while (c = *(arg++), c != 0) {
						if (c == '=') {
							def = arg;
							break;
						}
						bfputc (bf, (int) c);
					}
					bfprintf (bf, " %x\n", def);
					have_startup = 1;
					break;
				}
					
				case 'E' : {
					/* Preprocessor option */
					preproc_only = 1;
					break;
				}
					
				case 'F' : {
					/* Open option file */
					if (f != NULL) {
						const char *err = "Nested option files";
						error (ERROR_WARNING, err);
						break;
					}
					f = fopen (arg, "r");
					if (f == NULL) {
						const char *err = "Can't open option file '%s'";
						error (ERROR_WARNING, err, arg);
					}
					break;
				}
					
				case 'H' : {
					/* Inclusion reporting option */
					preset (OPT_include_verbose, OPTION_WARN);
					break;
				}
					
				case 'I' : {
					/* Include file search directory */
					uarg = ustring_copy (uarg);
					add_directory (uarg, NULL_string);
					break;
				}
					
				case 'M' : {
					/* Machine dependent options */
					known = set_machine (arg);
					break;
				}
					
				case 'N' : {
					/* Named include file search directory */
					string dir;
					uarg = ustring_copy (uarg);
					dir = ustrchr (uarg, ':');
					if (dir == NULL) {
						const char *err = "Bad '-%c' option";
						error (ERROR_WARNING, err, opt);
						add_directory (uarg, NULL_string);
					} else {
						*dir = 0;
						add_directory (dir + 1, uarg);
					}
					break;
				}
					
				case 'O' : {
					/* Optimisation mode */
					output_inline = 1;
					output_unused = 0;
					break;
				}
					
				case 'Q' : {
					/* Quit immediately */
					quit_immediately = 1;
					break;
				}

				case 'R' : {
					/* Reverse order of files */
					output_last = 0;
					break;
				}
					
				case 'S' : {
					/* Spec linker option */
					spec_linker = 1;
					break;
				}
					
				case 'U' : {
					/* Macro undefinition */
					BUFFER *bf = &internal_buff;
					if (streq (arg, "-")) {
						builtin_macros = 0;
						break;
					}
					bfprintf (bf, "#undef %x\n", arg);
					have_startup = 1;
					break;
				}
					
				case 'V' : {
					/* Print version number */
					string v = report_version (1);
					write_fmt (msg_stream, "%s\n\n", strlit (v));
					break;
				}
					
				case 'W' : {
					/* Switch on checks */
					char c;
					const char *sev = "warning";
					BUFFER *bf = &internal_buff;
					if (streq (arg, "all")) {
						check_level = 1;
						break;
					}
					if (!started_scope) {
						bfprintf (bf, "#pragma TenDRA begin\n");
						started_scope = 1;
					}
					bfprintf (bf, "#pragma TenDRA option \"");
					while (c = *(arg++), c != 0) {
						if (c == '=') {
							sev = arg;
							break;
						}
						bfputc (bf, (int) c);
					}
					bfprintf (bf, "\" %x\n", sev);
					have_startup = 1;
					break;
				}
					
				case 'X' : {
					/* Disable exception handling */
					output_except = 0;
					break;
				}
					
				case 'Z' : {
					/* Set maximum number of errors */
					BUFFER *bf = &internal_buff;
					if (!started_scope) {
						bfprintf (bf, "#pragma TenDRA begin\n");
						started_scope = 1;
					}
					bfprintf (bf, "#pragma TenDRA set error limit");
					bfprintf (bf, " %x\n", uarg);
					have_startup = 1;
					break;
				}
					
				case 'a' : {
					/* Complete program checks */
					complete_program = 1;
					break;
				}
					
				case 'c' : {
					/* Disable TDF output */
					output_capsule = 0;
					break;
				}
					
				case 'd' : {
					/* Dump file */
					string dump;
					uarg = ustring_copy (uarg);
					dump = ustrchr (uarg, '=');
					if (dump == NULL) {
						const char *err = "Bad '-%c' option";
						error (ERROR_WARNING, err, opt);
						break;
					}
					if (dump_name) {
						const char *err = "Multiple dump files";
						error (ERROR_WARNING, err);
					}
					dump_opt = uarg;
					dump_name = dump + 1;
					break;
				}
					
				case 'e' : {
					/* End-up file */
					LIST (string) p;
					uarg = ustring_copy (uarg);
					CONS_string (uarg, NULL_list (string), p);
					endup_files = APPEND_list (endup_files, p);
					have_startup = 1;
					break;
				}
					
				case 'f' : {
					/* Start-up file */
					LIST (string) p;
					uarg = ustring_copy (uarg);
					CONS_string (uarg, NULL_list (string), p);
					startup_files = APPEND_list (startup_files, p);
					have_startup = 1;
					break;
				}
					
				case 'g' : {
					/* Diagnostics mode */
					if (uarg [0] == 0) {
						output_diag = DIAG_VERSION;
					} else if (uarg [1] == 0) {
						switch (uarg [0]) {
						case '0' : output_diag = 0 ; break;
						case '1' : output_diag = 1 ; break;
						case '2' : output_diag = 2 ; break;
						default : known = 0 ; break;
						}
					} else {
						known = 0;
					}
					break;
				}
					
				case 'h' : {
					/* Help option */
					report_usage (msg_stream->file);
					break;
				}
					
				case 'i' : {
					/* Generate makefile dependencies */
					if (streq (arg, "M")) {
						inclusion_dependencies = DEP_ALL;
					} else if (streq (arg, "MM")) {
						inclusion_dependencies = DEP_NO_SYSTEM;
					} else if (arg [0] == 'O') {
						inclusion_obj_suffix = arg + 1;
					} else {
						known = 0;
					}
					if (known != 0) preproc_only = 1;
					break;
				}
					
				case 'j' : {
					/* Output options */
					output_option (uarg);
					break;
				}
					
				case 'm' : {
					/* Error options */
					error_option (uarg);
					break;
				}
					
				case 'n' : {
					/* Portability table */
					if (table_name) {
						const char *err = "Multiple portability tables";
						error (ERROR_WARNING, err);
					}
					table_name = ustring_copy (uarg);
					break;
				}
					
				case 'o' : {
					/* Output file */
					if (output) {
						const char *err = "Multiple output files";
						error (ERROR_WARNING, err);
					}
					output = ustring_copy (uarg);
					break;
				}
					
				case 'r' : {
					/* Error marker file */
					output_name [ OUTPUT_ERROR ] = ustring_copy (uarg);
					break;
				}
					
				case 's' : {
					/* Spec output file */
					if (spec_name) {
						const char *err = "Multiple spec output files";
						error (ERROR_WARNING, err);
					}
					spec_name = ustring_copy (uarg);
					break;
				}
					
				case 't' : {
					/* Output TDF token declarations */
					output_tokdec = 1;
					break;
				}
					
				case 'u' : {
					/* Unmangle an identifier name */
					unmangle_names = 1;
					break;
				}
					
				case 'v' : {
					/* Switch on verbose mode */
					verbose = 1;
					break;
				}
					
				case 'w' : {
					/* Suppress all warnings */
					preset (OPT_warning, OPTION_OFF);
					check_level = 0;
					break;
				}
					
				case 'z' : {
					/* Ignore compilation errors */
					preset (OPT_error, OPTION_WARN);
					max_errors = ULONG_MAX;
					break;
				}
					
				case '-' : {
					/* Last option */
					allow_opt = 0;
					break;
				}
					
#ifdef DEBUG
				case 'x' : {
					/* Debug options */
					debug_option (arg);
					quit_immediately = 1;
					break;
				}
#endif
				}
			}
			
			/* Deal with unknown options */
			if (known == 0) {
				const char *err = "Unknown option, '-%c%s'";
				error (ERROR_WARNING, err, opt, arg);
			}
			opt = next_opt;
		}
    }
    if (opt) {
		const char *err = "Incomplete option, '-%c'";
		error (ERROR_WARNING, err, opt);
    }
	
    /* Examine list of files */
    if (IS_NULL_list (files)) {
		CONS_string (NULL, NULL_list (string), files);
    }
    if (output || IS_NULL_list (TAIL_list (files))) {
		CONS_string (output, files, files);
		output_last = 1;
    }
    files = REVERSE_list (files);
    return (files);
}


/*
 *    INITIALISE FILE LOCATIONS
 *
 *    This routine initialises the standard file locations.
 */

static void
init_loc()
{
    IGNORE set_crt_loc (ustrlit ("<builtin-in>"), 1);
    builtin_loc = crt_loc;
    IGNORE set_crt_loc (ustrlit ("<command-line>"), 1);
    preproc_loc = crt_loc;
    decl_loc = crt_loc;
    stmt_loc = crt_loc;
    return;
}


/*
 *    PERFORM LANGUAGE DEPENDENT INITIALISATION
 *
 *    This routine performs any language dependent initialisation not
 *    covered in the main initialisation routines.  Note that the default
 *    language is C++.
 */

static void
init_lang(int c)
{
    if (c) {
		crt_linkage = dspec_c;
		set_std_namespace (NULL_id);
    }
    init_tok (c);
    return;
}


/*
 *    CALL MAIN INITIALISATION ROUTINES
 *
 *    This routine calls all the initialisation routines.
 */

static void
init_main()
{
    /* Set file locations */
    crt_loc = builtin_loc;
    decl_loc = builtin_loc;
    stmt_loc = builtin_loc;
    preproc_loc = builtin_loc;
	
    /* Initialise errors and options */
    init_hash ();
    init_lang (LANGUAGE_C);
    init_catalog ();
    init_option (check_level);
    if (output_diag) record_location = 1;
    init_dump (dump_name, dump_opt);
    if (do_header) dump_start (&crt_loc, NULL);
	
    /* Initialise macros and keywords */
    init_char ();
    init_constant ();
    init_keywords ();
    init_macros (builtin_macros, builtin_asserts);
	
    /* Read the portability table */
    read_table (table_name);
	
    /* Initialise types and namespaces */
    init_namespace ();
    push_namespace (global_namespace);
    init_types ();
    init_literal ();
    init_exception ();
    init_printf ();
    init_initialise ();
    init_token_args ();
    init_templates ();
    if (do_header) dump_end (&crt_loc);
    return;
}


/*
 *    CALL MAIN TERMINATION ROUTINES
 *
 *    This routine frees any memory unneeded after the parsing routines.
 */

static void
term_main()
{
    free_candidates (&candidates);
    free_buffer (&field_buff);
    free_buffer (&token_buff);
    term_macros ();
    term_input ();
    term_catalog ();
    term_itypes ();
    return;
}


/*
 *    CALL MAIN FILE PROCESSING ROUTINES
 *
 *    This routine calls the main routines for processing the given list
 *    of files.  This will have at least two elements, the last of which is
 *    the output file.
 */

static void
process_files(LIST (string) files)
{
    /* Find output file */
    int action;
    unsigned len;
    string output;
    PARSE_STATE st;
    unsigned max_len = 2;
    int mode = text_mode;
    LIST (string) input;
	
    /* Find output file */
    if (output_last) {
		LIST (string) end = END_list (files);
		output = DEREF_string (HEAD_list (end));
		input = files;
    } else {
		output = DEREF_string (HEAD_list (files));
		input = TAIL_list (files);
    }
    output_name [ OUTPUT_TDF ] = output;
    output_name [ OUTPUT_SPEC ] = spec_name;
	
    /* Find processing action */
    save_state (&st, 0);
    crt_state_depth = 0;
    if (spec_linker) {
		output_capsule = 0 ;	/* remove later */
		init_capsule ();
		max_len = 0;
		action = 2;
    } else if (preproc_only) {
		output_name [ OUTPUT_PREPROC ] = output;
		output_tdf = 0;
		output_capsule = 0;
		action = 1;
    } else {
		init_capsule ();
		action = 0;
    }
	
    /* Check number of input files */
    len = LENGTH_list (files);
    if (max_len && len > max_len) {
		const char *err = "Too many arguments (should have %u)";
		error (ERROR_WARNING, err, max_len);
		len = max_len;
    }
	
    /* Process start-up files in spec linker mode */
    if (action == 2) {
		if (have_startup) {
			IGNORE open_input (mode);
			crt_loc = builtin_loc;
			input_name = NULL;
			input_file = NULL;
			process_file ();
			close_input ();
		}
		mode = binary_mode;
    }
	
    /* Scan through input files */
    while (len >= 2) {
		/* Open input file */
		input_name = DEREF_string (HEAD_list (input));
		if (!open_input (mode)) {
			fail (ERR_fail_input (input_name));
			tenapp_exit ();
			return;
		}
		init_diag ();
		
		/* Process input file */
		switch (action) {
	    case 0 : {
			/* Parsing action */
			process_file ();
			break;
	    }
	    case 1 : {
			/* Preprocessing action */
			preprocess_file ();
			break;
	    }
	    case 2 : {
			/* Spec linker action */
			if (read_spec ()) have_syntax_error = 1;
			no_declarations++;
			break;
	    }
		}
		
		/* Close input file */
		clear_decl_blocks ();
		close_input ();
		input = TAIL_list (input);
		len--;
    }
	
    /* Write output file */
    if (have_syntax_error) {
		suppress_variable = 2;
		no_declarations++;
    }
    init_diag ();
    preproc_loc = crt_loc;
    restore_state (&st);
    if (action == 1) {
		/* Preprocessing action */
		term_option ();
    } else {
		/* Compilation actions */
		external_declaration (NULL_exp, 0);
		clear_templates (2);
		term_main ();
		IGNORE check_global (complete_program);
		term_option ();
		compile_pending ();
		write_capsule ();
    }
    return;
}


/*
 *    MAIN ROUTINE
 *
 *    This is the main routine.  It processes the command-line options,
 *    calls the initialisation routines and calls the main processing
 *    routine.
 */

int
main(int argc, char **argv)
{
    LIST (string) files;

	tenapp_init (argc, argv, "producer", PROG_VERSION);
	tenapp_add_eh (exit_handler);
    IGNORE set_machine (FS_MACHINE);
    init_loc ();
    files = process_args (argc - 1, argv + 1);
    builtin_startup ();
    if (!quit_immediately) {
		/* Process files */
		init_main ();
		if (unmangle_names) {
			unmangle_list (files, stdout, 1);
		} else {
			process_files (files);
			tenapp_exit ();
		}
    }
    return (exit_status);
}
