/* bastard.c : part of The Bastard disassembly environment ====================
 *
 * Contains initialization code, signal handlers, and the command line
 * interpreter [using the readline library]
 */
#include <bastard.h>
#include <readline/readline.h>
#include <readline/history.h>


//____________________________________________________________TranslateCmd()
int TranslateCmd( char* cmd) {
		char uCmd[16];
		int pos;

		/*  uCmd = uppercase'd first word of cmd */
		for ( pos = 0; pos < 16; pos++) {
			if ( isspace(cmd[pos]) == 0  && cmd[pos] != 0) 
					uCmd[pos] = toupper(cmd[pos]);
			else  break;
		}	
		/* terminate string */
		if (pos > 15) pos = 15;
		uCmd[pos] = 0;

		/* check aliases */
		if (pos == 1 && uCmd[0] >= 48 && uCmd[0] < 91)
				return(CMD_SHORTCUT[(uCmd[0] - 48)]);

		if      (! strcmp(uCmd, "LOAD"))
			return(CMD_LOAD);
		else if (! strcmp(uCmd, "HELP"))
			return(CMD_HELP);
		else if (! strcmp(uCmd, "QUIT"))
			return(CMD_QUIT);
		else if (! strcmp(uCmd, "DB"))
			return(CMD_DB);
		else if (! strcmp(uCmd, "SHOW"))
			return(CMD_SHOW);
		else if (! strcmp(uCmd, "SET"))
			return(CMD_SET);
		else if (! strcmp(uCmd, "EXEC"))
			return(CMD_EXEC);
		else if (! strcmp(uCmd, "DEBUG"))
			return(CMD_DEBUG);
		else if (! strcmp(uCmd, "HEADER"))
			return(CMD_HEADER);
		else if (! strcmp(uCmd, "SECTION"))
			return(CMD_SECTION);
		else if (! strcmp(uCmd, "RANGE"))
			return(CMD_RANGE);
		else if (! strcmp(uCmd, "DISASM"))
			return(CMD_DISASM);
		else if (! strcmp(uCmd, "STRINGS"))
			return(CMD_STRINGS);
		else if (! strcmp(uCmd, "MACRO"))
			return(CMD_MACRO);
		else if (! strcmp(uCmd, "ENDMACRO"))
			return(CMD_ENDMACRO);
		else if (! strcmp(uCmd, "RECORD"))
			return(CMD_RECORD);
		else if (! strcmp(uCmd, "STOP"))
			return(CMD_STOP);
		else if (! strcmp(uCmd, "RUN"))
			return(CMD_RUN);
		else if (! strcmp(uCmd, "!"))
			return(CMD_EXEC);
		else if (! strcmp(uCmd, "{{"))
			return(CMD_BRACE);
		// ...else 
		return(CMD_UNDEF);
}

//______________________________________________________________user_kill()
void user_kill( int sig) {
	cmdQUIT();
	printf("bastard! killed by signal %d\n", sig);
	exit(sig);
}
//_________________________________________________________DisasmDefaults()
void DisasmDefaults() {
	//initialize all structures to their default values
	disasm_env.flags = 0;
	disasm_eng.init = FALSE;
	disasm_env.ttyFmt = disasm_env.asmFmt = disasm_env.lprFmt = defaultFmt;
	strcpy(disasm_env.p1, ";>");
	strcpy(disasm_env.p2, "\\");
	strcpy(disasm_env.p3, " -");
	strcpy(disasm_env.p4, " ?");
	strcpy(target.name, "a.out");
	strcpy(target.cpu, "i386");
	strcpy(target.format, "ELF");
	strcpy(disasm_eng.name, "libi386.so");
	sprintf(disasm_eng.path, "%s/arch", disasm_env.home);
}
//______________________________________________________________ReadPrefs()
void ReadPrefs( char* filename ){
	/* to be written :P */
	FILE *f;
	char buf[256];
	printf("reading %s\n", filename);
	f = fopen(filename, "r");
	/* read in entire file and process as seerc script */
	fclose(f);
	return;
}

