#include <api/api_main.h>
#include <extension.h>

/* default settings */
static char defaultAsmFmt[] = "%n%:%^%a %8b\t%m\t%d%, %s %;%c %x";
static char defaultDataFmt[] = "%n%:%^%a %8b\t%;%c %x";

struct DISASM_ENV  disasm_env    = {0};
struct DISASM_PREFS disasm_prefs = {0};
struct DISASM_TGT target         = {0};

/* Tables for managing the settings structs via character params */
/* NOTE these only contain tables which are likely to be manually, by
 * the user or by a config file */
#define T_INT      1
#define T_CADDR   2
#define T_SIZE      3
#define T_STR      4
#define T_CHARPTR   5
#define T_LONG      6

struct SETTINGS_TBL {
   char *name;
   int type;
   int size;
   void *ptr;
} disasmtgt_tbl[] = {
   {"fd",        T_INT,     sizeof(int),     &target.FD},
   {"endian",    T_INT,     sizeof(int),     &target.endian},
   {"image",     T_CADDR,   sizeof(caddr_t), &target.image},
   {"size",      T_SIZE,    sizeof(size_t),  &target.size},
   {"entry",     T_LONG,    sizeof(long),    &target.entry},
   {"cpu",       T_STR,     32,              target.arch},
   {"format",    T_STR,     32,              target.format},
   {"lang",      T_STR,     32,              target.lang},
   {"assembler", T_STR,     32,              target.assembler},
   {"header",    T_CHARPTR, sizeof(char *),  &target.header},
   {"name",      T_STR,     64,              target.name},
   {"path",      T_STR,     PATH_MAX,        target.path},
   {"dbname",    T_STR,     PATH_MAX,        target.dbname},
   { NULL, 0, 0, NULL}
}, disasmenv_tbl[] = {
   {"home",        T_STR,      256,            disasm_env.home},
   {"p1",          T_STR,      16,             disasm_env.p1},
   {"p2",          T_STR,      16,             disasm_env.p2},
   {"p3",          T_STR,      16,             disasm_env.p3},
   {"p4",          T_STR,      16,             disasm_env.p4},
   {"dbpath",      T_STR,      256,            disasm_env.dbpath},
   {"output",      T_INT,      sizeof(int),    &disasm_env.output},
   {"flags",       T_INT,      sizeof(int),    &disasm_env.flags},
   {"endian",      T_INT,      sizeof(int),    &disasm_env.endian},
   { NULL, 0, 0, NULL}
}, disasmprefs_tbl[] = {
   {"options",    T_INT, sizeof(int),  &disasm_prefs.options},
   {"pager",    T_STR, PATH_MAX, disasm_prefs.pager},        
   {"editor",   T_STR, PATH_MAX, disasm_prefs.editor},        
   {"debugger", T_STR, PATH_MAX, disasm_prefs.debugger},        
   {"num_hist",    T_INT,      sizeof(int),    &disasm_prefs.num_hist},
   { NULL, 0, 0, NULL}
};

struct SETTING_STRUCTS_TBL {
   char *name;
   struct SETTINGS_TBL *table;
} bastard_settings[] = {
{ "target", disasmtgt_tbl},
{ "disasm_env", disasmenv_tbl},
{ "disasm_prefs", disasmprefs_tbl},
{ NULL, NULL}
};


/* --------------------------------------------------------------------- */
struct __CHARTOCONSTRUCT{
   char *name;
   int value;
} bastard_flags[] = {
   {"DB_LOADED",       0x00000001},        /* .bdb loaded */
   {"TGT_MAPPED",      0x00000002},        /* target bin image mmap'ed */
   {"DB_MOD",          0x00000004},        /* .bdb has been modified */
   {"TGT MOD",         0x00000008},        /* target bin image modified */
   {"PASS_1",          0x00000010},        /* main disassembly pass */
   {"PASS_2",          0x00000020},        /* subsequent passes */
   {"CODE_INT",        0x00000040},        /* intermediate code rep is done */
   {"CODE_FIN",        0x00000080},        /* final code rep is done */
   {"DISASM_TGT_LOADED",        0x01},
   {"DISASM_TGT_FORMAT_SET",    0x02},
   {"DISASM_TGT_ARCH_SET",      0x03},
   {"DISASM_TGT_ASM_SET",       0x04},
   {"DISASM_TGT_LANG_SET",      0x05},
   {"DISASM_TGT_PREDISASM",     0x06},
   {"DISASM_TGT_DISASM",        0x07},
   {"DISASM_TGT_POSTDISASM",    0x08},
   {"DISASM_TGT_INTCODE",       0x09},
   {"DISASM_TGT_FINCODE",       0x0A},
   {NULL,       0x00},
}, bastard_opts[] = {
   {"DISABLE_EXEC",    0x00010000},  /* do not allow shell escapes */
   {"QUIT_NOW",        0x00080000},  /* Easier this way */
   {"PAGINATE",        0x00000100},
   {"COLOR_TTY",       0x00000200},
   {"QUIET",           0x00001000},  /* print std msgs to STDOUT */
   {"ANNOY_USER",      0x00002000},  /* bug user with save prompts, etc */
   {"DEBUG_TIME",      0x00008000 }, /* horrendous debug output */
   {"DISASM_REDO",     0x00100000},  /* redo existing code [default: skip it] */
   {NULL,       0x00},
}, internal_consts[] = {
   {"BIG_ENDIAN_ORD",       0},
   {"LITTLE_ENDIAN_ORD",    1},
   {"ASM_OUTPUT_TTY",       0x00},
   {"ASM_OUTPUT_FILE",      0x01},
   {"ASM_OUTPUT_PRINTER",   0x02},
   {"ASM_OUTPUT_TTY_COLOR", 0x03},
   {NULL,       0x00},
};
int symtoval( struct __CHARTOCONSTRUCT *type, char *sym){
   char str[512];
   int x;

   for (x = 0; type[x].name != NULL; x++){
      if (! istrncmp(sym, type[x].name, strlen(sym)))
         return(type[x].value);
   }
   return(-1);
}


