/* macro.c : part of The Bastard disassembly environment ======================
 *
 * Contains the code for interfacing with the SeerC interpreter, as well as the
 * "interpreter" commands [i.e. LOAD, SHOW, etc] which are really wrappers for
 * API routines.
 */
#include "bastard.h"

/* 1-char shortcuts for interpreter commands */
int CMD_SHORTCUT[43] = { 
CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, // 0 - 5
CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, // 6 - ;
CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_HELP, CMD_UNDEF, CMD_STRINGS, // < - A
CMD_UNDEF, CMD_UNDEF, CMD_DISASM, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, // B - G
CMD_HEADER, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_LOAD, CMD_UNDEF, // H - M
CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_QUIT, CMD_RANGE, CMD_SECTION, // N - S
CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, CMD_UNDEF, // T - Y
CMD_UNDEF }; // Z

/* --------------------------------------------------------Utility Commands */
int istrncmp(char *s1, char *s2, int len) {
	/* case-insensitive strincmp */
	unsigned int x;
	char c1, c2;

	if ( ! s1 || ! s2 ) return(-1);
	for (x = 0; x < len; x++) {
		c1 = toupper(s1[x]); c2 = toupper(s2[x]);
		if (c1 < c2) return(-1);
		if (c1 > c2) return(1);
	}
	return(0);
}

int strtoupper( char *table, int len){
	int x;

	for (x = 0; x < len; x++) {
		if ( table[x] != 0)
			table[x] = toupper(table[x]);
		else break;
	}
	table[len - 1] = 0;
}

int CharToConst( char *c){
	scScript s;
	scInstance *i;
	char str[512];
	int x;

	sprintf(str,"#include bastard\nexport main;\n"
					"int main( ){\nreturn( %s );\n}\n", c);
	return(RunScriptText(str));
}

/* ----------------------------------------------------Interpreter Commands */
/* These commands are purely for the convenience of the user. Front-ends are
 * expected to use API commands to do the brunt of the work; therefore each
 * of the interpreter commands should be wrappers for API commands. Note that
 * specific, low-level commands such as 'DB' are provided for maintenance and
 * testing; they are not intended to be used by the front-end.
 */

/* NOTE : many of these are quick kludges that exist simply to make
 *        testing code changes more simple; in general, *everything*
 *        in this file should be considered non-production code and
 *        subject to heavy-handed rewrites.
 */


/* Command parser for 'sub commands', e.g. DB, SHOW, SET
 
   Should prob update main command line parser to do this too 
   but that might interfere with shortcuts, etc...
	
	returns 0 if unmatched 
*/
int TranslateSubCmd(char  *cmd, char* cmd_array[], int max){
	char uCmd[32];
	int x;

	for (x = 0; x < 32; x++) {
		if (isspace(cmd[x]) == 0 && cmd[x] != 0)
			uCmd[x] = toupper(cmd[x]);
		else break;
	}
	uCmd[31] = 0;

	for (x = 0; x < max; x++)
		if ( !strcmp(uCmd, cmd_array[x])) return(x);
	
	return(0);
}


int TranslateVar( char *var ){
	char uVar[32];
	int x;

	for (x = 0; x < 32; x++) {
		if (isspace(var[x]) || var[x] == 0) break;
		uVar[x] = toupper(var[x]);
	}
	uVar[31] = 0;
	return(x);
}

int cmdSET(char *cmd){
	/* set internal ENV variables */
	int flag;
	char *str, msg[64];

	strtok(cmd, " ");
	str = strtok(NULL, " ");
	strtoupper(str, strlen(str) + 1);
	flag = CharToConst( str );
	if (disasm_env.flags & flag) {
		disasm_env.flags ^= flag;
		sprintf(msg, "%s = OFF\n", str);
	}
	else {
		disasm_env.flags |= flag;
		sprintf(msg, "%s = ON\n", str);
	}
	Message(msg);
	return(1);
}

int cmdSHOW(char *cmd){
	/* show internal ENV variables */
	int flag;
	char *str, msg[64];

	strtok(cmd, " ");
	str = strtok(NULL, " ");
	strtoupper(str, strlen(str) + 1);
	flag = CharToConst( str );
	if (disasm_env.flags & flag) sprintf(msg, "%s = ON\n", str);
	else sprintf(msg, "%s = OFF\n", str);
	Message(msg);
	return(1);
}

