#include <cli/bastard_main.h>


/* protos */

struct ACTION static cmd_table[] = {
   { "HELP", '?', 0, &cmd_help, 
           "                        \t: This help screen." },
   { "API", 0, 0, &cmdAPI, 
          "                         \t: Display bastard.h"},
   { "BDB", 'B', 0, &cmdBDB, 
          "[filename]               \t: Load a .bdb file" },
   { "DB", 0, 1, &db_cmd, 
         "[command]                 \t: Execute a database command. Try \'DB ?\'" },
   { "DEBUG", 0, 0, &cmdDEBUG, 
            "                       \t: Run target  in an external debugger" },
   { "DISASM", 'D', 0, &cmdDISASM, 
             "[filename]            \t: Display a disassembly of the target; use \'filename\' instead of STDOUT if specified."},
   { "DUMP", 'U', 0, &cmdDUMP, 
           "                        \t: Print hexdump of target"},
   { "EXEC", '!', 0, &cmdEXEC, 
           "[command]               \t: Execute command in a shell" },
   { "HEADER", 'H', 0, &cmdHEADER, 
             "                      \t: Display target file header" },
   { "HOWTO", 0, 0, &cmdHOWTO, 
            "                       \t: Display the Disasm-HOWTO"},
   { "LOAD", 'L', 0, &cmdLOAD, 
           " [target] [format] [cpu]\t: Load/disassemble target. Assumes \'a.out\', \'ELF\', and \'ix86\' if parameters are not specified." },
   //{ "MACRO", 0, 0, &cmdMACRO, "[name]                 \t: Start writing a command-line macro/batch file" },
   { "MAN", 'M', 0, &cmdMAN, 
          "                         \t: Display bastard manual"},
   //{ "ENDMACRO", 0, 0, &cmdENDMACRO, "\t: Stop writing a command-line macro/batch file" },
   { "QUIT", 'Q', 0, &cmdQUIT, 
           "                        \t: Exit the disassembler" },
   { "RANGE", 'R', 0, &cmdRANGE, 
            "[start] [end]          \t: Display disassembly of addresses between \'start\' and \'end\'" },
   //{ "RECORD", 0, 0, &cmdRECORD, "[name]\t: Start recording a command-line macro/batch file" },
   //{ "STOP", 0, 0, &cmdSTOP, "\t: Stop recording" },
   { "RUN", 0, 0, &cmdRUN, 
          "[name]                   \t: Run a macro/batch file" },
   { "SECTION", 'S', 0, &cmdSECTION, 
              "[name]               \t: Print section \'name\'" },
   { "SET", 0, 0, &cmdSET, 
          "[variable] [value]       \t: Set bastard flags."},
   { "SHOW", 0, 0, &cmdSHOW, 
           "[variable]              \t: Display settings of bastard flags" },
   { "STRINGS", 'A', 0, &cmdSTRINGS, 
              "                     \t: Display strings in target"},
   { "{{", 0, 0, &cmdBRACE, 
         "                          \t: Write and execute a BC script" },
   { NULL, 0, 0, NULL, NULL}
};

struct SUBACTION  static 
   db_tbl[] = { /* 1 */
      { "HELP", &subcmd_help, "DB Help: This help screen." },
      { "COUNT", &db_cmd, "[table]\t: Count number of records in table" },
      { "DESC", &db_cmd, "[table]\t: Describe table"},
      { "DUMP", &db_cmd, "[table]\t: Dump contents of table to STDOUT" },
      { "FIND", &db_cmd, "[table] [key] [value]\t: Find record in table where key == value" },
      { "MAX", &db_cmd, "[table] [key]\t: Find record in table where key has the greatest value" },
      { "MIN", &db_cmd, "[table] [key]\t: Find record in table where key has the least value" },
      { NULL, NULL, NULL}
   },
   /*set_tbl[] = { // 2
      { "HELP", &subcmd_help, "SET Help: This help screen" },
      { NULL, NULL, NULL}
   },
   show_tbl[] = { // 3
      { "HELP", &subcmd_help, "SHOW Help: This help screen" },
      { NULL, NULL, NULL}
   }, */
   *sub_cmd_tbl[] = { db_tbl }; //, set_tbl, show_tbl };;


/* ======================================= Actual functions */
          
int istrcmp( char *s1, char *s2 ){
      int j;
      
      for (j = 0; ; j++){
         if ( (!s1[j] || isspace(s1[j])) && !s2[j] ) break;
         if ( toupper(s1[j]) != s2[j] ) return(0);
         if (!s1[j] || !s2[j]) return(0);         
      }
      return(j);
}

