/*
 * Copyright (c) 1995, 1996, 1997, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <rpc/types.h>
#include <rpc/xdr.h>

#include <mom/mint.h>
#include <mom/pres_c.h>
#include <mom/compiler.h>
#include <mom/c/libpres_c.h>

#include "lexxer.h"
#include "xlate_util.h"
#include "global.h"
#include "type.h"
#include "error.h"

char *progname;

static char *cpp_args = NULL;
static int cpp_args_size = 0;
static int cpp_args_length = 0;

extern void translate(); /* Translation of types, routines */
extern int yyparse();
void gen_error_mappings( pres_c_1 * );

extern int yydebug;
extern FILE *yyin;

extern char *ihead_name;
int make_ihead;


char *infilename;

FILE *fin;
FILE *fout;

pres_c_1 out_pres_c;

int gen_client = 1;
int gen_server = 1;

extern void make_iheader(void);

static FILE *f_open(const char *file_name, const char *mode)
{
	FILE *ret;
	if ( (ret = fopen(file_name,mode)) )
		return ret;
	else
		perror(file_name);

	return NULL;
}

static void add_to_cpp_args(char *arg);

/*
 * Open input file, run thorugh C preprocessor  
 */
static void open_input(char *infile)
{
	infilename = (infile == NULL) ? "<stdin>" : infile;
	
	yyin = call_c_preprocessor(infile, cpp_args);
	if (!yyin)
		panic("Can't open file `%s' for reading.", infilename);
}

/*
 * Print the standard usage to stderr
 */
static void print_usage(void)
{
    fprintf(stderr,"Flick MIG IDL front end usage:\n");
    fprintf(stderr,"\tflick_fe_mig [<options>] [<infile>]\n");
    fprintf(stderr,"If <infile> is unspecified, defaults to stdin.\n");
    fprintf(stderr,"Options:\n");
    fprintf(stderr,"\t -?, -h, --help: Print this message.\n");
    fprintf(stderr,"\t -v, -V, --version: Print program version number.\n");
    fprintf(stderr,"\t -c, --client: Generate client presentation only.\n");
    fprintf(stderr,"\t\t (Default is both)\n");
    fprintf(stderr,"\t -s, --server: Generate server presentation only.\n");
    fprintf(stderr,"\t\t (Default is both)\n");
    fprintf(stderr,"\t -o<file>, -o <file>, --output <file>: Output to <file>.\n");
    fprintf(stderr,"\t\t Defaults to <infile>.prc, stdout if <infile> unspecified.\n");

    fprintf(stderr,"\t-I<dir>: Specify include directories for cpp.\n");
    fprintf(stderr,"\t-D<sym>[=<value>]: Define <sym> to be <value> (Defaults to 1)\n");
    fprintf(stderr,"\t-U<sym>: Undefine symbol.\n");
    fprintf(stderr,"\t-Xcpp <option>: Pass <option> to cpp.\n");

    exit(1);
}

static void print_version(void)
{
    fprintf(stderr,"Flick MIG IDL front end, version %s.\n",FLICK_VERSION);
    exit(0);
}

/* 
 * Add this string as an argument for the C preprocessor.  We aggregrate all of
 * the arguments into a single string, which is later pulled apart by the
 * `call_c_preprocessor' function.  We should probably have an interface to the
 * library that takes an argv instead.
 */
static void add_to_cpp_args(char *arg)
{
	int arg_length;
	
	if (!arg)
		return;
	
	arg_length = strlen(arg);
	
	if ((cpp_args_length + 1 /* SPC */ + arg_length + 1 /* NUL */)
	    > cpp_args_size) {
		cpp_args_size = cpp_args_length + 1 + arg_length + 1
				+ 256 /* extra */;
		cpp_args = (char *) mustrealloc(cpp_args, cpp_args_size);
	}
	
	if (cpp_args_length <= 0) {
		strcpy(cpp_args, arg);
		cpp_args_length = arg_length;
	} else {
		strcat(cpp_args, " ");
		strcat(cpp_args, arg);
		cpp_args_length += 1 /* SPC */ + arg_length;
	}
}

