#include <cli/bastard_main.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <config.h>

/* --------------------------------------------------------------------- */

void cmdSET(int argc, char **argv, struct COMMAND *c){
   /* set internal ENV variables */
   char *opt, msg[64];
   int rv;

   if (! argc || !argv[0]) opt = NULL;
   else opt = argv[0];

   if ( SetBastardOption(opt) > 0 && (rv = GetBastardOption(opt)) > -1) {
      if (rv > 0) sprintf(msg, "%s = ON\n", opt);
      else sprintf(msg, "%s = OFF\n", opt);
      Message(msg);
   } 
   return;
}

void cmdSHOW(int argc, char **argv, struct COMMAND *c){
   /* show internal ENV variables */
   char *opt, msg[64];
   int rv;

   if (! argc || !argv[0]) opt = NULL;
   else opt = argv[0];

   if ((rv = GetBastardOption(opt)) > -1) {
      if (rv > 0) sprintf(msg, "%s = ON\n", opt);
      else sprintf(msg, "%s = OFF\n", opt);
      Message(msg);
   } 
   return;
}

void cmdDB(int argc, char **argv, struct COMMAND *c){
   Message("Seek HELP\n");
   return; 
}


void cmdLOAD(int argc, char **argv, struct COMMAND *c){
   /* generic file load/disassembler. Assumes a.out, ELF, i386 */
   struct DISASM_ENV *disasm_env = GetBastardEnv();
   struct DISASM_TGT *target = GetBastardTgt();
   int err = 0;

   /* wrapper for multiple API routines */
   if (disasm_env->flags & DB_LOADED) CloseDB(); //duplicated in LoadDB but nec.
   if (argc && argv[0][0]) strncpy(target->name, argv[0], 63);
   if (argc > 1 && argv[1][0]) strncpy(target->format, argv[1],15);
   if (argc > 2 && argv[2][0]) strncpy(target->arch, argv[2], 31);
   if (argc > 3 && argv[3][0]) strncpy(target->assembler, argv[3], 31);
   if (argc > 4 && argv[4][0]) strncpy(target->lang, argv[4], 31);
   
   if ( target->status < DISASM_TGT_LOADED && 
         ! LoadTarget(target->name) ) {
      DEBUG_Print("LoadTarget Failed!\n");
      err = GetLastError();
   } else if ( target->status < DISASM_TGT_FORMAT_SET && 
         ! SetTargetFormat(target->format) ) {
      DEBUG_Print("SetTargetFormat Failed!\n");
      err = GetLastError();
   } else if ( target->status < DISASM_TGT_ARCH_SET && 
         ! SetTargetArch(target->arch) ) {
      DEBUG_Print("SetTargetArch Failed!\n");
      err = GetLastError();
   } else if ( target->status < DISASM_TGT_ASM_SET && 
         ! SetTargetAssembler(target->assembler)) {
      DEBUG_Print("SetTargetAsm Failed!\n");
      err = GetLastError();
   } else if ( target->status < DISASM_TGT_LANG_SET && 
         !SetTargetLanguage(target->lang) ) {
      DEBUG_Print("SetTargetLang Failed!\n");
      err = GetLastError();
   } else if ( target->status < DISASM_TGT_DISASM && 
         ! DisassembleTarget("full", NULL )){ 
      DEBUG_Print("DisassembleTarget Failed!\n");
      err = GetLastError();
   }
   
   if (err) PrintErrorMsg(err); 

   return;
}

void cmdEXEC(int argc, char **argv, struct COMMAND *c){
   int x, len;
   char cline[256] = {0};
   struct DISASM_ENV *disasm_env = GetBastardEnv();

   if (! (disasm_env->flags & DISABLE_EXEC)) {
      if (argc && argv[0] ){
         sprintf( cline, "/bin/sh -c");
         for (x = 0; x < argc; x++ ){
            len =256 - strlen(cline);
            len = (len > 0 ) ? len : 0;
            strncat(cline, " ", len);
            strncat(cline, argv[x], --len);
            
         }
      } else { 
         strcpy(cline, "/bin/sh -ri");
      }
      system(cline);
   }
   return;
}