int cmdDB(char *cmd){
	char *db_cmd, *base_cmd;
	int cmd_num;

	strtok(cmd, " ");
	db_cmd = strtok(NULL, " ");
	if (db_cmd != NULL ) 
		cmd_num = TranslateSubCmd(db_cmd, DB_CMD, NUM_DB_CMD);
	else cmd_num = 0; //help will always be 0
	db_cmd += strlen(db_cmd) + 1;

	switch (cmd_num){
		case 1:
			dbCMD(db_cmd, DB_COUNT); break;
		case 2:
			dbCMD(db_cmd, DB_DESC); break;
		case 3:
			dbCMD(db_cmd, DB_DUMP); break;
		case 4:
			dbCMD(db_cmd, DB_FIND); break;
		case 5:
			dbCMD(db_cmd, DB_MAX); break;
		case 6:
			dbCMD(db_cmd, DB_MIN); break;
		case 0:
		default:
			dbHELP(db_cmd);
	}
	return(1);
}


int cmdLOAD(char *cmd){
	int x;
	char *fname, *fformat, *cpu;

	/* generic file load/disassembler. Assumes a.out, ELF, i386 */
	/* wrapper for multiple API routines */
	if (disasm_env.flags & DB_LOADED) CloseDB(); //duplicated in LoadDB but nec.
	strtok(cmd, " ");
	if ((fname = strtok(NULL, " ")) != NULL) strncpy(target.name, fname, 63);
	if ((fformat = strtok(NULL," ")) != NULL) strncpy(target.format,fformat,15);
	if ((cpu = strtok(NULL, " ")) != NULL) strncpy(target.cpu, cpu, 31);
	
	if (!LoadTarget(target.name) ) return(0);
	if (!SetFileFormat(target.format) ) return(0);
	if (!SetFileArch(target.cpu) ) return(0);
	if (!DisassembleFull() ) return(0);

	return(1);
}


int cmdEXEC(char *cmd){
	char cline[256];

	if (! (disasm_env.flags & DISABLE_EXEC)) {
		if ( (cmd = strchr(cmd, ' ')) != NULL) 
			sprintf(cline, "/bin/sh -c %s", cmd);
		else 
			sprintf(cline, "/bin/sh -ri");
		system(cline);
	}
	return(1);
}

int cmdDEBUG(char *cmd){
	/* debug the target executable */
	/* maps to API routine */
	Debug();
	return(1);
}

int cmdHELP(char *cmd){
	char *topic;
	/* hmm, take a guess... */
	strtok(cmd, " ");
	topic = strtok(NULL, " ");
	if (topic != NULL) {
		printf("%s\n", HELPSTR[TranslateCmd(topic)]);

	} else printf("Usage: HELP [topic]\t--\'HELP HELP\' lists all topics\n");
	return(1);
}

int cmdQUIT(){
	/* leave the app */
	free(cmd);
	Quit();
	return(1);
}

int cmdHEADER(char *cmd){
	PrintHeader();
	return(1);
}


int cmdSECTION(char *cmd){
	char *tmp, name[32], secStr[128];
	struct section sec;
	struct address addr;
	int cont;
	
	strtok(cmd, " ");
	tmp = strtok(NULL, " ");
	if (tmp == NULL){
		Message("Specify a section name when using 'SECTION'. Names:\n"); 
		cont = DBIndexFirst(SECTION_NAME, &sec);
		while( cont ){
			sprintf( secStr, "%s\trva: %08X\tsize: %8X\n", sec.name, sec.rva, 
					sec.size);
			Message(secStr);
			cont = DBIndexNext(SECTION_NAME, &sec);
		}
	}
	else {
		strcpy( name, tmp);
		if (! DBIndexFind(SECTION_NAME, &name, &sec)) return(0);
		if (! (cont = DBIndexFind(ADDRESS_RVA, &sec.rva, &addr)) ) return(0);
		while( cont && addr.rva < (sec.rva + sec.size)) {
			PrintAddress( addr.rva );
			DBIndexNext(ADDRESS_RVA, &addr);
		}
	}
	return(1);
}

int cmdRANGE(char *cmd){
	/* get starting and ending address */
	/* find closest addr (start_addr) */
	/* while (addr< end_addr) addr = get_addr_from_db() */
	/*       PrintAddress( addr ) */
	Message("Not yet implemented\n");
	return(1);
}

int cmdDISASM(char *cmd){
	struct address addr;
	struct section sec;
	int cont, cont2;

	cont = DBIndexFirst(SECTION_RVA, &sec);
	while( cont ) {
		if ( (cont2 = DBIndexFind(ADDRESS_RVA, &sec.rva, &addr)) ) {
			printf("Section: %s\tStart Address: %08X\tEnd Address: %08X\n",
							 sec.name, sec.rva, sec.rva + sec.size);
			printf("--------------------------------------------------------------"
				 	"-----------\n");
			while( cont2 && addr.rva < sec.rva + sec.size){
				PrintAddress( addr.rva );
				cont2 = DBIndexNext(ADDRESS_RVA, &addr);
			}
		printf("\n\n");
		}
		cont = DBIndexNext(SECTION_RVA, &sec);
	}
	return(1);
}