/* Disasm Preferences ------------------------------------------------- */
void * GetBastardSetting(char *group, char *name){
   int x, y;
   struct SETTINGS_TBL *tbl;

   for (x = 0; bastard_settings[x].name; x++ ) {
      if (! istrncmp( group, bastard_settings[x].name, strlen(group)) ){
         /* search the table for this group looking for 'name' */
         tbl = bastard_settings[x].table;
         for (y = 0; tbl[y].name; y++) {
            if (! istrncmp(name, tbl[x].name, strlen(name)) )
               return(tbl[x].ptr);
         }
      }
   }
   return(0); /* bas group/name */
}

int SetBastardSetting(char *group, char *name, char *val){
   int x, y;
   struct SETTINGS_TBL *tbl;

   for (x = 0; bastard_settings[x].name; x++ ) {
      if (! istrncmp( group, bastard_settings[x].name, strlen(group)) ){
         /* search the table for this group looking for 'name' */
         tbl = bastard_settings[x].table;
         for (y = 0; tbl[y].name; y++) {
            if (! istrncmp(name, tbl[y].name, strlen(name)) ){
               switch( tbl[x].type ){
                  /*   -------- Copy String Directly ---------- */
                  case T_STR:
                     strncpy((char *)tbl[x].ptr, val, tbl[x].size); break;
                  /*   -------- Disallow Pointer Setting ---------- */
                  case T_CADDR:
                     //*((caddr_t *)tbl[x].ptr) = *((caddr_t *)val); break;
                  case T_CHARPTR:
                     //*(char **)tbl[x].ptr = (char *) val; break;
                     return(0); /* not allowed to set these */
                  /*   -------- Convert from a Hex # String to a Long ------ */
                  case T_SIZE:
                     //*((size_t *)tbl[x].ptr) = *((size_t *)val); break;
                  case T_LONG:
                     //*(long *)tbl[x].ptr = *((long *)val); break;
                  case T_INT:
                  default:
                     //*((int *)tbl[x].ptr) = *((int *)val); break;
                     *(long *)tbl[x].ptr = strtol(val, NULL, 16); break;
                     break;
               }
               return(1);
            }
         }
      }
   }
   return(0); /* bad group/name */
}
int SetBastardOption( char *name ){
   int opt;
   char buf[64];

   if (! name ) {
      /* print out all options and return NULL) */
      for (opt = 0; bastard_opts[opt].name != NULL; opt++){
         sprintf(buf, "%s\n", bastard_opts[opt].name);
         Message(buf);
      }
      return(-1);
   }
   if ( (opt = symtoval(&bastard_opts, name)) > -1) {
      if (disasm_prefs.options & opt) disasm_prefs.options ^= opt;
      else disasm_prefs.options |= opt;
      return(1);
   }
   return(0);
}
int GetBastardOption( char *name ){
   int opt;
   char buf[64];

   if (! name ) {
      /* print out all options and values and return NULL) */
      for (opt = 0; bastard_opts[opt].name != NULL; opt++){
         if (disasm_prefs.options & bastard_opts[opt].value)
            sprintf(buf, "%s=ON\n", bastard_opts[opt].name); 
         else
            sprintf(buf, "%s=OFF\n", bastard_opts[opt].name); 
         Message(buf);
      }
      return(-1);
   }
   if ( (opt = symtoval(&bastard_opts, name)) > -1) {
      if (disasm_prefs.options & opt) return(1);
      return(0);
   }
   return(-1);
}
int SetPager(char *name) {
   strncpy(disasm_prefs.pager, name, PATH_MAX);
   return(1);
}
int SetEditor(char *name){ 
   strncpy(disasm_prefs.editor, name, PATH_MAX);
   return(1);
}
int SetDebugger(char *name){ 
   strncpy(disasm_prefs.debugger, name, PATH_MAX);
   return(1);
}
/* convenience routines */
char * GetBastardHome(){ return(disasm_env.home); }
int GetTargetFD(){ return(target.FD); }
char * GetTargetImage() { return((char *)target.image); }
int GetTargetSize() { return((int) target.size); }

