#include "cl.h"

#define FILE_TMP  "CL386.$$$"

static void run_compiler( void );
static void run_assembler( void );
static void run_linker( void );

static void delete_file( char *data, int temp ) { if (temp) remove(data); }

void make( void ) {
    run_compiler();
    run_assembler();
    run_linker();

    if ( (options & _OPT_LINK) && !( options & _OPT_KEEPGEN) ) {
        iter( FILE_ASM, delete_file );
        iter( FILE_OBJ, delete_file );
    }
    else if ( (options & _OPT_ASSEMBLE )  && !( options & _OPT_KEEPGEN) )
        iter( FILE_ASM, delete_file );

    if ( !( options & _OPT_KEEPRSP) )
        remove( FILE_TMP );

}

static int lcount( int what );

#define cpp_count()  lcount( FILE_CPP )
#define asm_count()  lcount( FILE_ASM )
#define obj_count()  lcount( FILE_OBJ )
#define lib_count()  lcount( FILE_LIB )
#define inc_count()  lcount( PATH_INC )

#define libpath_count()  lcount( PATH_LIB )

static int ncount;
static void fcount(char *data ) { ncount++; }

int lcount( int what ) {
    ncount = 0;
    iter0( what, &fcount );
    return ncount;
}

static FILE *frsp;

static void open_rsp(void) {
    frsp = fopen( FILE_TMP, "wt" );
    if ( frsp == NULL ) {
        fprintf( stderr, "Error open file : '%s'\n", FILE_TMP );
        exit(-1);
    }
}

static void close_rsp( void ) {
    fclose( frsp );
//  remove( FILE_TEMP );
}

static void exec( char *prg, char *arg ) {
    int err;
    static char *args[3];

    args[0] = search( prg );
    args[1] = arg;
    args[2] = NULL;

    err = spawnvp( P_WAIT, args[0], args );

    if ( err != 0 )
        if ( !( options & _OPT_KEEPRSP) )
            remove( FILE_TMP );
    
    if ( err > 0 ) {
        fprintf( stderr, "\n'%s' exit code = %Xh\n", prg, err );
        exit(err);
    }
    else if ( err < 0 ) {
        perror( prg );
        exit( -1 );
    }
}

static void compiler_opt( char *opt ) { fprintf( frsp,   " %s", opt ); }
static void compiler_def( char *opt ) { fprintf( frsp, " -D%s", opt ); }
static void compiler_inc( char *opt ) { fprintf( frsp, "%s;", opt ); }
static void compiler_fls( char *opt ) { fprintf( frsp, "\n%s", opt ); }

void run_compiler( void ) {
    // compile & .CPP files > 0
    if (  ( options & _OPT_COMPILE ) && ( cpp_count() > 0 )  ) {
        open_rsp();
        fprintf( frsp, "%ci %ce %cl %cA",
            ( ( options & _OPT_PPRFILE ) ? '+' : '-' ),
            ( ( options & _OPT_ERRFILE ) ? '+' : '-' ),
            ( ( options & _OPT_LSTFILE ) ? '+' : '-' ),
            ( ( options & _OPT_EXTEND  ) ? '+' : '-' ) );
        iter0( OPT_CPP,  compiler_opt );
        if ( assembler == ASSEMBLER_NASM )
            fprintf( frsp, " -C+N");
        else if (assembler == ASSEMBLER_WASM ||
            assembler == ASSEMBLER_LASM ||
            assembler == ASSEMBLER_MASM ||
            assembler == ASSEMBLER_ML6X )
            fprintf( frsp, " -C+M");
        iter0( DEFINED,  compiler_def );
        if ( inc_count() > 0 ) {
            fprintf( frsp, " -I" );
            iter0( PATH_INC, compiler_inc );
        }
        iter0( FILE_CPP, compiler_fls );

        close_rsp();

        exec( "CC386", "/f" FILE_TMP );
    }
}

/**************************************************************
                    ASSEMBLERS
***************************************************************/
static void run_tasm( void );
static void run_tasmx( void );
static void run_tasm32( void );
static void run_masm( void );
static void run_nasm( void );
static void run_lasm( void );
static void run_ml6x( void );
static void run_wasm( void );

void run_assembler( void ) {
    // assemble & .ASM files > 0
    if (  ( options & _OPT_ASSEMBLE ) && ( asm_count() > 0 )  ) {
        switch ( assembler ) {
            case ASSEMBLER_TASM:
                run_tasm();
                break;
            case ASSEMBLER_TASMX:
                run_tasmx();
                break;
            case ASSEMBLER_TASM32:
                run_tasm32();
                break;
            case ASSEMBLER_MASM:
                run_masm();
                break;
            case ASSEMBLER_NASM:
                run_nasm();
                break;
            case ASSEMBLER_ML6X:
                run_ml6x();
                break;
            case ASSEMBLER_LASM:
                run_lasm();
                break;
            case ASSEMBLER_WASM:
                run_wasm();
                break;
            default:
                fprintf( stderr, "Use unknow assembler !\n" );
                exit(-1);
        }
    }
}

