/* * re_plugin_prototypes.c * * A programmers editor * * re_data plugin to support showing common prototypes hints. * * Author: Dario Rodriguez antartica@whereismybit.com * This program is licensed under the terms of GNU GPL v2.1+ */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "re_data.h" #include "re_plugin_prototypes.h" #include "prototypes_c89_posix.h" #include "prototypes_tcl.h" #define PLUGINNAME "prototypes" #define MAXINDEXDATALEN 1024 typedef struct protoindex_t { int nentries; const char **index; const char **values; } protoindex_t; typedef struct cproto_t { int sizeprotoindexes; protoindex_t *protoindexes; long lastpos; int lastindex; protolang_t lastprotolang; int sizebuffer; int usedbuffer; char buffer[MAXINDEXDATALEN]; } cproto_t; static int redata_prototypes_add_or_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo, int is_unadd); static int redata_prototypes_postload(redata_t *redata, redata_plugin_t *slot,char *filename); static cproto_t *cproto_init(void); static void cproto_free(cproto_t *cproto); static cproto_t *cproto_getplugin(redata_t *redata); static int cproto_buffer_inv(cproto_t *cproto); const char *mystrstr(char *heystack, char *needle); static int cproto_buffer_getindex(cproto_t *cproto); int redata_prototypes_register(redata_t *redata, redata_plugin_t *slot) { cproto_t *cproto; if(redata==NULL || slot==NULL) return(-1); if((cproto=cproto_init())==NULL) return(-1); /* insufficient memory */ strncpy(slot->name,PLUGINNAME,sizeof(slot->name)); slot->name[sizeof(slot->name)-1]='\0'; slot->unregister=redata_prototypes_unregister; slot->postload=redata_prototypes_postload; slot->add_or_unadd=redata_prototypes_add_or_unadd; slot->userptr=cproto; return(0); } int redata_prototypes_unregister(redata_t *redata, redata_plugin_t *slot,char *filename) { cproto_t *cproto=(cproto_t *) ((slot!=NULL)?(slot->userptr):NULL); if(redata==NULL || slot==NULL || cproto==NULL) return(-1); cproto_free(cproto),cproto=NULL,slot->userptr=NULL; return(0); } static int redata_prototypes_add_or_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo, int is_unadd) { /* reset the cache */ cproto_t *cproto=(cproto_t *) ((slot!=NULL)?(slot->userptr):NULL); if(redata==NULL || slot==NULL || cproto==NULL || undo==NULL || slot->active==0) return(-1); /* sanity check failed */ cproto->lastpos=-1; cproto->lastindex=-1; cproto->lastprotolang=protolang_undefined; return(0); } static int redata_prototypes_postload(redata_t *redata, redata_plugin_t *slot,char *filename) { #warning XXX TODO: get the language from the filename return(-1); } static cproto_t * cproto_init(void) { int i; cproto_t *cproto; protoindex_t *protoindex; const char *ptrindex,*ptrvalues; int n; if((cproto=malloc(sizeof(cproto_t)))==NULL) return(NULL); /* insuf. mem. */ memset(cproto,0,sizeof(cproto_t)); /* initialize protoindexes */ if((cproto->protoindexes=malloc(sizeof(protoindex_t)*protolang_end))==NULL) { cproto_free(cproto),cproto=NULL; return(NULL); /* insuf. mem. */ } memset(cproto->protoindexes,0,sizeof(protoindex_t)*protolang_end); cproto->sizeprotoindexes=protolang_end; for(i=0;i<protolang_end;i++) { protoindex=cproto->protoindexes+i; if(i==protolang_c) { protoindex->nentries=SIZE_PROTOTYPES_C89_POSIX; ptrindex=index_prototypes_c89_posix; ptrvalues=values_prototypes_c89_posix; } else if(i==protolang_tcl) { protoindex->nentries=SIZE_PROTOTYPES_TCL; ptrindex=index_prototypes_tcl; ptrvalues=values_prototypes_tcl; } else { cproto_free(cproto),cproto=NULL; return(NULL); /* internal error, unknown protolang */ } if((protoindex->index=malloc(sizeof(char *)*protoindex->nentries))==NULL || (protoindex->values=malloc(sizeof(char *)*protoindex->nentries))==NULL) { cproto_free(cproto),cproto=NULL; return(NULL); /* insufficient memory */ } memset(protoindex->index,0,sizeof(char *)*protoindex->nentries); memset(protoindex->values,0,sizeof(char *)*protoindex->nentries); for(n=0;n<protoindex->nentries ;n++,ptrindex+=strlen(ptrindex)+1,ptrvalues+=strlen(ptrvalues)+1) { protoindex->index[n]=ptrindex; protoindex->values[n]=ptrvalues; } } /* init caches */ cproto->lastpos=-1; cproto->lastindex=-1; cproto->lastprotolang=protolang_undefined; cproto->usedbuffer=0; cproto->sizebuffer=sizeof(cproto->buffer); return(cproto); } static void cproto_free(cproto_t *cproto) { int i; if(cproto==NULL) return; if(cproto->protoindexes!=NULL) { protoindex_t *protoindex; for(i=0;i<cproto->sizeprotoindexes;i++) { protoindex=cproto->protoindexes+i; if(protoindex->index!=NULL) free(protoindex->index),protoindex->index=NULL; if(protoindex->values!=NULL) free(protoindex->values),protoindex->values=NULL; } free(cproto->protoindexes),cproto->protoindexes=NULL; } free(cproto),cproto=NULL; return; } static cproto_t * cproto_getplugin(redata_t *redata) { cproto_t *cproto; int i; if(redata==NULL) return(NULL); /* sanity check failed */ for(i=0;i<redata->sizeplugins;i++) { if(strcmp(redata->plugins[i].name,PLUGINNAME)==0) break; } if(i>=redata->sizeplugins || redata->plugins[i].active==0) return(NULL); /* plugin not found or nor active */ cproto=(cproto_t *) (redata->plugins[i].userptr); if(cproto==NULL) return(NULL); /* internal error */ return(cproto); } const char * mystrstr(char *haystack, char *needle) { char *ptr; int l; if(needle[0]=='\0') return(haystack); l=strlen(needle); for(ptr=strchr(haystack,needle[0]);ptr!=NULL;ptr=strchr(ptr+1,needle[0])) { if(memcmp(ptr,needle,l)==0) return(ptr); } return(NULL); } const char * redata_prototypes_get(redata_t *redata, long pos) { int numchunk,savednumchunk; int offset,savedoffset; long chunkstartpos; rechunk_t *chunk; cproto_t *cproto; protoindex_t *protoindex; char c; int ntry; enum { searching_forward, searching_paren, searching_name, searching_nameend, searching_ended, } status; if(redata==NULL || pos<0 || (cproto=cproto_getplugin(redata))==NULL) return(NULL); /* sanity check failed */ if(cproto->lastprotolang==protolang_undefined) redata_prototypes_detectlang(redata); protoindex=cproto->protoindexes+cproto->lastprotolang; if(pos==cproto->lastpos) return((cproto->lastindex!=-1)?(protoindex->values[cproto->lastindex]):NULL); if(redata_getposptr(redata,pos,&savednumchunk,&savedoffset)!=0) return(NULL); /* couldn't get pos */ cproto->lastpos=pos; cproto->lastindex=-1; /* FIRST TRY: check if current word is in index */ /* SECOND TRY: check if word before the last '( is in index */ for(ntry=0;ntry<2;ntry++) { numchunk=savednumchunk; offset=savedoffset; chunkstartpos=pos-offset; status=(ntry==0)?searching_forward:searching_paren; cproto->usedbuffer=0; /* search forward for current word end */ if(ntry==0) { for(;status==searching_forward && numchunk<redata->sizechunks ;chunkstartpos+=chunk->useddata,numchunk++,offset=0) { chunk=redata->chunks[numchunk]; for(;offset<chunk->useddata;offset++) { c=chunk->data[offset]; if(!((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c=='_')) { status=searching_name; break; } } if(status!=searching_forward) break; } } if(ntry==0 && status!=searching_name) continue; /* search backwards for prototype info */ for(;numchunk>=0 && status!=searching_ended ;numchunk-- ,offset=(numchunk>=0)?redata->chunks[numchunk]->useddata-1:0 ,chunkstartpos-=(numchunk>=0)?redata->chunks[numchunk]->useddata:0) { chunk=redata->chunks[numchunk]; for(;offset>=0;offset--) { c=chunk->data[offset]; if(c=='\n' && !(numchunk==savednumchunk && offset==savedoffset)) { status=searching_ended; break; } if(status==searching_paren) { if(c=='(') { status=searching_name; continue; } } else if(status==searching_name) { if(c==')') { status=searching_paren; continue; } if((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c=='_') { cproto->usedbuffer=0; cproto->buffer[(cproto->usedbuffer++)]=c; status=searching_nameend; } } else { /* status==searching_nameend */ if(c=='(') { if(cproto->usedbuffer>0) { /* if we had a hit, return that */ cproto_buffer_inv(cproto); /* search string */ if((cproto->lastindex=cproto_buffer_getindex(cproto))!=-1) return(protoindex->values[cproto->lastindex]); } cproto->usedbuffer=0; status=searching_nameend; continue; } else if(!((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c=='_' || (cproto->lastprotolang==protolang_tcl && c==' '))) { status=searching_ended; break; } else { if((cproto->usedbuffer+1)<cproto->sizebuffer) cproto->buffer[(cproto->usedbuffer++)]=c; } } } } if((status==searching_nameend || status==searching_ended) && cproto->usedbuffer>0) { /* trim spaces leading to word (for tcl) */ while(cproto->usedbuffer>0 && cproto->buffer[cproto->usedbuffer-1]==' ') cproto->usedbuffer--; /* put the string in the correct order */ cproto_buffer_inv(cproto); /* search string */ if((cproto->lastindex=cproto_buffer_getindex(cproto))!=-1) return(protoindex->values[cproto->lastindex]); } } return(NULL); } protolang_t redata_prototypes_detectlang(redata_t *redata) { cproto_t *cproto; char linebuf[1024]; if(redata==NULL || (cproto=cproto_getplugin(redata))==NULL) return(protolang_undefined); /* sanity check failed */ if(redata_line_getstartstr(redata,0,linebuf,sizeof(linebuf))!=0) return(protolang_undefined); /* couldn't detect file type using first line */ if(linebuf[0]=='#' && (mystrstr(linebuf,"tclsh")!=NULL || mystrstr(linebuf,"wish")!=NULL || mystrstr(linebuf,"expect")!=NULL)) cproto->lastprotolang=protolang_tcl; else cproto->lastprotolang=protolang_c; cproto->lastpos=-1; return(cproto->lastprotolang); } static int cproto_buffer_inv(cproto_t *cproto) { int i,l,l2; char c; if(cproto==NULL) return(-1); /* terminate string */ cproto->buffer[cproto->usedbuffer]='\0'; /* invert string */ l=cproto->usedbuffer; l2=l>>1; for(i=0;i<l2;i++) { c=cproto->buffer[i]; cproto->buffer[i]=cproto->buffer[l-1-i]; cproto->buffer[l-1-i]=c; } return(0); } static int cproto_buffer_getindex(cproto_t *cproto) { #warning TODO use qsearch() int i; protoindex_t *protoindex; if(cproto==NULL) return(-1); cproto->buffer[cproto->usedbuffer]='\0'; protoindex=cproto->protoindexes+cproto->lastprotolang; for(i=0;i<protoindex->nentries;i++) { if(strcmp(protoindex->index[i],cproto->buffer)==0) return(i); } return(-1); }