//                              -*- Mode: C++ -*- 
// 
// CForall Version 1.0, Copyright (C) Peter A. Buhr 2002
// 
// cfa.cc -- 
// 
// Author           : Peter A. Buhr
// Created On       : Tue Aug 20 13:44:49 2002
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue Nov 11 09:19:15 2014
// Update Count     : 106
// 

#include <iostream>
#include <cstdio>					// perror
#include <cstdlib>					// putenv, exit
#include <unistd.h>					// execvp
#include <string>					// STL version

#include "config.h"					// configure info

using std::cerr;
using std::endl;
using std::string;


//#define __DEBUG_H__


bool prefix( string arg, string pre ) {
    return arg.substr( 0, pre.size() ) == pre;
} // prefix


void shuffle( const char *args[], int S, int E, int N ) {
    // S & E index 1 passed the end so adjust with -1
#ifdef __DEBUG_H__
    cerr << "shuffle:" << S << " " << E << " " << N << endl;
#endif // __DEBUG_H__
    for ( int j = E-1 + N; j > S-1 + N; j -=1 ) {
#ifdef __DEBUG_H__
	cerr << "\t" << j << " " << j-N << endl;
#endif // __DEBUG_H__
	args[j] = args[j-N];
    } // for
} // shuffle


int main( int argc, char *argv[] ) {
    string Version( VERSION );				// current version number from CONFIG
    string Major( "0" ), Minor( "0" ), Patch( "0" );	// default version numbers
    int posn1 = Version.find( "." );			// find the divider between major and minor version numbers
    if ( posn1 == -1 ) {				// not there ?
	Major = Version;
    } else {
	Major = Version.substr( 0, posn1 );
	int posn2 = Version.find( ".", posn1 + 1 );	// find the divider between minor and patch numbers
	if ( posn2 == -1 ) {				// not there ?
	    Minor = Version.substr( posn1 );
	} else {
	    Minor = Version.substr( posn1 + 1, posn2 - posn1 - 1 );
	    Patch = Version.substr( posn2 + 1 );
	} // if
    } // if

    string installincdir( CFA_INCDIR );			// fixed location of cc1 and cfa-cpp commands
    string installlibdir( CFA_LIBDIR );			// fixed location of include files

    string heading;					// banner printed at start of cfa compilation
    string arg;						// current command-line argument during command-line parsing
    string Bprefix;					// path where gcc looks for compiler command steps
    string langstd;					// language standard

    string compiler_path( GCC_PATH );			// path/name of C compiler
    string compiler_name;				// name of C compiler

    bool nonoptarg = false;				// indicates non-option argument specified
    bool link = true;					// linking as well as compiling
    bool verbose = false;				// -v flag
    bool quiet = false;					// -quiet flag
    bool debug = true;					// -debug flag
    bool help = false;					// -help flag
    bool CFA_flag = false;				// -CFA flag
    bool cpp_flag = false;				// -E or -M flag, preprocessor only
    bool debugging = false;				// -g flag

    const char *args[argc + 100];			// cfa command line values, plus some space for additional flags
    int sargs = 1;					// starting location for arguments in args list
    int nargs = sargs;					// number of arguments in args list; 0 => command name

    const char *libs[argc + 20];			// non-user libraries must come separately, plus some added libraries and flags
    int nlibs = 0;

#ifdef __DEBUG_H__
    cerr << "CFA:" << endl;
#endif // __DEBUG_H__

    // process command-line arguments

    for ( int i = 1; i < argc; i += 1 ) {
#ifdef __DEBUG_H__
	cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	arg = argv[i];					// convert to string value
#ifdef __DEBUG_H__
	cerr << "arg:\"" << arg << "\"" << endl;
#endif // __DEBUG_H__
	if ( prefix( arg, "-" ) ) {
	    // pass through arguments

	    if ( arg == "-Xlinker" || arg == "-o" ) {
		args[nargs] = argv[i];			// pass the argument along
		nargs += 1;
		i += 1;
		if ( i == argc ) continue;		// next argument available ?
		args[nargs] = argv[i];			// pass the argument along
		nargs += 1;
	    } else if ( arg == "-XCFA" ) {		// CFA pass through
		i += 1;
		args[nargs] = ( *new string( string("-D__CFA_FLAG__=") + argv[i] ) ).c_str();
		nargs += 1;

	    // CFA specific arguments

	    } else if ( arg == "-CFA" ) {
		CFA_flag = true;			// strip the -CFA flag
		link = false;
		args[nargs] = "-E";			// replace the argument with -E
		nargs += 1;
	    } else if ( arg == "-debug" ) {
		debug = true;				// strip the debug flag
	    } else if ( arg == "-nodebug" ) {
		debug = false;				// strip the nodebug flag
	    } else if ( arg == "-quiet" ) {
		quiet = true;				// strip the quiet flag
	    } else if ( arg == "-noquiet" ) {
		quiet = false;				// strip the noquiet flag
	    } else if ( arg == "-help" ) {
		help = true;				// strip the help flag
	    } else if ( arg == "-nohelp" ) {
		help = false;				// strip the nohelp flag
	    } else if ( arg == "-compiler" ) {
		// use the user specified compiler
		i += 1;
		if ( i == argc ) continue;		// next argument available ?
		compiler_path = argv[i];
		if ( putenv( (char *)( *new string( string( "__U_COMPILER__=" ) + argv[i]) ).c_str() ) != 0 ) {
		    cerr << argv[0] << " error, cannot set environment variable." << endl;
		    exit( EXIT_FAILURE );
		} // if

	    // C++ specific arguments

	    } else if ( arg == "-v" ) {
		verbose = true;				// verbosity required
		args[nargs] = argv[i];			// pass the argument along
		nargs += 1;
	    } else if ( arg == "-g" ) {
		debugging = true;			// symbolic debugging required
		args[nargs] = argv[i];			// pass the argument along
		nargs += 1;
	    } else if ( prefix( arg, "-B" ) ) {
		Bprefix = arg.substr(2);		// strip the -B flag
		args[nargs] = ( *new string( string("-D__GCC_BPREFIX__=") + Bprefix ) ).c_str();
		nargs += 1;
	    } else if ( prefix( arg, "-b" ) ) {
		if ( arg.length() == 2 ) {		// separate argument ?
		    i += 1;
		    if ( i == argc ) continue;		// next argument available ?
		    arg += argv[i];			// concatenate argument
		} // if
		// later versions of gcc require the -b option to appear at the start of the command line
		shuffle( args, sargs, nargs, 1 );	// make room at front of argument list
		args[sargs] = ( *new string( arg ) ).c_str(); // pass the argument along
		if ( putenv( (char *)( *new string( string( "__GCC_MACHINE__=" ) + arg ) ).c_str() ) != 0 ) {
		    cerr << argv[0] << " error, cannot set environment variable." << endl;
		    exit( EXIT_FAILURE );
		} // if
		sargs += 1;
		nargs += 1;
	    } else if ( prefix( arg, "-V" ) ) {
		if ( arg.length() == 2 ) {		// separate argument ?
		    i += 1;
		    if ( i == argc ) continue;		// next argument available ?
		    arg += argv[i];			// concatenate argument
		} // if
		// later versions of gcc require the -V option to appear at the start of the command line
		shuffle( args, sargs, nargs, 1 );	// make room at front of argument list
		args[sargs] = ( *new string( arg ) ).c_str(); // pass the argument along
		if ( putenv( (char *)( *new string( string( "__GCC_VERSION__=" ) + arg ) ).c_str() ) != 0 ) {
		    cerr << argv[0] << " error, cannot set environment variable." << endl;
		    exit( EXIT_FAILURE );
		} // if
		sargs += 1;
		nargs += 1;
	    } else if ( arg == "-c" || arg == "-S" || arg == "-E" || arg == "-M" || arg == "-MM" ) {
		args[nargs] = argv[i];			// pass the argument along
		nargs += 1;
		if ( arg == "-E" || arg == "-M" || arg == "-MM" ) {
		    cpp_flag = true;			// cpp only
		} // if
		link = false;                           // no linkage required
	    } else if ( arg[1] == 'l' ) {
		// if the user specifies a library, load it after user code
		libs[nlibs] = argv[i];
		nlibs += 1;
	    } else {
		// concatenate any other arguments
		args[nargs] = argv[i];
		nargs += 1;
	    } // if
	} else {
	    // concatenate other arguments
	    args[nargs] = argv[i];
	    nargs += 1;
	    nonoptarg = true;
	} // if
    } // for

#ifdef __DEBUG_H__
    cerr << "args:";
    for ( int i = 1; i < nargs; i += 1 ) {
	cerr << " " << args[i];
    } // for
    cerr << endl;
#endif // __DEBUG_H__

    if ( cpp_flag && CFA_flag ) {
	cerr << argv[0] << " error, cannot use -E and -CFA flags together." << endl;
	exit( EXIT_FAILURE );
    } // if

    string d;
    if ( debug ) {
	d = "-d";
    } // if

    args[nargs] = "-I" CFA_INCDIR;
    nargs += 1;

    if ( link ) {
	// include the cfa library in case it's needed
	args[nargs] = "-L" CFA_LIBDIR;
	nargs += 1;
	args[nargs] = "-lcfa";
	nargs += 1;
    } // if

    // add the correct set of flags based on the type of compile this is

    args[nargs] = ( *new string( string("-D__CFA_MAJOR__=") + Major ) ).c_str();
    nargs += 1;
    args[nargs] = ( *new string( string("-D__CFA_MINOR__=") + Minor ) ).c_str();
    nargs += 1;

    if ( cpp_flag ) {
	args[nargs] = "-D__CPP__";
	nargs += 1;
    } // if

    if ( CFA_flag ) {
	args[nargs] = "-D__CFA__";
	nargs += 1;
    } // if

    if ( debug ) {
	heading += " (debug)";
	args[nargs] = "-D__CFA_DEBUG__";
	nargs += 1;
    } else {
	heading += " (no debug)";
    } // if

    if ( Bprefix.length() == 0 ) {
	Bprefix = installlibdir;
	args[nargs] = ( *new string( string("-D__GCC_BPREFIX__=") + Bprefix ) ).c_str();
	nargs += 1;
    } // if

    // execute the compilation command

    args[0] = compiler_path.c_str();			// set compiler command for exec
    // find actual name of the compiler independent of the path to it
    int p = compiler_path.find_last_of( '/' );		// scan r -> l for first '/'
    if ( p == -1 ) {
	compiler_name = compiler_path;
    } else {
	compiler_name = *new string( compiler_path.substr( p + 1 ) );
    } // if

    if ( prefix( compiler_name, "gcc" ) ) {		// allow suffix on gcc name
	args[nargs] = "-no-integrated-cpp";
	nargs += 1;
	args[nargs] = "-Wno-deprecated"; 
	nargs += 1;
	args[nargs] = ( *new string( string("-B") + Bprefix + "/" ) ).c_str();
	nargs += 1;
    } else {
	cerr << argv[0] << " error, compiler " << compiler_name << " not supported." << endl;
	exit( EXIT_FAILURE );
    } // if

    for ( int i = 0; i < nlibs; i += 1 ) {		// copy non-user libraries after all user libraries
	args[nargs] = libs[i];
	nargs += 1;
    } // for

    args[nargs] = NULL;					// terminate with NULL

#ifdef __DEBUG_H__
    cerr << "nargs: " << nargs << endl;
    cerr << "args:" << endl;
    for ( int i = 0; args[i] != NULL; i += 1 ) {
	cerr << " \"" << args[i] << "\"" << endl;
    } // for
#endif // __DEBUG_H__

    if ( ! quiet ) {
	cerr << "CFA " << "Version " << Version << heading << endl;

	if ( help ) {
	    cerr <<
		"-debug\t\t\t: use cfa runtime with debug checking" << endl <<
		"-help\t\t\t: print this help message" << endl <<
		"-quiet\t\t\t: print no messages from the cfa command" << endl <<
		"-CFA\t\t\t: run the cpp preprocessor and the cfa-cpp translator" << endl <<
		"-XCFA -cfa-cpp-flag\t: pass next flag as-is to the cfa-cpp translator" << endl <<
		"...\t\t\t: any other " << compiler_name << " flags" << endl;
	} // if
    } // if

    if ( verbose ) {
	if ( argc == 2 ) exit( EXIT_SUCCESS );		// if only the -v flag is specified, do not invoke gcc

	for ( int i = 0; args[i] != NULL; i += 1 ) {
	    cerr << args[i] << " ";
	} // for
	cerr << endl;
    } // if

    if ( ! nonoptarg ) {
	cerr << argv[0] << " error, no input files" << endl;
	exit( EXIT_FAILURE );
    } // if

    // execute the command and return the result

    execvp( args[0], (char *const *)args );		// should not return
    perror( "CFA Translator error: cfa level, execvp" );
    exit( EXIT_FAILURE );
} // main

// Local Variables: //
// compile-command: "make install" //
// End: //