/*  TASM
********/

static void tasm_inc( char *inc ) { fprintf( frsp, " /i%s", inc ); }
static void tasm_def( char *def ) { fprintf( frsp, " /d%s", def ); }

static void tasm_one( char *data ) {
    fprintf( frsp, "/t /ml /m9 /z%c ",
        ( ( options & _OPT_DEBUG ) ? 'i' : 'n' ) );
    iter0( PATH_INC, tasm_inc );
    iter0( DEFINED , tasm_def );
/*    _splitpath( data, drive, dir, name, ext );
#if defined( OBJ2CURDIR)
    drive[0] = 0; dir[0] = 0;
#endif
*/  if ( options & _OPT_LSTFILE )
        fprintf( frsp, " %s,%s,%s,NUL;\n",
            data, file2curdir( data, ".OBJ" ), file2curdir( data, ".LST" ) );
    else
        fprintf( frsp, " %s,%s,NUL,NUL;\n",
            data, file2curdir( data, ".OBJ" ) );
}

void run_tasm( void ) {
    open_rsp();
    iter0( FILE_ASM, tasm_one );
    close_rsp();
    exec( "TASM", "@" FILE_TMP );
}

void run_tasmx( void ) {
    open_rsp();
    iter0( FILE_ASM, tasm_one );
    close_rsp();
    exec( "TASMX", "@" FILE_TMP );
}

void run_tasm32( void ) {
    open_rsp();
    iter0( FILE_ASM, tasm_one );
    close_rsp();
    exec( "TASM32", "@" FILE_TMP );
}

static char args[ _MAX_PATH ];


/*  MASM
********/

static void masm_inc( char *inc ) { strcat( strcat( args, " /I"), inc ); }
static void masm_def( char *def ) { strcat( strcat( args, " /D"), def ); }

static void masm_one( char *data ) {
    strcpy( args, "/t /Ml" );
    if ( options & _OPT_DEBUG )
        strcat( args, " /Zi" );
    iter0( PATH_INC, masm_inc );
    iter0( DEFINED , masm_def );

    strcat( strcat( args, " " ), data );
/*  _splitpath( data, drive, dir, name, ext );
#if defined( OBJ2CURDIR)
    drive[0] = 0; dir[0] = 0;
#endif
*/
    strcat( strcat( args, "," ), file2curdir( data, ".OBJ" ) );
    if ( options & _OPT_LSTFILE )
        strcat( strcat( strcat( args, "," ), file2curdir( data, ".LST" )), ",NUL" );
    else
        strcat( args, ",NUL,NUL" );
    exec( "MASM", args );
}

void run_masm( void ) {
    iter0( FILE_ASM, masm_one );
}

/*  NASM
********/

static void nasm_inc( char *inc ) { strcat( strcat( args, " -i"), inc ); }
static void nasm_def( char *def ) { strcat( strcat( args, " -d"), def ); }

static void nasm_one( char *data ) {
    strcat( strcpy( args, " -f obj " ), data );

    if ( options & _OPT_LSTFILE )
        strcat( strcat( args, " -l " ), file2curdir( data, ".LST" ) );
        
    iter0( PATH_INC, nasm_inc );
    iter0( DEFINED , nasm_def );

//    strcat( strcat( args, " -o " ), file2curdir( data, ".OBJ" ) );
//    strcat( strcat( args, " " ), data );
    exec( "NASM", args );
}

void run_nasm( void ) {
    iter0( FILE_ASM, nasm_one );
}

/*  MASM v6.x
*************/

static void ml6x_one( char *data ) {
    strcpy( args, "/nologo /c /Cp" );
    if ( options & _OPT_DEBUG )
        strcat( args, " /Zd /Zi" );
    iter0( PATH_INC, masm_inc );
    iter0( DEFINED , masm_def );


    strcat( strcat( args, " /Fo" ), file2curdir( data, ".OBJ" ) );

    if ( options & _OPT_LSTFILE )
        strcat( strcat( args, " /Fl" ), file2curdir( data, ".LST" ) );

    strcat( strcat( args, " " ), data );

    exec( "ML", args );
}

void run_ml6x( void ) {
    iter0( FILE_ASM, ml6x_one );
}

/*  LASM v6.x
*************/