int ProcessCommand( char *buf) {
   int i, pos=0, start = 0;
   struct ACTION *cmd;
   struct SUBACTION *sublist, *subcmd;
   struct COMMAND *cmdline;
   char *ptr, u;
   FILE *pipe, *old_out;

   
   /* strip leading whitespace */
   while (isspace(buf[start])) ++start;
   /* strip trailing space and newline */
   i = strlen(buf) - 1;
   if (isspace(buf[i])) buf[i] = 0;

   /* =================== Get Main Command ======================= */
   u = toupper(buf[start]);
   for ( i = 0; cmd_table[i].name != NULL; i++) {
      cmd =  &cmd_table[i];
      /* check for match against alias */
      if (u == cmd->alias && (! buf[start+1] || isspace(buf[start+1]))  ) {
         pos++; /* advance past command */
         break;
      }
      /*check for match against name */
      if (pos = istrcmp( &buf[start], cmd->name)) 
         break;
      cmd = NULL; //&cmd_table[0]; /* HELP is always 0 */
   }

   if (! cmd) return(0); /* no match */
   /* At this point, command is filled */
   cmdline  = (struct COMMAND *) calloc(sizeof(struct COMMAND), 1);
   if (! cmdline) return(0);
   strncpy(cmdline->command, cmd->name, 32);
   cmdline->fn = cmd->handler;

   /* ================== Get SubCommand ========================== */
   start = pos;
   while ( isspace(buf[start])) ++start;
   buf = &buf[start];
   start = pos = 0;

   if (buf[start] && cmd->sub_cmd) {
      sublist = sub_cmd_tbl[cmd->sub_cmd - 1];
      for (i = 0; sublist[i].name != NULL; i++ ){
         subcmd = &sublist[i];
         if (pos = istrcmp(&buf[start], subcmd->name))
            break;
         subcmd = &sublist[0]; /* HELP is always 0 */
      }
      sublist = NULL;
      strncpy(cmdline->subcmd, subcmd->name, 32);
      cmdline->sub_tbl = cmd->sub_cmd - 1;
      cmdline->fn = subcmd->handler;
   }

   /* ================== Check for extension =======================*/
   start = pos;
   if (buf[0] && 
         (ptr = strchr(&buf[start], '|')) ||
         (ptr = strchr(&buf[start], '>')) ){

      if (*ptr == '|') cmdline->extension = 1;
      else cmdline->extension = 2;
      ptr[0] = '\0';   /* end command line here */
      ptr++;
      /* remove newline and leading whitespace */
      while( isspace(ptr[0]) ) ptr++;
      if (ptr[strlen(ptr) -1] =='\n') ptr[strlen(ptr)-1] = '\0';
      cmdline->cline = malloc( 1024 );
      if (! cmdline->cline) return(0);
      /* text following "|" becomes command line */
      strncpy(cmdline->cline, ptr, 1023);
   }

   /* ========================= Get Args ============================== */
   while (isspace(buf[start])) start++;
   buf = &buf[start];
   pos = start = 0;
   if (buf[pos])  { /* don't even try this if there is no more data */
      cmdline->argc++;
      while( buf[pos] ) {
         if (isspace(buf[pos])) {
            while( isspace(buf[pos]) ) pos++; /* suck up all whitespace */
            if (buf[pos]) cmdline->argc++;   /* ignore space at end of line */
         }
         pos++;
      }
      cmdline->argv = calloc(cmdline->argc,  sizeof(char *));
      if (! cmdline->argv) return(0);
   
      if (cmdline->argc) cmdline->argv[0] = &buf[start];
      i = 1;
      while( cmdline->argc && buf[start] && i < cmdline->argc){
         if (isspace(buf[start])) {
            buf[start] = '\0';
            while (isspace(buf[++start])) start++;
            if( buf[start]) { 
               cmdline->argv[i] = &buf[start];
               i++; /* increment arg count */
            }
         } else start++;
      }
   } /* end arg processing */
         
   /* ================== Call fn to Handle Command ==================== */
   if (! cmdline->extension && (GetOption(PAGINATE)) ){
      cmdline->extension = 1;
      cmdline->cline = GetPager();
   }
   if (cmdline->extension == 1) {
      pipe = popen(cmdline->cline, "w");
      old_out = stdout;
      stdout = pipe;
   } else if (cmdline->extension == 2) {
      pipe = fopen(cmdline->cline, "a");
      old_out = stdout;
      stdout = pipe;
   }
   ((CmdHandler)*cmdline->fn)(cmdline->argc, cmdline->argv, cmdline);
   if (cmdline->extension == 1) {
      stdout = old_out;
      pclose(pipe);
   } else if (cmdline->extension == 2) {
      stdout = old_out;
      fclose(pipe);
   }

   if (cmdline->argv) free(cmdline->argv);
   if (cmdline->cline) free(cmdline->cline);
   free(cmdline);
   return(1);
}

void *cmd_help(int argc, char **argv, struct COMMAND *c){
   int x;

   printf("Help Topic: MAIN\n");
   for (x = 0; cmd_table[x].name != NULL; x++){
      printf("   %s %s", cmd_table[x].name, cmd_table[x].helpstr);
      if( cmd_table[x].alias) printf(" (Alias: %c)", cmd_table[x].alias );
      printf("\n");
   }
   return;
}
void *subcmd_help(int argc, char **argv, struct COMMAND *c){
   /*print ->name, ->alias, ->helpstr */
   struct SUBACTION *sublist;
   int x;
   
   printf("Help Topic: %s\n", c->command);
   sublist = sub_cmd_tbl[c->sub_tbl];
   for (x = 0; sublist[x].name != NULL; x++){
      printf("   %s %s\n", sublist[x].name, sublist[x].helpstr);
   }
   return;
}