int cmdSTRINGS(char *cmd){
	DumpStrings();
	return(1);
}

int cmdMACRO(char *cmd){
	/* write a SeerC script */
	/* maps to API routine */
	char *line, *script = NULL;
	int cont = 1;
	
	while (cont) {
		line = (char *) readline(disasm_env.p3);
		if ( ! istrncmp( line, "ENDMACRO", 8) )
			cont = 0;
		else {
			script = realloc(script, strlen(line));
			strcat( script, line);
		}
	}
	if (strlen(script) < 2048){
		/* DefineMacro( name, script); */
	} else {
		Message("Macro is too large to save in database.\n"
				  "Enter a filename for the macro:");
		/* ** name will be .bdb/macros/$name.sc */
		/* gets, fopen, fwrite, fclose */
		/* id = DefineMacro( name, script ); */
		/* SetMacroType( id, MACOR_FILE );*/ 
	}
	
	return(1);
}

int cmdENDMACRO(char *cmd){
	Message("ENDMACRO is only valid in the context of a MACRO block!\n");
	return(0);
}

int cmdRECORD(char *cmd){
	/* record commandline actions ... may be useless */
	/* maps to API routine */
	Message("Not yet implemented\n");
	return(0);
}

int cmdSTOP(char *cmd){
	Message("Not yet implemented\n");
	return(0);
}

int cmdRUN(char *cmd){
	/* Run Macro */
	Message("Not yet implemented\n");
	return(0);
}

int cmdBRACE(char *cmd){
	char *line, *tmp, *src;
	scScript s;
	scInstance *i;
	int x, cont = 1;

	src = calloc(1, 1);			  
	while (cont){
		line = (char *) readline(disasm_env.p3);
		if (! strcmp(line, "}}")) cont = 0;
		else {
			src = realloc(src, strlen(src) + strlen(line) + 1);
			strcat(src, line);
			strcat(src, "\n");
		}
		if (line) free(line);
	} 
	tmp = calloc(strlen(src) + strlen(scTemplate), 1); 
	sprintf(tmp, scTemplate, src);

	x = ExecMacroLine(tmp);
	free(tmp);
	return(x);
}

/* ------------------------------------------------------------- Seer Stuff */
#include "bastard_seer.h"
void loadSeerInc( char *name, char *buf, char *incname){
	int fd;
	struct stat tmpStat;	
	char fname[256], *incfile;

	sprintf(fname, "%s/include/%s", disasm_env.home, name);
	fd = open(fname, O_RDONLY);
	fstat(fd, &tmpStat);
	buf = calloc(tmpStat.st_size + 1, 1);
	read(fd, buf, tmpStat.st_size);
	scAdd_Internal_Header(incname, buf);
	if (scErrorNo)  {
		printf("SeerC Error: %d %s in \n%s\n", scErrorNo, scErrorMsg, name);
		SetLastError(3010);
		
	}
	close(fd);
	return;
}

void doSeer(){
	scScript s;
	scInstance *i;
	
	scInit_SeeR();
	if (disasm_env.flags & DEBUG_LIB) scOpen_Debug("seer.debug.out");
	InitSeerSymbols();

	/* Prepare Common Include File */
	loadSeerInc( "seer.clib.h", c_inc, "clib");
	loadSeerInc( "seer.b_def.h", b_def, "bastard_def");
	loadSeerInc( "seer.bastard.h", b_inc, "bastard");
	
	return;
}

void finSeer(){
	free(c_inc);
	free(b_inc);
	free(b_def);
	 if (disasm_env.flags & DEBUG_LIB)scClose_Debug();
	return;
}

	/* example of how to do this Seer crap ;) 
	s=scCompile_Text("import int printf(char *,...);\n"
					  "export main;\n"
					  "int main() {\n"
					  "  printf(\"Hello Bastard\");\n"
					  "  return(0);\n"
					  "}\n");
	if (!s) printf("AGH! Seer failed! %s %s\n", scErrorLine, scErrorMsg);
	i = scCreate_Instance(s,"");
	if (scErrorNo)  printf("Seer Create Instance failed\n");
	scStart_Instance(i);
	if (scErrorNo)  printf("Seer Start Instance failed\n");
	scFree_Instance(i);
	free(s);  */

			
/* =================================================== ExecMacroLine */
int ExecMacroLine(char *cmd) {
	int x;
	char *script;
	
	script = calloc(strlen(scTemplate) + strlen(cmd), 1);
	if (! script ) return(SetLastError(9050));
	sprintf(script, scTemplate, cmd);
	
	x = RunScriptText(script);
	free(script);
	return(x);
}
