/* Copyright Yann Weber This file is part of asmsh. asmsh is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. asmsh is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with asmsh. If not, see . */ #include "completion.h" char **asmsh_completion(const char *buf, const char *text, int start, int end) { if(end == 0) { char **instr, **cmds, **ptr; int ilen, clen; cmds = asmsh_compl_cmds(text); instr = asmsh_compl_instr(text); ilen=clen=0; for(ptr=cmds; *ptr; ptr++) { clen++; } for(ptr=instr; *ptr; ptr++) { ilen++; } ptr = realloc(cmds, sizeof(*cmds) * (ilen+clen+1)); if(!ptr) { perror("Unable to realloc all completions"); free(cmds); free(instr); return NULL; } cmds=ptr; ptr=&cmds[clen]; memcpy(&cmds[clen], instr, ilen*sizeof(*instr)); cmds[ilen+clen]=NULL; return cmds; } if(*buf != '.') { if(start == 0) { return asmsh_compl_instr(text); } return asmsh_compl_arg(buf, text, start); } return asmsh_compl_cmds(text); } /// Replace uppper char leading macro if needed static int _completion_upper_macro(const char *macro, const char *start, char **ret, size_t *i); char **asmsh_compl_instr(const char *start) { const size_t icount = sizeof(x86_64_instr)/sizeof(*x86_64_instr); size_t i,j; char **ret; int start_len, cmp; ret = malloc(icount * 2 * sizeof(char*)); if(!ret) { perror("Unable to allocate completion result"); return NULL; } bzero(ret, icount * 2 * sizeof(char*)); if(!start || !*start) { i=0; for(j=0; j 0) { continue; // handled by macro } // normal comparison cmp = strncmp(start, x86_64_instr[j].mnemo, start_len); if(cmp == 0) { ret[i]=strdup(x86_64_instr[j].mnemo); if(!ret[i]) { goto err_strdup; } i++; } else if (cmp < 0) { break; } } } return ret; err_strdup: perror("Error while copying string for completion"); i--; do { free(ret[i]); }while(i>0); free(ret); return NULL; } char **asmsh_compl_cmds(const char *start) { const size_t ccount = sizeof(asmsh_CMDS)/sizeof(*asmsh_CMDS); int i, j, slen, cmp; char **ret; const asmsh_cmd_t *ccmd; ret = malloc(sizeof(*ret) * (ccount+1)); if(!ret) { perror("Unable to allocate cmd completion result"); return NULL; } bzero(ret, sizeof(*ret) * (ccount+1)); slen = (!start || !*start)?0:strlen(start); j=0; for(i=0; istr) { break; } cmp = slen?strncmp(start, ccmd->str, slen):1; if(!slen || !cmp) { if(!(ret[j] = strdup(ccmd->str))) { perror("Unable to strdup cmd completion"); goto errdup; } j++; } if(cmp < 0) { break; } } return ret; errdup: i--; for(; i>=0; i--) { free(ret[i]); } free(ret); return NULL; } /// match against macro static int _completion_macro_match(const char *macro, const char *buf, int mlen); char **asmsh_compl_arg(const char *buf, const char *text, int start) { const asmsh_compinstr_t *match = NULL; int argno; int i, tlen; int matchlen = 0; int macro = 0; char **ret = malloc(sizeof(char*)*512); /* large enough */ if(!ret) { perror("unable to allocate buffer"); return NULL; } bzero(ret, sizeof(char*)*512); tlen = text?strlen(text):0; for(i=0; i<(sizeof(x86_64_instr)/sizeof(*x86_64_instr))-1; i++) { int cmp; const char *instr = x86_64_instr[i].mnemo; int ilen = strlen(instr); macro=0; if(instr[ilen-1] >= 'A' && instr[ilen-1] <= 'Z') { ilen--; macro = 1; cmp = _completion_macro_match(instr, buf, ilen); if(cmp < 0) { goto err; } } else { cmp = strncmp(buf, instr, ilen); } if(cmp < 0) { break; } else if(cmp > 0) { continue; } else if(!macro && (buf[ilen] != ' ' && buf[ilen] != '\t')) { continue; //not a full match } // match or approximate macro match match = &x86_64_instr[i]; matchlen = ilen; while(buf[matchlen] && buf[matchlen] != ' ' && buf[matchlen] != '\t') { matchlen++; } break; } if(!match) { ret = malloc(sizeof(char*)); *ret = NULL; return ret; } argno=0; for(i=matchlen; i<=start; i++) { if(buf[i] == ',') { argno=1; } } int args = argno?match->arg2:match->arg1; if(macro && match->mnemo[strlen(match->mnemo)-1] == 'S') { int wreg=ASHCOMP_REGALL; switch(buf[matchlen-1]) { case 'b'://M_sizes[0]: wreg = ASHCOMP_REG8; break; case 'w'://M_sizes[1]: wreg = ASHCOMP_REG16; break; case 'd'://M_sizes[2]: wreg = ASHCOMP_REG32; break; case 'q'://M_sizes[3]: wreg = ASHCOMP_REG64; break; default: wreg = ASHCOMP_REGALL; break; } args &= (0xff ^ ASHCOMP_REGALL) | wreg; } i=0;// result index for(const asmsh_symtable_elt_t *elt=asmsh_symtable; elt->regs; elt++) { if(!(elt->flag & args)) { continue; } if(elt->flag == ASHCOMP_IMM && tlen > 1 && *text=='$') { // immediate value handling continue; } for(const char * const *r=elt->regs; *r; r++) { if(tlen && strncmp(text, *r, tlen)) { continue; } if(!(ret[i] = strdup(*r))) { goto err_strdup; } if(argno == 0 && **r == '%') { //full match on arg0 adding "," char *tmp = ret[i]; char _buf[32]; snprintf(_buf, 31, "%s,", tmp); ret[i] = strdup(_buf); } i++; } } return ret; err_strdup: perror("Unable to strdup arg match"); i--; while(i>=0) { free(ret[i]); i--; } err: free(ret); return NULL; } static int _completion_upper_macro(const char *macro, const char *start, char **ret, size_t *i) { int stlen, len, need_cmp; char buf[32]; /* large enough */ char fmt[32]; len = strlen(macro); stlen = strlen(start); switch(macro[len-1]) { case 'C': case 'S': break; default: return 0; } need_cmp=1; if(len > stlen) { if(strncmp(start, macro, stlen) == 0) { need_cmp = 0; } else { return 0; } } else { if(strncmp(start, macro, len-1)) { return 0; } } switch(macro[len-1]) { case 'C': memcpy(fmt, macro, len-1); fmt[len-1]='%'; fmt[len]='s'; fmt[len+1]='\0'; for(const char * const *cc=M_conds; *cc; cc++) { int r = snprintf(buf, 31, fmt, *cc); if(r < 0) { return -1; } buf[r] = '\0'; if(!need_cmp || strncmp(start, buf, r