int main(int argc, char **argv)
{
    int i;

    /* Which command line args did we see, didn't see. */
    int seen_in = 0;
    int seen_client = 0;
    int seen_server = 0;
    int seen_output = 0;

    char *inname = NULL;
    
    char *outname = NULL;
    
    ihead_name = NULL;
	
    progname = argv[0];
    
    set_program_name(progname); /* for fatal error messages */

    /* Parse the args */

    for (i = 1; i < argc; i++)
	{
	    if (argv[i][0] == '-')
		{
		    switch (argv[i][1])
			{
			case '-':

			    if (strcmp(argv[i],"--help") == 0)
				{
				    print_usage();
				    exit(0);
				}
			    
			    else if (strcmp(argv[i],"--version") == 0)
				{
				    print_version();
				    exit(0);
				}

			    else if (strcmp(argv[i],"--client") == 0)
				{
				    if (seen_client)
					    panic("Client option already seen.");
				    gen_server = 0;
				    
				    seen_client = 1;
				}
			    
			    else if (strcmp(argv[i],"--server") == 0)
				{
				    if (seen_server)
					    panic("Server option already seen.");
				    gen_client = 0;
				    
				    seen_server = 1;
				}
			    
			    else if (strcmp(argv[i],"--output") == 0)
				{
				    if (seen_output)
					    panic("Output option already seen.");
				    if( ++i == argc || argv[i][0] == '-')
					    panic("Output option must be followed by a filename.");
				    else
					outname = argv[i];
				}
			    else
				{
				    fprintf(stderr,
					    "Unknown option `%s'seen.\n\n",
					    argv[i]);
				    print_usage();
				    exit(1);
				}

			    break;
			    			    
			case '?':
			case 'h':
			
			    print_usage();
			    exit(0);
			    
			    break;

			case 'v':
			case 'V':
				
			    print_version();
			    exit(0);
			    
			    break;
			    
			case 'c':
			    if (seen_client)
				    panic("Client option already seen.");
			    
			    gen_server = 0;
			    
			    seen_client = 1;
			    
			    break;
			    
			case 's':
			    if (seen_server)
				    panic("Server option already seen.");
			    
			    gen_client = 0;
			    
			    seen_server = 1;
			    
			    break;
			    
			case 'o':

			    if (seen_output)
				    panic("Output option already seen.");
			    
			    if (strlen(argv[i]) > 2)
				outname = argv[i] + 2;
			    else
				{
				    if (++i == argc || argv[i][0] == '-')
					    panic("Output option must be followed by a filename.");
				    else
					outname = argv[i];
				}
				
			    seen_output = 1;
			    
			    break;

			case 'I':
			case 'D':
			case 'U':
			
			    add_to_cpp_args(argv[i]);
			    
			    break;

			case 'X':
			    if (strcmp(argv[i],"-Xcpp"))
				{
				    fprintf(stderr,
					    "Unknown option `%s' seen.\n\n",
					    argv[i]);
				    
				    print_usage();
				    exit(1);
				}
			    else
				{
				    add_to_cpp_args(argv[i+1]);
				    i++;
				}
								  
			    break;

			case 'i':

				if (strcmp(argv[i],"-iheader"))
				    {
				        fprintf(stderr,"Unknown option `%s' seen", argv[i]);
					print_usage();
					exit(1);
				    }

				if( ++i == argc || argv[i][0] == '-')
				    panic("Output option must be followed by a filename.");
				
				ihead_name = argv[i];
				make_ihead = 1;
				
				break;

			default:
			    
			    fprintf(stderr,"Unknown option `%s' seen.\n\n",
				    argv[i]);
				
			    print_usage();
			    exit(1);
			    break;
			}
		}
	    else
		{
		    if (seen_in)
			    panic("Input file already seen.");

		    inname = argv[i];
		    seen_in = 1;
		}
	}

    /* Open up I/O. */

    open_input(inname);

    if (outname)
	{
	    fout = f_open(outname,"wb");
	}
    else
	{
	    if (inname)
		{
		    outname = resuffix(inname,".prc");
		    fout = f_open(outname,"wb");
		}
	    else
		fout = stdout;
	}
    
    init_global();
    init_meta(&out_pres_c.meta_data);
    meta_add_channel(&out_pres_c.meta_data,
		     meta_add_file(&out_pres_c.meta_data, "(generated)",
				   IO_FILE_INPUT),
		     "");
    builtin_file = meta_add_file(&out_pres_c.meta_data, "(builtin)",
				 IO_FILE_BUILTIN);
    /*
     * We strip off any leading path on `inname'.  We don't want PRES_C
     * files to differ based on the path to the input file.
     */
    root_filename = (inname == NULL) ? "<stdin>" : muststrdup(inname);
    root_file = meta_add_file(&out_pres_c.meta_data,
			      file_part(root_filename),
			      IO_FILE_INPUT | IO_FILE_ROOT);
    init_type();
    LookNormal();
    yyparse();
    
    if (errors > 0)
	    fatal(errors==1 ? "%d error found" : "%d errors found", errors);

    if (make_ihead)
	    make_iheader();

    out_pres_c.a.defs.defs_len = 0;
    out_pres_c.a.defs.defs_val = 0;
    out_pres_c.pres_context = "mig"; /* Name of PG style. */
    out_pres_c.error_mappings.error_mappings_len = 0;
    out_pres_c.error_mappings.error_mappings_val = 0;
    init_channel_maps(&out_pres_c);
    for( i = 0; i < out_pres_c.meta_data.files.files_len; i++ ) {
	map_file_channels(&out_pres_c, i);
    }
    gen_error_mappings( &out_pres_c );
    
    build_init_cast();
    translate();
    pres_c_1_writefh(&out_pres_c,fout);

    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1