static void lasm_one( char *data ) {
    strcpy( args, "/G1 /O /S /U /Y" );

    if ( options & _OPT_DEBUG )
        strcat( args, " /ZH" );

    iter0( PATH_INC, masm_inc );
    iter0( DEFINED , masm_def );

    strcat( strcat( args, " /FO" ), file2curdir( data, ".OBJ" ) );

    if ( options & _OPT_LSTFILE )
        strcat( strcat( args, " /L" ), file2curdir( data, ".LST" ) );

    strcat( strcat( args, " " ), data );

    exec( "LASM", args );
}

void run_lasm( void ) {
    iter0( FILE_ASM, lasm_one );
}

/*  WASM
*************/
static void wasm_inc( char *inc ) { strcat( strcat( args, " -i="), inc ); }
static void wasm_def( char *def ) { strcat( strcat( args, " -d"), def ); }

static void wasm_one( char *data ) {
    strcpy( args, "-4ps -fpi87 -q -mf" );

    if ( options & _OPT_DEBUG )
        strcat( args, " /d1" );

    iter0( PATH_INC, wasm_inc );
    iter0( DEFINED , wasm_def );

    strcat( strcat( args, " /fo=" ), file2curdir( data, ".OBJ" ) );

    if ( options & _OPT_LSTFILE )
        strcat( strcat( args, " /fe=" ), file2curdir( data, ".LST" ) );

    strcat( strcat( args, " " ), data );

    exec( "WASM", args );
}

void run_wasm( void ) {
    iter0( FILE_ASM, wasm_one );
}

static void run_tlink( void );
static void run_wlink( void );
static void run_link( void );
static void run_valx( void );

static char *libpath = NULL;

static void linker_libpath( char *data ) { if ( libpath == NULL ) libpath = data; }

void run_linker( void ) {
    // .OBJ & .LIB files > 0
    if (  ( options & _OPT_LINK ) && ( lib_count() > 0 || obj_count() > 0 )  ) {
        iter0( PATH_LIB, linker_libpath );
        if ( libpath == NULL )
            libpath = ".";
        switch ( linker ) {
            case LINKER_TLINK:
                run_tlink();
                break;
            case LINKER_WLINK:
                run_wlink();
                break;
            case LINKER_LINK:
                run_link();
                break;
            case LINKER_VALX:
                run_valx();
                break;
            default:
                fprintf( stderr, "Use unknow linker !\n" );
                exit(-1);
        }
    }
}

static void tlink_out( char *data ) { fprintf( frsp, " %s", data ); }
static void tlink_libpath( char *data ) { fprintf( frsp, "%s;", data ); }

void run_tlink( void ) {

    if ( dosx != DOSX_PMODE ) {
        fprintf( stderr, "Warning : TLINK use only with PMODE !\n" );
        dosx = DOSX_PMODE;
    }

    open_rsp();

    fprintf( frsp, "/3/c/d" );

    if ( options & _OPT_MAPFILE )
        fprintf( frsp, "/m/l/s" );
    else
        fprintf( frsp, "/x" );

    if ( options & _OPT_DEBUG )
        fprintf( frsp, "/v" );

    if ( libpath_count() > 0 ) {
        fprintf( frsp, "/L" );
        iter0( PATH_LIB, tlink_libpath );
    }

    if ( !( options & _OPT_NODEFLIB ) ) {
        if ( options & _OPT_DEBUG )
            fprintf( frsp, " %s\\C0DOSD.OBJ", libpath );
        else
            fprintf( frsp, " %s\\C0DOS.OBJ", libpath );
    }

    iter0( FILE_OBJ, tlink_out );

    fprintf( frsp, "\n%s.EXE", exename );

    if ( options & _OPT_MAPFILE )
        fprintf( frsp, "\n%s.MAP\n", exename );
    else
        fprintf( frsp, "\nNUL\n" );

    if ( !( options & _OPT_NODEFLIB ) )
        fprintf( frsp, " %s\\CLDOS.LIB", libpath );

    iter0( FILE_LIB, tlink_out );
    fprintf( frsp, "\n" );

    close_rsp();

    exec( "TLINK", "@" FILE_TMP );
}

static void wlink_obj( char *data ) { fprintf( frsp, "file %s\n", data ); }
static void wlink_lib( char *data ) { fprintf( frsp, "library %s\n", data ); }
static void wlink_libpath( char *data ) { fprintf( frsp, "libpath %s\n", data ); }