char * GetPager() { return(disasm_prefs.pager); }
char * GetEditor(){ return(disasm_prefs.editor); }
char * GetDebugger(){ return(disasm_prefs.debugger); }
/* these allow the structures to be accesses directly, for speed/convenience */
struct DISASM_ENV   * GetBastardEnv()   { return(&disasm_env);   }
struct DISASM_PREFS * GetBastardPrefs() { return(&disasm_prefs); }
struct DISASM_TGT   * GetBastardTgt()   { return(&target);       }

/* These clear the settings structures */
int ClearTarget() {return((int)memset(&target, 0, sizeof(struct DISASM_TGT))); }
int ClearEnv() {
        /* do not overwrite home dir */
        memset(&disasm_env + 128, 0, sizeof(struct DISASM_ENV) - 128);
        return(1);
}

/* these fill the settings structures with "factory defaults" */
int SetEnvDefaults(){
   struct stat s;

   disasm_env.flags = 0;

   strcpy(disasm_env.p1, ";>");
   strcpy(disasm_env.p2, "\\");
   strcpy(disasm_env.p3, " -");
   strcpy(disasm_env.p4, " ?");
   strcpy(disasm_env.dbpath, disasm_env.home);
   sprintf(disasm_env.dbpath, "%s/.bastard", getenv("HOME"));
   if (stat(disasm_env.dbpath, &s)) mkdir(disasm_env.dbpath, 0700);

   disasm_env.endian = endian_check(); /* in util.c */
   disasm_env.output = ASM_OUTPUT_TTY;
        
   return(1);
}

int SetTargetDefaults(){
   strcpy(target.name, "a.out");
   strcpy(target.arch, "i386");
   strcpy(target.format, "ELF");
   strcpy(target.lang, "C");
   strcpy(target.assembler, "intel");
   return(1);
}

int SetPrefDefaults(){
   strcpy(disasm_prefs.pager, "less");
   strcpy(disasm_prefs.editor, "vim");
   strcpy(disasm_prefs.debugger, "gdb");
   disasm_prefs.num_hist = 64;
   return(1);
}

   int GetEnvFlag( int flag ){
      if (disasm_env.flags & flag) 
         return(1);         
      return(0);
   }

   int SetEnvFlag( int flag ){
      disasm_env.flags != flag;         
      return(0);
   }

   int ClearEnvFlag( int flag ){
      disasm_env.flags ^= flag;         
      return(0);
   }

   int GetOption( int flag ){
      if (disasm_prefs.options & flag) 
         return(1);         
      return(0);
   }

   int SetOption( int flag ){
      disasm_prefs.options != flag;         
      return(0);
   }

   int ClearOption( int flag ){
      disasm_prefs.options ^= flag;         
      return(0);
   }

/* WARNING: these next can cause memory leaks if used over and over. 
 * An API in the asm extension should be provided for these ! */
   void SetTtyAsmFmt(char *fmt){
      if ( fmt && ext_asm->asm_ttyMono != fmt ) {
         ext_asm->asm_ttyMono = malloc(strlen(fmt) + 1);
         strcpy(ext_asm->asm_ttyMono, fmt);
      }
      return;
   }

   void SetTtyDataFmt(char *fmt){
      if ( fmt && ext_asm->data_ttyMono != fmt ) {
         ext_asm->data_ttyMono = malloc(strlen(fmt) + 1);
         strcpy(ext_asm->data_ttyMono, fmt);
      }
      return;
   }

   void SetFileAsmFmt(char *fmt){
      if ( fmt && ext_asm->asm_file != fmt) {
         ext_asm->asm_file = malloc(strlen(fmt) + 1);
         strcpy(ext_asm->asm_file, fmt);
      }
      return;
   }

   void SetFileDataFmt(char *fmt){
      if ( fmt && ext_asm->data_file != fmt ) {
         ext_asm->data_file = malloc(strlen(fmt) + 1);
         strcpy(ext_asm->data_file, fmt);
      }
      return;
   }

   void SetLprAsmFmt(char *fmt){
      if ( fmt && ext_asm->asm_lpr != fmt) {
         ext_asm->asm_lpr = malloc(strlen(fmt) + 1);
         strcpy(ext_asm->asm_lpr, fmt);
      }
      return;
   }

   void SetLprDataFmt(char *fmt){
      if ( fmt && ext_asm->data_lpr != fmt) {
         ext_asm->data_lpr = malloc(strlen(fmt) + 1);
         strcpy(ext_asm->data_lpr, fmt);
      }
      return;
   }

