//	bfe2 - bochs connection
//	Copyright (c) 1999-2003 Brand Huntsman and Lee Salzman
//


#include "common.h"
#include "functions.h"


//////////////////////////////////////////////////////////////////////////

// global
FILE *writepipe, *readpipe;
uint bochs_running, bochs_offline, p_step;
int bochs_pid;

// local
gint sigio_tag;
uint bochs_died, starting_bochs;

//////////////////////////////////////////////////////////////////////////


uint popen2( char *command, FILE **in, FILE **err ){
	int ifd[2], efd [2];

	if(in != NULL) pipe(ifd);
	if(err != NULL) pipe(efd);

	bochs_pid = fork();
	if(bochs_pid == -1) return(0);
	if(bochs_pid == 0){
		char *argv[4];
		argv[0] = "sh";
		argv[1] = "-c";
		argv[2] = command;
		argv[3] = 0;
g_print("CHILD-%d\n", bochs_pid);
		if(in != NULL) dup2(ifd[0], STDIN_FILENO);
		if(err != NULL) dup2(efd[1], STDERR_FILENO);
		freopen("/dev/null", "a", stdout);
		execv("/bin/sh", argv);
		exit(EXIT_FAILURE);
	} else {
g_print("PARENT-%d\n", bochs_pid);
		if(err != NULL) *err = fdopen(efd[0], "r");
		if(in != NULL) *in = fdopen(ifd[1], "w");
	}
	return(1);
}


void sigchld_handler( int x ){
g_print("DEAD-%d\n", bochs_pid);
	if(starting_bochs) bfe_quit("Couldn't find Bochs");
	bochs_died = 1;
	// this will call the toggle_bochs() function in main.c
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bochs_button), FALSE);
}


void sigio_handler( gpointer data, gint source, GdkInputCondition condition ){
	ctrlStop(1);
}


void set_ctrl_online( gboolean v ){
	ctrlEnableControls(v);

	breakpointsEnablePage(v);
	watchpointsEnablePage(v);
	memoryEnablePage(v);
	structuresEnablePage(v);
	prefsEnablePage(v);

	gtk_widget_set_sensitive(refreshall_button, v);
}


//////////////////////////////////////////////////////////////////////////


void hook_sigio( ){
	sigio_tag = gdk_input_add(fileno(readpipe), GDK_INPUT_READ, sigio_handler, NULL);
}


void unhook_sigio( ){
	gdk_input_remove(sigio_tag);
}


void bochsInit( ){
	bochs_running = 0;
	bochs_offline = 1;
	bochs_died = 0;
}


void bochsShutdown( ){
	signal(SIGCHLD, SIG_IGN);

	if(bochs_running){
		unhook_sigio();
		if(!bochs_died) kill(bochs_pid, SIGINT);
		bochs_running = 0;
	}

	if(bochs_died){
		historyWrite("Lost connection to Bochs");
		bochs_died = 0;
	} else if(!bochs_offline){
		historyWrite("Terminating Bochs");
		fprintf(writepipe, "q\n");
		fclose(readpipe);
		fclose(writepipe);
	}

	set_ctrl_online(FALSE);

	bochs_offline = 1;
}


void bochsStart( ){
	#ifdef __DEBUG__
	 g_print("BFE DEBUG: starting bochs... \n");
	#endif

	historyReset();

g_print("START-%d\n", bochs_pid);
	starting_bochs = 1;
	signal(SIGCHLD, sigchld_handler);
	if(!popen2(BOCHS_PATH, &writepipe, &readpipe)){
		#ifdef __DEBUG__
		 g_print("\tFAILED\n");
		#endif
		historyWrite("BOCHS FAILED TO START");
		return;			// failed to start bochs
	}
	sleep(1);
	starting_bochs = 0;
	setbuf(readpipe, NULL);		// disable line buffering
	setbuf(writepipe, NULL);
	bochs_offline = 0;
g_print("START-OK-%d\n", bochs_pid);

	set_ctrl_online(TRUE);

	historyWrite("Starting Bochs");

	prompt_read();
	fprintf(writepipe, "s\n");
	ctrlGetInstruction();

	breakpointsReload();
	watchpointsReload();

	#ifdef __DEBUG__
	 g_print("\tOK\n");
	#endif
}


void prompt_read( ){
	char c;

	if(bochs_offline) return;

	#ifdef __DEBUG__
	 g_print("BFE DEBUG: reading prompt... \n");
	#endif

	for(c = fgetc(readpipe); c != '<' && c != EOF; c = fgetc(readpipe));
	if(c != '<') return;
	if(fscanf(readpipe, "bochs:%u>", &p_step) != 1) return;

	#ifdef __DEBUG__
	 g_print("\tOK\n");
	#endif
}