void cmdDEBUG(int argc, char **argv, struct COMMAND *c){
   /* debug the target executable */
   /* maps to API routine */
   Debug();
   return;
}

void cmdQUIT(int argc, char **argv, struct COMMAND *c){
   /* leave the app */
   int x; 
   FILE *f;
   char buf[PATH_MAX];
   HIST_ENTRY *hist;
   
   struct DISASM_ENV *disasm_env = GetBastardEnv();
   struct DISASM_PREFS *disasm_prefs = GetBastardPrefs();
   x = disasm_prefs->num_hist;

   sprintf(buf, "%s/.bastard/cmd_history", getenv("HOME"));
   if ( f = fopen(buf, "w") ) {
      while( x >= 0 && ( hist = previous_history() ) ){
         if (hist->line[0] != '\n') fprintf(f, "%s\n", hist->line);
         x--;
      }
   }
   /* get history for saving in config db */
   /*while( x >= 0 && ( hist = previous_history() ) ){
      AddHistCmd(x, hist->line);
      x--;
   } */
   Quit();
   disasm_env->flags &= QUIT_NOW;
   if ( ! (disasm_prefs->options & QUIET)) printf("\nYou bastard!\n");
   else  pclose(  popen("stty echo", "r") );

   exit(0);
}

void cmdHEADER(int argc, char **argv, struct COMMAND *c){
   PrintHeader();
   return;
}

