//                              -*- Mode: C++ -*- 
// 
// CForall Version 1.0, Copyright (C) Peter A. Buhr 2002
// 
// cpp.cc -- 
// 
// Author           : Peter A. Buhr
// Created On       : Thu Aug 29 12:24:06 2002
// Last Modified By : Peter A. Buhr
// Last Modified On : Sat Dec  6 08:31:49 2014
// Update Count     : 51
// 

#include <iostream>
#include <string>
#include <stdio.h>					// tempnam, freopen, perror
#include <unistd.h>					// execvp, fork, unlink
#include <sys/wait.h>					// wait

#include "../config.h"					// configure info

using namespace std;


//#define __DEBUG_H__


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

    string arg;
    string bprefix;

    string cpp_in;
    string cpp_out;
    string cpp;
    string cpp_name( "gcc" );

    bool CFA_flag = false;
    bool cpp_flag = false;
    bool gnu = false;

    const char *args[argc + 100];			// leave space for 100 additional cfa command line values
    int nargs = 1;					// 0 => command name
    const char *argsCppOnly[argc];			// cpp only arguments
    int nargsCppOnly = 0;
    const char *argsCFAOnly[argc];			// CFA only arguments
    int nargsCFAOnly = 0;

    // get a name of a temporary file

    char *tmpfile = tempnam( NULL, "CFA" );		// storage is not freed

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

    // process all the arguments

    for ( i = 1; i < argc; i += 1 ) {
#ifdef __DEBUG_H__
	cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	arg = argv[i];
#ifdef __DEBUG_H__
	cerr << "arg:\"" << arg << "\"" << endl;
#endif // __DEBUG_H__
	if ( arg.substr(0,1) == "-" ) {
	    if ( arg == "-D__CFA__" ) {
		CFA_flag = true;			// strip -D__CFA__ flag
	    } else if ( arg == "-D__CPP__" ) {
		cpp_flag = true;			// strip -D__CPP__ flag
	    } else if ( arg.substr(0,sizeof("-D__GNU")-1) == "-D__GNU" ) {
		gnu = true;				// strip -D__GNUxxx flags to remove duplication
	    } else if ( arg == "-lang-c" ) {		// strip -lang-c flag
	    } else if ( arg.substr(0,sizeof("-A")-1) == "-A" ) { // strip -A flags
	    } else if ( arg.substr(0,sizeof("-D__STDC_HOSTED__")-1) == "-D__STDC_HOSTED__" ) { // strip this define: causes conflict
	    } else if ( arg.substr(0,sizeof("-o")-1) == "-o" ) {
		i += 1;					// strip -o flag and its argument
	    } else if ( arg.substr(0,sizeof("-D__CFA_FLAG__")-1) == "-D__CFA_FLAG__" ) {
		argsCFAOnly[nargsCFAOnly] = ( *new string( arg.substr(sizeof("-D__CFA_FLAG__")) ) ).c_str(); // pass argument along
		nargsCFAOnly += 1;
	    } else if ( arg == "-v" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg.substr(0,sizeof("-I")-1) == "-I" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-C" || arg == "-P" || arg == "-H" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg.substr(0,sizeof("-W")-1) == "-W" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-lint" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-pedantic" || arg == "-pedantic-errors" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-traditional" || arg == "-trigraphs" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-dM" || arg == "-dD" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-M" || arg == "-MG" || arg == "-MM" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
	    } else if ( arg == "-MD" || arg == "-MMD" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg == "-imacros" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg == "-include" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg == "-idirafter" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg == "-iprefix" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg == "-iwithprefix" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg == "-isystem" ) {
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
		i += 1;
		argsCppOnly[nargsCppOnly] = argv[i];	// pass argument along
		nargsCppOnly += 1;
#ifdef __DEBUG_H__
		cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( arg.substr(0,sizeof("-D__GCC_BPREFIX__")-1) == "-D__GCC_BPREFIX__" ) {
		bprefix = arg.substr(sizeof("-D__GCC_BPREFIX__"));
	    } else if ( arg.substr(0,sizeof("-D__GCC_MACHINE__")-1) == "-D__GCC_MACHINE__" ) {
		argsCppOnly[nargsCppOnly] = "-b";	// pass argument along
		nargsCppOnly += 1;
		argsCppOnly[nargsCppOnly] = ( *new string(arg.substr(sizeof("-D__GCC_MACHINE__")) ) ).c_str(); // pass argument along
		nargsCppOnly += 1;
	    } else if ( arg.substr(0,sizeof("-D__GCC_VERSION__")-1) == "-D__GCC_VERSION__" ) {
		argsCppOnly[nargsCppOnly] = "-V";	// pass argument along
		nargsCppOnly += 1;
		argsCppOnly[nargsCppOnly] = ( *new string( arg.substr(sizeof("-D__GCC_VERSION__")) ) ).c_str(); // pass argument along
		nargsCppOnly += 1;
	    } else if ( arg.substr(0,sizeof("-D__CPP_NAME__")-1) == "-D__CPP_NAME__" ) {
		cpp_name = arg.substr(sizeof("-D__CPP_NAME__"));
	    } else if ( arg.substr(1,1) != "+" ) {
		args[nargs] = argv[i];			// pass argument along
		nargs += 1;
	    } // if
	} else {
	    if ( cpp_in.length() == 0 ) {
		cpp_in = arg;
#ifdef __DEBUG_H__
		cerr << "cpp_in:\"" << cpp_in << "\"" << endl;
#endif // __DEBUG_H__
	    } else if ( cpp_out.length() == 0 ) {
		cpp_out = arg;
#ifdef __DEBUG_H__
		cerr << "cpp_out:\"" << cpp_out << "\""<< endl;
#endif // __DEBUG_H__
	    } else {
		cerr << "Usage: " << argv[0] << " input-file [output-file] [options]" << endl;
		exit( 1 );
	    } // if
	} // if
    } // for

    argsCppOnly[nargsCppOnly] = "-D__cplusplus";
    nargsCppOnly += 1;

    if ( cpp_in.length() == 0 ) {
	cerr << "Usage: " << argv[0] << " input-file [output-file] [options]" << endl;
	exit( 1 );
    } // if

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

    // The -E flag is specified so only run the preprocessor and output is
    // written to standard output.
    //
    // OR
    //
    // The preprocessor is called internally during compilation, probably by
    // "collect" during linking to compile some ctor/dtor code. In this case,
    // the cfa-cpp preprocessor is not run.  Output is redirected to cpp_out

    if ( cpp_flag || bprefix.length() == 0 ) {
	if ( ! cpp_flag ) {
	    if ( freopen( cpp_out.c_str(), "w", stdout ) == NULL ) { // redirect stdout if not -E
		cerr << argv[0] << ": Error can't write to " << cpp_out << endl;
		exit( -1 );
	    } // if

	    // If called by collect, must prevent the compiler from recursively
	    // calling this cpp through the -B path. To stop the recursion,
	    // sent the COMPILER_PATH environment variable to the NULL string,
	    // which removes the -B path supplied on the initial cfa command.

	    if ( gnu ) {
		putenv( "COMPILER_PATH=" );
	    } // if
	} // if

	if ( bprefix.length() == 0 ) {			// collect ?
	    args[0] = "gcc";				// use gcc
	} else {
	    args[0] = cpp_name.c_str();
	} // if
	args[nargs] = "-E";
	nargs += 1;

	for ( i = 0; i < nargsCppOnly; i += 1 ) {	// copy cpp only arguments
	    args[nargs] = argsCppOnly[i];
	    nargs += 1;
	} // if

	args[nargs] = cpp_in.c_str();
	nargs += 1;
	args[nargs] = NULL;				// terminate argument list

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

	execvp( args[0], (char *const *)args );		// should not return
	perror( "CFA translator error: cpp level, exec" );
	exit( -1 );
    } // if

    // Run the C preprocessor and save the output in tmpfile.

    if ( fork() == 0 ) {				// child process ?
	if ( freopen( tmpfile, "w", stdout ) == NULL) {	// redirect output to tmpfile
	    cerr << argv[0] << ": Error can't write to " << tmpfile << endl;
	    exit( -1 );
	} // if

	args[0] = cpp_name.c_str();
	args[nargs] = "-E";
	nargs += 1;

	for ( i = 0; i < nargsCppOnly; i += 1 ) {	// copy cpp only arguments
	    args[nargs] = argsCppOnly[i];
	    nargs += 1;
	} // if

	args[nargs] = cpp_in.c_str();
	nargs += 1;
	args[nargs] = NULL;				// terminate argument list

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

	execvp( args[0], (char *const *)args );		// should not return
	perror( "CFA translator error: cpp level, exec" );
	exit( -1 );
    } // if

    wait( &code );					// wait for child to finish

    if ( WIFSIGNALED(code) != 0 ) {			// child completed successfully ?
	unlink( tmpfile );
	cerr << "CFA translator error: cpp failed with signal " << WTERMSIG(code) << endl;
	exit( -1 );
    } // if

    // If -CFA flag specified, run the cfa-cpp preprocessor on the temporary
    // file, and output is written to standard output.  Otherwise, run the
    // cfa-cpp preprocessor on the temporary file and save the result into the
    // output file.

    if ( fork() == 0 ) {				// child process ?
	args[0] = ( *new string( bprefix + "/cfa-cpp" ) ).c_str();

	for ( i = 0; i < nargsCFAOnly; i += 1 ) {	// copy CFA only arguments
	    args[nargs] = argsCFAOnly[i];
	    nargs += 1;
	} // if
	args[nargs] = "-p";
	nargs += 1;

	args[nargs] = tmpfile;
	nargs += 1;

	if ( ! CFA_flag ) {				// run cfa-cpp ?
	    args[nargs] = cpp_out.c_str();
	    nargs += 1;
	} // if
	args[nargs] = NULL;				// terminate argument list

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

	execvp( args[0], (char *const *)args );		// should not return
	perror( "CFA translator error: cpp level, exec" );
    } // if

    wait( &code );					// wait for child to finish

    unlink( tmpfile );

    if ( WIFSIGNALED(code) != 0 ) {			// child completed successfully ?
	cerr << "CFA translator error: cfa-cpp failed with signal " << WTERMSIG(code) << endl;
	exit( -1 );
    } // if

    if ( CFA_flag ) {					// -CFA flag ?
	exit( -1 );					// tell gcc not to go any further
    } else {
	exit( WEXITSTATUS(code) );
    } // if
} // main


// Local Variables: //
// compile-command: "gmake" //
// End: //