//___________________________________________________________________main()
int main (int argc, char* argv[]){
	int x, retVal = 0;
	FILE *fTemp, *oldIN, *oldOUT, *oldERR, *newSTR;
	char tmpBuf[128], *tmp, *script;
	struct stat tmpStat;

	/* ----------------- Setup Operations Environment -------------- */
	/* + Get the location of bastard [for .bdb, libs, etc] */
	if ( !(tmp = getenv("BASTARD_HOME")) ){
		tmp = tmpBuf;
		if (strchr(argv[0], '/') != NULL) {
			strncpy(tmp, argv[0], sizeof(tmpBuf));
		} else {
			fTemp = popen("which bastard", "r");
			if (! fTemp ) {
				printf("Which is broken! Use Explicit Path Names!\n");
				return(-1);
			} else {
				fgets(tmp, sizeof(tmpBuf), fTemp);
				pclose(fTemp);
			}
		}
		/* handle symbolic links to the bastard */
		if (lstat( tmp, &tmpStat) == -1) {
			printf("Fatal error %X: lstat %s\n", errno, tmp);
			return(-1);
		}
		if (S_ISLNK(tmpStat.st_mode)){
			if (readlink( tmp, tmp, 128) == -1) {
		    	printf("Fatal Error %X: readlink %s\n", errno, tmp);
				return(-1);
			}
		} 
	}
	strncpy(disasm_env.home, tmp, (strrchr(tmp, '/') - tmp + 1));

	/* + Set some non-vital defaults */
	DisasmDefaults();
	oldIN = stdin; oldOUT = stdout; oldERR = stderr;

	/* + Apply settings in RC scripts */
	if ( stat("/etc/bastard.rc", &tmpStat) == 0) ReadPrefs("/etc/bastard.rc");
	//if ( stat("~/.bastardrc", &tmpStat) == 0) ReadPrefs("~/.bastardrc");
	if ( stat("./.bastardrc", &tmpStat) == 0) ReadPrefs("./.bastardrc");

	/* + Trap User Signals */
	signal( SIGKILL, user_kill);
	signal( SIGABRT, user_kill);
	signal( SIGINT, user_kill);
	signal( SIGTERM, user_kill);
	signal( SIGSEGV, user_kill);
	
	/* ------------------- Command Line Options  -------------------- */
	for (x = 1; x < argc; x++) {
		if (argv[x][0] == '-' ) {
			switch (argv[x][1]) {
				case 'i': /* redirect STDIN */
						newSTR = fopen(argv[x+1], "r");
						if(newSTR == NULL) {
								SetLastError(1010);
								PrintErrorMsg( GetLastError());
								return(-1);
						}
						stdin = newSTR;
						x++; /* advance argument past parameter */
						break;
				case 'o': /* redirect STDOUT */
						newSTR = fopen(argv[x+1], "w");
						if(newSTR == NULL) {
								SetLastError(1011);
								PrintErrorMsg( GetLastError());
								return(-1);
						}
						stdout = newSTR;
						x++; 
						break;
				case 'e': /* redirect STDERR */
						newSTR = fopen(argv[x+1], "w");
						if(newSTR == NULL) {
								SetLastError(1012);
								PrintErrorMsg( GetLastError());
								return(-1);
						}
						stderr = newSTR;
						x++; 
						break;
				case 's': /* run specified script on target */
						script = argv[x+1];
						//runandquit = 1;
						x++;
						break;
				case 'p': /* set prompt */
						strncpy(disasm_env.p1, argv[x+1], 15);
						x++; 
						break;
				case 'a': /* annoy user */
						disasm_env.flags |= ANNOY_USER;
						break;
				case 'q': /* suppress useless output */
						disasm_env.flags |= QUIET;
						break;
				case 'd': /* debug mode */
						disasm_env.flags |= DEBUG_LIB;
						break;
				case 'l': /* use libopcodes */
						strcpy(disasm_eng.name, "libopcodes.so");
						break;
				case '-': /* read file from stdin */
						//tmpnam(target.name);
						//TODO : RunAndQuit();
						//runandquit = 1;
						break;
				case 'h': case '?': default:
						printf("Usage: %s %s [-i in] [-o out] [-e err]"
										" [-p p1] [filename]\n",
										argv[0], "[-aqdlh?]");
						return(-1);
			}
		/* All non-hyphenated parameters are assumed to be the target */
		} else {
				strncpy( target.name, argv[x], sizeof(target.name));
		}	   
	}

	/* ----------------- Initialize Add-On Packages  ----------------- */
	doSeer();
	doTyphoon();
	/* ------------------------ Get Started  ------------------------- */
	if ( !(disasm_env.flags & QUIET) ) {
		printf("==========================================================="
		 		"=====================\n");
		printf("   B A S T A R D                    disassembly environment\n");
		printf("   brought to you by the proud folks at the HCU linux forum\n");
	}

	/* CmdLine loop [goto]
	 * This is a disgusting hack to counter some strange stack corruption
	 * problems happening with the seer/readline interaction. To Be Fixed!! */
here_loc:
		retVal = 1;
		if (! (disasm_env.flags & QUIET)) cmd = readline(disasm_env.p1);
		else cmd = readline("");
		add_history(cmd);
		
		switch( TranslateCmd(cmd)){	
			case CMD_SET:
				cmdSET(cmd); break;
			case CMD_SHOW:
				cmdSHOW(cmd); break;
			case CMD_DB:
				cmdDB(cmd); break;
			case CMD_LOAD:
				cmdLOAD(cmd); break;
			case CMD_EXEC:
				cmdEXEC(cmd); break;
			case CMD_DEBUG:
				cmdDEBUG(cmd); break;
			case CMD_HELP:
				cmdHELP(cmd); break;
			case CMD_QUIT:
				cmdQUIT(); retVal = 0;
			case CMD_HEADER:
				cmdHEADER(cmd); break;
			case CMD_SECTION:
				cmdSECTION(cmd); break;
			case CMD_RANGE:
				cmdRANGE(cmd); break;
			case CMD_DISASM:
				cmdDISASM(cmd); break;
			case CMD_STRINGS:
				cmdSTRINGS(cmd); break;
			case CMD_MACRO:
				cmdMACRO(cmd); break;
			case CMD_ENDMACRO:
				cmdENDMACRO(cmd); break;
			case CMD_RECORD:
				cmdRECORD(cmd); break;
			case CMD_RUN:
				cmdRUN(cmd); break;
			case CMD_STOP:
				cmdSTOP(cmd); break;
			case CMD_BRACE:
				cmdBRACE(cmd); break;
			case CMD_UNDEF: default:			
				ExecMacroLine(cmd);
				break;
	} /* end  switch */
	/* Also, END DISGUSTING HACK */
	if (cmd) free(cmd);
	if (retVal) goto here_loc;
	//if(cmd) free(cmd);

	/* ------------------------ Final Cleanup ----------------------- */
	if ( ! (disasm_env.flags & QUIET)) printf("\nYou bastard!\n");
	return(0);
}