void cmdSECTION(int argc, char **argv, struct COMMAND *c){
   char *tmp, name[32], secStr[128];
   struct section sec;
   struct address addr;
   int cont;
   
   if (! argc || !argv[0]){
      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 {
      if (! DBIndexFind(SECTION_NAME, argv[0], &sec)) return;
      if (! (cont = DBIndexFind(ADDRESS_RVA, &sec.rva, &addr)) ) return;
      while( cont && addr.rva < (sec.rva + sec.size)) {
         PrintAddress( addr.rva );
         DBIndexNext(ADDRESS_RVA, &addr);
      }
   }
   return;
}

void cmdRANGE(int argc, char **argv, struct COMMAND *c){
   long start_rva, end_rva;
   char *s1=NULL, *s2=NULL;
   struct address addr;
   int cont, *state;
   
   if (argc < 2 ) {
      Message("Specify a starting and ending address!\n");
      return;
   }
   /* get starting and ending address */
   start_rva = strtol(argv[0], NULL, 16);
   end_rva = strtol(argv[1], NULL, 16);
   if ( (s1 && s1[0]) || (s2 && s2[0])){
      Message("Specify starting and ending addresses in hex!\n");
      return;
   }
   /* find closest addr (start_addr) */
   cont = DBFindClosestNext(ADDRESS_RVA, &start_rva, &addr);

   /* while (addr< end_addr) addr = get_addr_from_db() */
   while (cont && addr.rva <= end_rva) {
      state = DBSaveState();
      PrintAddress( addr.rva );
      DBRestoreState(state);
      cont= DBIndexNext(ADDRESS_RVA, &addr);
   }

   return;
}

void cmdBDB(int argc, char **argv, struct COMMAND *c){
   if (! argc || ! argv[0][0]){
      Message("Please specify a .bdb file to load!");
      return;
   }
   LoadBDB(argv[0]);
   
   return;
}

void cmdDISASM(int argc, char **argv, struct COMMAND *c){
   struct address addr;
   struct section sec;
   int cont, *state;
   FILE *oldSTDOUT, *fd = NULL;

   if (argc && argv[0][0]) {
      oldSTDOUT = stdout;
      stdout = fd = fopen(argv[0], "w");
   }
   cont = DBIndexFirst(SECTION_RVA, &sec);
   while( cont ) {

      if ( (cont = DBFindClosestNext(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( cont && addr.rva < sec.rva + sec.size){
            state = DBSaveState();
            PrintAddress( addr.rva );
            DBRestoreState(state);
            cont = DBIndexNext(ADDRESS_RVA, &addr);
         }
      printf("\n\n");
      }
      cont = DBIndexNext(SECTION_RVA, &sec);
   }
   if (fd) {
      stdout = oldSTDOUT;
      fclose(fd);
   }
   return;
}

void cmdDUMP(int argc, char **argv, struct COMMAND *cmd){
   int x, y;
   unsigned char hex[48+4], ascii[16+4], c;
   struct DISASM_TGT *target = GetBastardTgt();

   for (x = 0; x < target->size; x += y) {
      memset(hex, 0, 52);
      memset(ascii, 0, 20);
      for( y = 0; x + y < target->size && y < 16; y++){
         c=((char*)target->image)[x+y];
         sprintf(hex,"%s%02X ",hex,c);
         if ( isprint(c) ) ascii[y] = c;
         else ascii[y] = '.';
      }
      /* pad last line if < 16 bytes */
      if ( y < 15)
         for (; y < 16; y++)
            sprintf(hex, "%s   ", hex);
      printf("%08X : %s\t%s\n", x, hex, ascii);
   }
}

void cmdSTRINGS(int argc, char **argv, struct COMMAND *c){
   DumpStrings();
   return;
}

void cmdViewDocFile(char *filename){
   char cline[PATH_MAX];
   struct DISASM_ENV *disasm_env = GetBastardEnv();
   struct DISASM_PREFS *disasm_prefs = GetBastardPrefs();

   snprintf( cline, PATH_MAX, "/bin/sh -c \'%s %s/doc/%s\'", 
                   disasm_prefs->pager, disasm_env->home, filename);
            
   system(cline);
   return;
}
void cmdMAN(int argc, char **argv, struct COMMAND *c){
      cmdViewDocFile("bastard.txt");
      return;
}
void cmdAPI(int argc, char **argv, struct COMMAND *c){
      cmdViewDocFile("../include/bastard.h");
      return;
}
void cmdHOWTO(int argc, char **argv, struct COMMAND *c){
      cmdViewDocFile("Disasm-HOWTO.txt");
      return;
}


void cmdMACRO(int argc, char **argv, struct COMMAND *c){
   /* write a SeerC script */
   /* maps to API routine */
   struct DISASM_ENV *disasm_env = GetBastardEnv();
   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;
}

void cmdENDMACRO(int argc, char **argv, struct COMMAND *c){
   Message("ENDMACRO is only valid in the context of a MACRO block!\n");
   return;
}

void cmdRECORD(int argc, char **argv, struct COMMAND *c){
   /* record commandline actions ... may be useless */
   /* maps to API routine */
   Message("Not yet implemented\n");
   return;
}

void cmdSTOP(int argc, char **argv, struct COMMAND *c){
   Message("Not yet implemented\n");
   return;
}

void cmdRUN(int argc, char **argv, struct COMMAND *c){
   /* Run Macro */
   if (argc && argv[0][0]) RunScript(argv[0]);
   return;
}

void cmdBRACE(int argc, char **argv, struct COMMAND *c){
   char *line, path[512];
   int x, cont = 1;
   FILE *f;
   struct DISASM_ENV *disasm_env = GetBastardEnv();

   //src = calloc(1, 1);           
   snprintf(path, 512, "%s/scripts/.tmp_macro", disasm_env->home);
   f = fopen( path, "w"); 
   if (! f) return;
   fputs("#include <string.h>\n", f);
   fputs("#include <unistd.h>\n", f);
   fputs("#include <stdlib.h>\n", f);
   fputs("int main(void) {\n", f );
   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");
         fprintf(f, "%s\n", line);
      }
      if (line) free(line);
   } 
   fputs("return(0);\n}", f);
   fclose(f);

   //x = ExecMacroLine(src);
   ExecScript(path, 0, NULL);
   return;
}