void run_wlink( void ) {

    open_rsp();

    switch ( dosx ) {
        case DOSX_PMODEW :
            fprintf( frsp, "op osname='CC386+PMODE/W'\n"
                           "op stub=%s\\PMODEW.EXE\n", libpath );
            break;
        default :
            fprintf( stderr, "Warning : WLINK can't use with PMODE ! Use DOS4G ...\n" );
        case DOSX_DOS4G :
            fprintf( frsp, "op osname='CC386+DOS/4G[W]'\n"
                           "op stub=%s\\4GSTUB.EXE\n", libpath );
            break;
        case DOSX_WDOSX :
            fprintf( frsp, "op osname='CC386+WDOSX'\n"
                           "op stub=%s\\WDOSXLE.EXE\n", libpath );
            break;
        case DOSX_DOS32A :
            fprintf( frsp, "op osname='CC386+DOS/32'\n"
                           "op stub=%s\\DOS32A.EXE\n", libpath );
            break;
        case DOSX_ZRDX :
            fprintf( frsp, "op osname='CC386+ZRDX'\n"
                           "op stub=%s\\ZRDX.EXE\n", libpath );
            break;
        case DOSX_CAUSEWAY :
            fprintf( frsp, "op osname='CC386+CAUSEWAY'\n"
                           "op stub=%s\\CAUSEWAY.EXE\n", libpath );
            break;
    }

    fprintf( frsp, "format os2 le\n" 
                   "op nod\n"
                   "op quiet\n");

    if ( options & _OPT_MAPFILE )
        fprintf( frsp, "op map\n" );

    if ( options & _OPT_DEBUG )
        fprintf( frsp, "debug all\nop symf\n" );

    iter0( PATH_LIB, wlink_libpath );

    fprintf( frsp, "name %s.EXE\n", exename );
    
    if ( !( options & _OPT_NODEFLIB ) )
        fprintf( frsp, "file %s\\C0DOSW.OBJ\n", libpath );

    iter0( FILE_OBJ, wlink_obj );

    if ( !( options & _OPT_NODEFLIB ) )
        fprintf( frsp, "library %s\\CLDOS.LIB\n", libpath );

    iter0( FILE_LIB, wlink_lib );

    close_rsp();

    exec( "WLINK", "@" FILE_TMP );
}

static void link_out( char *data ) { fprintf( frsp, " %s", data ); }

void run_link( void ) {

    if ( dosx != DOSX_PMODE ) {
        fprintf( stderr, "Warning : LINK use only with PMODE !\n" );
        dosx = DOSX_PMODE;
    }

    strcpy( args, "/NOIGNORECASE" );

    if ( options & _OPT_MAPFILE )
        strcat( args, " /MAP" );
        
    strcat( args, " @" FILE_TMP );
        
    open_rsp();

    if ( !( options & _OPT_NODEFLIB ) ) {
        if ( options & _OPT_DEBUG )
            fprintf( frsp, " %s\\C0DOSD.OBJ", libpath );
        else
            fprintf( frsp, " %s\\C0DOS.OBJ", libpath );
    }

    iter0( FILE_OBJ, link_out );
    
    fprintf( frsp, "\n%s.EXE\n", exename );
    
    if ( options & _OPT_MAPFILE )
        fprintf( frsp, "%s.MAP\n", exename );
    else
        fprintf( frsp, "NUL\n" );

    if ( !( options & _OPT_NODEFLIB ) )
        fprintf( frsp, " %s\\CLDOS.LIB", libpath );

    iter0( FILE_LIB, link_out );
    fprintf( frsp, "\n" );

    close_rsp();

    exec( "LINK", args );

}


void run_valx( void ) {

    if ( dosx != DOSX_PMODE ) {
        fprintf( stderr, "Warning : VALX use only with PMODE !\n" );
        dosx = DOSX_PMODE;
    }

    strcpy( args, "-NOCA -USE32" );

    if ( options & _OPT_MAPFILE )
        strcat( args, " -MAP" );
    else
        strcat( args, " -NOMAP" );
        
    strcat( args, " @" FILE_TMP );
        
    open_rsp();

    if ( !( options & _OPT_NODEFLIB ) ) {
        if ( options & _OPT_DEBUG )
            fprintf( frsp, " %s\\C0DOSD.OBJ", libpath );
        else
            fprintf( frsp, " %s\\C0DOS.OBJ", libpath );
    }

    iter0( FILE_OBJ, tlink_out );

    fprintf( frsp, ",%s.EXE", exename );
    
    if ( options & _OPT_MAPFILE )
    	fprintf( frsp, ",%s.MAP,", exename );
    else
    	fprintf( frsp, ",NUL," );
    	
    if ( !( options & _OPT_NODEFLIB ) )
        fprintf( frsp, "%s\\CLDOS.LIB", libpath );

    iter0( FILE_LIB, tlink_out );
    fprintf( frsp, "\n" );

    close_rsp();

    exec( "VALX", args );
}
