/* * re_data.c * * A programmers editor * * Structures to hold the current file contents. * * 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 <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <limits.h> #include <inttypes.h> #include <stdarg.h> #include "re_data.h" #include "sha3/sha3.h" /* DEFAULT CHUNKSIZE: 1024 */ //#define CHUNKSIZE 65536 //#define CHUNKSIZE 32768 //#define CHUNKSIZE 4096 //#define CHUNKSIZE 1024 #define CHUNKSIZE 160 //#define CHUNKSIZE 16 //#define CHUNKSIZE 3 #warning XXX: TODO: CHANGE CHUNKSIZE TO A SANE VALUE (not 1!), THIS IS ONLY FOR TESTING UTF-8 multi-byte CHARS #define UNDOBLOCK 1024 #define ADDNBLOCK 1024 #define UNDOGROWSIZE (256*1024) #define SECURESAVEPREFIX "." #define SECURESAVEPOSTFIX ".saving" #define UTF8_IS_ASCII_OR_START(c) (((c)&0xc0)!=0x80) #define UTF8_IS_ASCII(c) (((c)&0x80)==0) #define UTF8_IS_MULTIBYTESTART(c) ((((c)&0x80)==0x80)&&(((c)&(0x80|0x40))!=0x80)) #define UTF8_IS_MULTIBYTECONT(c) (((c)&(0x80|0x40))==0x80) #define UTF8_MULTIBYTESTART2LEN(c) ((((c)&0xe0)==0xc0)?2: \ (((c)&0xf0)==0xe0)?3: \ (((c)&0xf8)==0xf0)?4: \ (((c)&0xfc)==0xf8)?5: \ (((c)&0xfe)==0xfc)?6: \ 1) static int redata_hash_gen(redata_t *redata, char *filename, char *buf, long buflen, char *resbuf129bytes); static char *securesave_genname(char *filename, char *buf, int bufsize); static void *mymemrchr(const void *s, int c, size_t n); static void meminvert(void *start, void *end); static size_t memrchroffset(char *ptr, int c, size_t n); #if 0 static void redata_debug_chunkdump(redata_t *redata, char *title) { int m,k; char c; title=(title==NULL)?"":title; fprintf(stderr,"%s:CHUNKDUMP (sizechunks:%i)\n",title,redata->sizechunks); for(m=0;m<redata->sizechunks;m++) { fprintf(stderr,"%s:chunk[%i] len:%-5i data:\"",title,m,redata->chunks[m]->useddata); for(k=0;k<redata->chunks[m]->useddata;k++) { c=redata->chunks[m]->data[k]; if(c=='\n' || c=='\0') fprintf(stderr,"\\%c",(c=='\n')?'n':'0'); else if(c<' ' || c>'~' || c=='\\') fprintf(stderr,"\\x%02X",((unsigned char *)redata->chunks[m]->data)[k]); else fprintf(stderr,"%c",c); } fprintf(stderr,"\"\n"); } } #define CHUNKDEBUG(a) redata_debug_chunkdump a #else #define CHUNKDEBUG(a) #endif redata_t * redata_init(int (*pluginregisterfn)(redata_t *redata, redata_plugin_t *slot), ...) { redata_t *redata; va_list args; int nargs; int res; int (*fn)(redata_t *redata, redata_plugin_t *slot); /* count number of plugins */ va_start(args,pluginregisterfn); for(nargs=0,fn=pluginregisterfn;fn!=NULL;fn=va_arg(args,int (*)(redata_t *redata, redata_plugin_t *slot))) nargs++; va_end(args); /* get memory */ if((redata=malloc(sizeof(redata_t)+sizeof(redata_plugin_t)*(nargs-1)))==NULL) return(NULL); /* sanity check failed */ memset(redata,0,sizeof(redata_t)+sizeof(redata_plugin_t)*(nargs-1)); redata->sizeplugins=nargs; /* data */ redata->sizechunks=0; redata->chunks=NULL; redata->chunkdatasize=CHUNKSIZE; redata->available=0; /* undo */ redata->undostack.sizeundo=0; redata->undostack.usedundo=0; redata->undostack.undo=NULL; redata->undostack.buf=NULL; /* plugins */ va_start(args,pluginregisterfn); for(nargs=0,fn=pluginregisterfn;fn!=NULL;fn=va_arg(args,int (*)(redata_t *redata,redata_plugin_t *slot))) { res=fn(redata,redata->plugins+nargs); redata->plugins[nargs].active=(res==0)?1:0; nargs++; } va_end(args); /* filename */ redata->filename[0]='\0'; /* all done */ return(redata); } void redata_free(redata_t *redata) { int i; if(redata==NULL) return; /* nothing to do */ /* plugins */ for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].unregister!=NULL) redata->plugins[i].unregister(redata,redata->plugins+i,redata->filename); memset(redata->plugins+i,0,sizeof(redata_plugin_t)); } /* data */ for(i=0;i<redata->sizechunks;i++) { if(redata->chunks[i]!=NULL) free(redata->chunks[i]),redata->chunks[i]=NULL; } redata->sizechunks=0; if(redata->chunks!=NULL) free(redata->chunks),redata->chunks=NULL; if(redata->tmpchunk!=NULL) free(redata->tmpchunk),redata->tmpchunk=NULL; /* undo */ if(redata->undostack.undo!=NULL) free(redata->undostack.undo),redata->undostack.undo=NULL; if(redata->undostack.buf!=NULL) free(redata->undostack.buf),redata->undostack.buf=NULL; /* redo */ if(redata->redostack.undo!=NULL) free(redata->redostack.undo),redata->redostack.undo=NULL; if(redata->redostack.buf!=NULL) free(redata->redostack.buf),redata->redostack.buf=NULL; /* addnbuf */ if(redata->addnbuf!=NULL) free(redata->addnbuf),redata->addnbuf=NULL,redata->sizeaddnbuf=0; /* free main struct */ free(redata),redata=NULL; return; } void redata_idleproc(redata_t *redata, char *filename) { int i; if(redata==NULL) return; for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].commit!=NULL) redata->plugins[i].commit(redata,redata->plugins+i,filename); } return; } int redata_config_chunkdatasize(redata_t *redata, int chunkdatasize) { /* NOTE: this can only be configured immediately after doing redata_init() */ if(redata==NULL || chunkdatasize<=0 || redata->sizechunks!=0) return(-1); /* sanity check failed */ redata->chunkdatasize=chunkdatasize; return(0); } long redata_getsize(redata_t *redata) { if(redata==NULL) return(0); /* sanity check failed */ return(redata->chunkdatasize*redata->sizechunks); } long redata_getavailable(redata_t *redata) { if(redata==NULL) return(0); /* sanity check failed */ return(redata->available); } long redata_getused(redata_t *redata) { long used; if(redata==NULL) return(0); /* sanity check failed */ used=redata->chunkdatasize*redata->sizechunks-redata->available; return(used); } int redata_getposptr(redata_t *redata, long pos, int *numchunk, int *offset) { long used; int i; long ipos; used=redata_getused(redata); if(redata==NULL || pos<0 || pos>used || numchunk==NULL || offset==NULL) return(-1); /* sanity check failed */ for(ipos=0,i=0;i<redata->sizechunks;ipos+=redata->chunks[i]->useddata,i++) { if(pos>(ipos+redata->chunks[i]->useddata)) continue; /* found */ *numchunk=i; *offset=pos-ipos; return(0); } return(-1); } int redata_getchar(redata_t *redata, long pos) { int numchunk; int offset; int avail; rechunk_t *chunk; if(redata==NULL || pos<0 || pos>=redata_getsize(redata)) return(-1); /* sanity check failed */ if(redata_getposptr(redata,pos,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ /* search for data starting at that pos */ for(;numchunk<redata->sizechunks;numchunk++,offset=0) { chunk=redata->chunks[numchunk]; avail=chunk->useddata-offset; if(avail>0) { return((int) (((unsigned char *)chunk->data)[offset])); } } return(-1); /* not found */ } int redata_getdata(redata_t *redata, long frompos, long size, char *dest) { int nchunk,off,avail; long n; if(redata==NULL || frompos<0 || size<0 || dest==NULL || (frompos+size)>redata_getused(redata) || redata_getposptr(redata,frompos,&nchunk,&off)!=0) return(-1); /* sanity check failed */ for(n=0;n<size && nchunk<redata->sizechunks;nchunk++,off=0) { avail=redata->chunks[nchunk]->useddata-off; if(avail<=0) continue; if(avail>(size-n)) avail=size-n; memcpy(dest+n,redata->chunks[nchunk]->data+off,avail); n+=avail; } return(0); } int redata_wipe(redata_t *redata) { int i; if(redata==NULL) return(-1); /* sanity check failed */ /* data */ for(i=0;i<redata->sizechunks;i++) { redata->chunks[i]->useddata=0; memset(&(redata->chunks[i]->whatin),0,sizeof(whatin_t)); } redata->available=redata_getsize(redata); /* plugins */ for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].wipe!=NULL) redata->plugins[i].wipe(redata,redata->plugins+i,redata->filename); } redata->filename[0]='\0'; /* all done */ return(0); } int redata_chunk_insertnew(redata_t *redata, int afterthischunk) { rechunk_t *newchunk; int i; if(redata==NULL || afterthischunk<(-1) || afterthischunk>=redata->sizechunks) return(-1); if(redata->chunks[redata->sizechunks-1]->useddata!=0) { if(redata_preallocate(redata,redata_getsize(redata)+redata->chunkdatasize)==-1 || redata->chunks[redata->sizechunks-1]->useddata!=0) return(-1); } newchunk=redata->chunks[redata->sizechunks-1]; /* move the emtpy chunk to after chunkno */ for(i=redata->sizechunks-1;i>(afterthischunk+1);i--) redata->chunks[i]=redata->chunks[i-1]; redata->chunks[afterthischunk+1]=newchunk; return(0); } int redata_chunk_deletechunk(redata_t *redata, int chunkno) { rechunk_t *chunk; int i; if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks) return(-1); chunk=redata->chunks[chunkno]; if(chunk->useddata>0) { redata->available+=chunk->useddata; chunk->useddata=0; chunk->whatin_fresh=0; redata_chunk_unfreshnext(redata, chunkno); } for(i=chunkno;(i+1)<redata->sizechunks;i++) redata->chunks[i]=redata->chunks[i+1]; redata->chunks[redata->sizechunks-1]=chunk; return(0); } int redata_preallocate(redata_t *redata, long newsize) { long cursize; int nchunks; int i; long rechunksize; rechunk_t **newchunks,*chunk; int oldsizechunks; if(redata==NULL || redata->chunkdatasize==0 || newsize<0) return(-1); /* sanity check failed */ rechunksize=sizeof(rechunk_t)-1+redata->chunkdatasize; /* before doing any chunk alloc, we setup tmpchunk (used for moves) */ if(redata->tmpchunk==NULL) { if((redata->tmpchunk=malloc(rechunksize))==NULL) return(-1); /* insuf. mem. */ memset(redata->tmpchunk,0,rechunksize); } /* new chunk processing */ if((cursize=redata_getsize(redata))>=newsize) return(0); /* all done */ nchunks=(newsize-cursize+redata->chunkdatasize-1)/redata->chunkdatasize; if((newchunks=realloc(redata->chunks,sizeof(rechunk_t *)*(redata->sizechunks+nchunks)))==NULL) return(-1); /* insuf. mem. */ redata->chunks=newchunks; memset(redata->chunks+redata->sizechunks,0,sizeof(rechunk_t *)*nchunks); oldsizechunks=redata->sizechunks; for(i=0;i<nchunks;i++) { if((chunk=malloc(rechunksize))==NULL) return(-1); /* insuf. mem. */ memset(chunk,0,rechunksize); chunk->useddata=0; redata->chunks[oldsizechunks+i]=chunk; redata->sizechunks++; redata->available+=redata->chunkdatasize; } return(0); } int redata_chunk_movedata(redata_t *redata, int chunkfrom, long posfrom, int chunkto, long posto, long size) { if(redata==NULL || size<0 || chunkfrom<0 || chunkfrom>=redata->sizechunks || posfrom<0 || (posfrom+size)>redata->chunks[chunkfrom]->useddata || chunkto<0 || chunkto>=redata->sizechunks || posto<0 || (posto)>redata->chunks[chunkto]->useddata || (chunkfrom!=chunkto && (redata->chunkdatasize-redata->chunks[chunkto]->useddata)<size) || (chunkfrom==chunkto && posfrom<posto && posfrom+size>posto) ) return(-1); /* sanity check failed */ if(size==0 || (chunkfrom==chunkto && posfrom==posto) || (chunkfrom==chunkto && (posfrom+size)==posto) ) return(0); /* all done */ if(chunkfrom!=chunkto) { rechunk_t *from,*to; from=redata->chunks[chunkfrom]; to=redata->chunks[chunkto]; memmove(to->data+posto+size,to->data+posto,to->useddata-posto); memcpy(to->data+posto,from->data+posfrom,size); memmove(from->data+posfrom,from->data+posfrom+size,from->useddata-posfrom-size); from->useddata-=size; to->useddata+=size; redata_chunk_unfreshrange(redata,chunkfrom,chunkto); redata_chunk_unfreshnext(redata,chunkfrom); redata_chunk_unfreshnext(redata,chunkto); } else { /* from==to */ rechunk_t *chunk=redata->chunks[chunkfrom]; memcpy(redata->tmpchunk->data,chunk->data+posfrom,size); if(posfrom>posto) { memmove(chunk->data+posto+size,chunk->data+posto,posfrom-posto); memcpy(chunk->data+posto,redata->tmpchunk->data,size); } else { memmove(chunk->data+posfrom,chunk->data+posfrom+size,posto-posfrom-size); memcpy(chunk->data+posto-size,redata->tmpchunk->data,size); } chunk->whatin_fresh=0; redata_chunk_unfreshnext(redata,chunkfrom); } return(0); } int redata_chunk_insertdata(redata_t *redata, int chunkto, long posto, char *buf, long buflen) { rechunk_t *chunk; if(redata==NULL || buflen<0 || buf==NULL || chunkto<0 || chunkto>=redata->sizechunks || posto<0 || posto>redata->chunks[chunkto]->useddata || (redata->chunkdatasize-redata->chunks[chunkto]->useddata)<buflen) return(-1); /* sanity check failed */ chunk=redata->chunks[chunkto]; memmove(chunk->data+posto+buflen,chunk->data+posto,chunk->useddata-posto); memcpy(chunk->data+posto,buf,buflen); chunk->useddata+=buflen; redata->available-=buflen; chunk->whatin_fresh=0; redata_chunk_unfreshnext(redata,chunkto); return(0); } int redata_chunk_deletedata(redata_t *redata, int chunkno, long pos, long n) { rechunk_t *chunk; if(redata==NULL || n<0 || chunkno<0 || chunkno>=redata->sizechunks || pos<0 || pos>redata->chunks[chunkno]->useddata || (redata->chunks[chunkno]->useddata-pos)<n) return(-1); /* sanity check failed */ if(n==0) return(0); /* all done */ chunk=redata->chunks[chunkno]; memmove(chunk->data+pos,chunk->data+pos+n,chunk->useddata-pos-n); chunk->useddata-=n; redata->available+=n; chunk->whatin_fresh=0; redata_chunk_unfreshnext(redata,chunkno); return(0); } int redata_chunk_splithere(redata_t *redata, int chunkno, int pos) { rechunk_t *chunk; int size; if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks || pos<0 || pos>redata->chunks[chunkno]->useddata) return(-1); /* sanity check failed */ chunk=redata->chunks[chunkno]; if(chunk->useddata==pos) return(0); /* all done: already splitted */ size=redata->chunks[chunkno]->useddata-pos; if((chunkno+1)<redata->sizechunks && (redata->chunkdatasize-redata->chunks[chunkno+1]->useddata)>=size) { redata_chunk_movedata(redata,chunkno,pos,chunkno+1,0,size); return(0); /* all done: moved data cleanly to next chunk */ } if(redata_chunk_insertnew(redata,chunkno)!=0) return(-1); /* insuf. mem. */ redata_chunk_movedata(redata,chunkno,pos,chunkno+1,0,size); return(0); } int redata_chunk_fillfromnext(redata_t *redata, int chunkno, int n) { if(redata==NULL || chunkno<0 || (chunkno+1)>=redata->sizechunks || (redata->chunkdatasize-redata->chunks[chunkno]->useddata)<n || (redata->chunks[chunkno+1]->useddata)<n) return(-1); /* sanity check failed */ return(redata_chunk_movedata(redata,chunkno+1,0,chunkno,redata->chunks[chunkno]->useddata,n)); } int redata_chunk_unfreshnext(redata_t *redata, int chunkno) { int next; if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks) return(-1); /* sanity check failed */ /* invalidate whatin for next chunks until one not empty */ for(next=chunkno+1;next<redata->sizechunks;next++) { redata->chunks[next]->whatin_fresh=0; if(redata->chunks[next]->useddata!=0) break; } return(0); } int redata_chunk_unfreshrange(redata_t *redata, int fromchunkno, int tochunkno) { int chunkno; if(redata==NULL || fromchunkno<0 || fromchunkno>=redata->sizechunks || tochunkno<0 || tochunkno>=redata->sizechunks) return(-1); /* sanity check failed */ if(fromchunkno>tochunkno) { chunkno=fromchunkno; fromchunkno=tochunkno; tochunkno=chunkno; } /* invalidate whatin in range */ for(chunkno=fromchunkno;chunkno<=tochunkno;chunkno++) redata->chunks[chunkno]->whatin_fresh=0; return(0); } int redata_whatin_refresh(redata_t *redata, int chunkno) { rechunk_t *chunk; int nlcount; int i,lim; if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL) return(-1); /* sanity check failed */ chunk=redata->chunks[chunkno]; if(chunk->whatin_fresh) return(0); memset(&(chunk->whatin),0,sizeof(whatin_t)); nlcount=0; for(i=0,lim=chunk->useddata;i<lim;i++) nlcount+=(chunk->data[i]=='\n')?1:0; chunk->whatin.nlcount=nlcount; if(chunkno>0) { int prev; for(prev=chunkno-1;prev>0 && redata->chunks[prev]->useddata==0;prev--) ; if(redata->chunks[prev]->useddata>0 && redata->chunks[prev]->data[redata->chunks[prev]->useddata-1]!='\n') chunk->whatin.iscontinuation=1; } chunk->whatin_fresh=1; return(0); } int redata_fix_nl(redata_t *redata, int chunkno) { rechunk_t *chunk,*nextchunk; int linesize, nextlinesize, avail, nextavail; unsigned char *ptr,*nextptr; /* make sure a line of len<chunksize is entirely inside one chunk (to simplify the syntax highlighting code) */ if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL) return(-1); /* sanity check failed */ if(chunkno==(redata->sizechunks-1) || redata->chunks[chunkno]->useddata==0 || redata->chunks[chunkno]->data[redata->chunks[chunkno]->useddata-1]=='\n') return(0); /* Nothing to do (last chunk, chunk empty or already fixed) */ chunk=redata->chunks[chunkno]; nextchunk=redata->chunks[chunkno+1]; ptr=mymemrchr(chunk->data,'\n',chunk->useddata); nextptr=memchr(nextchunk->data,'\n',nextchunk->useddata); linesize=(ptr==NULL)?chunk->useddata:(chunk->useddata-(ptr-chunk->data)-1); nextlinesize=(nextptr==NULL)?nextchunk->useddata:((nextptr-nextchunk->data)+1); avail=redata->chunkdatasize-chunk->useddata; nextavail=redata->chunkdatasize-nextchunk->useddata; if(avail>=nextlinesize) { /* move remaining bytes of line to current chunk */ redata_chunk_movedata(redata, chunkno+1, 0, chunkno, chunk->useddata, nextlinesize); } else if(nextavail>=linesize) { /* move incomplete line to next chunk */ redata_chunk_movedata(redata, chunkno, chunk->useddata-linesize, chunkno+1, 0, linesize); } else if(ptr!=NULL) { /* only if we have more data in chunkno before this long line */ int newavail; /* create a new empty chunk in-between and put the line there */ if(redata_chunk_insertnew(redata,chunkno)==-1) return(-1); /* move the data; if it doesn't fit, move from the beginning until it is full */ redata_chunk_movedata(redata, chunkno, chunk->useddata-linesize, chunkno+1, 0, linesize); newavail=redata->sizechunks-linesize; redata_chunk_movedata(redata, chunkno+2, 0, chunkno+1, linesize, (newavail<nextlinesize)?newavail:nextlinesize); } return(0); } int redata_loadquestions_setup(redata_t *redata, char *filename) { int i; if(redata==NULL || filename==NULL) return(-1); /* sanity check failed */ redata_loadquestions_wipe(redata); /* unsaved and other plugins: loadquestion (pre-to-load) */ for(i=0;i<redata->sizeplugins;i++) { if(!redata->plugins[i].active) continue; if(redata->plugins[i].loadquestion!=NULL) redata->plugins[i].loadquestion(redata,redata->plugins+i,filename); } return(0); } question_t * redata_loadquestions_getnext(redata_t *redata) { int i; if(redata==NULL) return(NULL); /* sanity check failed */ for(i=0;i<redata->sizeplugins;i++) { if(!redata->plugins[i].active || redata->plugins[i].question.active==0) continue; if(redata->plugins[i].question.selectedoption==-1) return(&(redata->plugins[i].question)); } return(NULL); /* no more questions */ } int redata_loadquestions_reply(redata_t *redata, question_t *question, int selectedoption) { if(redata==NULL || question==NULL || selectedoption<-1 || selectedoption>=question->nopts) return(-1); question->selectedoption=(selectedoption==-1)?question->defaultoption:selectedoption; return(0); } int redata_loadquestions_wipe(redata_t *redata) { int i; if(redata==NULL) return(-1); for(i=0;i<redata->sizeplugins;i++) { if(!redata->plugins[i].active) continue; memset(redata->plugins[i].questionfilename,0,sizeof(redata->plugins[i].questionfilename)); memset(&(redata->plugins[i].question),0,sizeof(redata->plugins[i].question)); redata->plugins[i].question.active=0; redata->plugins[i].question.selectedoption=-1; } return(0); } int redata_load(redata_t *redata, char *filename, char **errordesc) { #warning TODO: what to do when there plugins with questions AFTER loading instead of before loading... int fd,nread,totalread; int chunkno, avail; struct stat statbuf; rechunk_t *chunk; int i; if(redata==NULL || filename==NULL) { if(errordesc!=NULL) *errordesc="Internal error"; return(-1); /* sanity check failed */ } redata_wipe(redata); strncpy(redata->filename,filename,sizeof(redata->filename)); redata->filename[sizeof(redata->filename)-1]='\0'; if((fd=open(filename,O_RDONLY))==-1 || fstat(fd,&statbuf)!=0 || !S_ISREG(statbuf.st_mode)) { if(fd!=-1) close(fd),fd=-1; if(errordesc!=NULL) *errordesc="File not found, couldn't query size or not a regular file"; return(-1); /* file not found, couldn't query size or not a regular file */ } /* preallocate 10% more than needed */ if(redata_preallocate(redata,statbuf.st_size+(statbuf.st_size/10)+1 )) { close(fd),fd=-1; if(errordesc!=NULL) *errordesc="Insufficient memory"; return(-1); /* insuf. mem. */ } for(totalread=0,chunkno=0,nread=0;totalread<statbuf.st_size;totalread+=nread,nread=0) { if(chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL) { /* alloc another chunk */ if(redata_preallocate(redata,redata_getsize(redata)+redata->chunkdatasize)==-1 || chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL) { close(fd),fd=-1; fprintf(stderr,"redata_load: INTERNAL ERROR\n"); return(-2); /* internal error */ } } chunk=redata->chunks[chunkno]; /* leave 10% free on each chunk */ avail=(redata->chunkdatasize-redata->chunkdatasize/10)-chunk->useddata; avail=(avail<0)?0:avail; avail=((totalread+avail)>statbuf.st_size)?statbuf.st_size-totalread:avail; if(avail==0) { chunkno++; /* full, try next */ continue; } if((nread=read(fd,chunk->data+chunk->useddata,avail))<=0) { close(fd),fd=-1; if(errordesc!=NULL) *errordesc="Short read loading file"; return(-1); /* short read */ } chunk->useddata+=nread; redata->available-=nread; } close(fd),fd=-1; for(chunkno=0;chunkno<redata->sizechunks;chunkno++) redata_fix_nl(redata,chunkno); for(chunkno=0;chunkno<redata->sizechunks;chunkno++) redata_whatin_refresh(redata,chunkno); /* unsaved and other plugins: postload */ for(i=0;i<redata->sizeplugins;i++) { if(!redata->plugins[i].active) continue; if(redata->plugins[i].postload!=NULL) redata->plugins[i].postload(redata,redata->plugins+i,filename); } redata_loadquestions_wipe(redata); #warning TODO: IMPLEMENT POSTLOADQUESTIONS (call now loadquestion_postload). /* mark as not modified */ redata->needs_saving=0; /* all done */ return(0); } int redata_save(redata_t *redata, char *paramfilename, char **errordesc) { int fd; int i,n; char tmplinkfilename[PATH_MAX+1]; char tmpfile[PATH_MAX+1]; struct stat statbuf; char *filename; if(redata==NULL || paramfilename==NULL) { if(errordesc!=NULL) *errordesc="Internal error"; return(-1); /* sanity check failed */ } filename=paramfilename; memset(tmpfile,0,sizeof(tmpfile)); if(stat(paramfilename,&statbuf)==0 && (statbuf.st_mode & S_IFLNK)!=0 && readlink(paramfilename,tmpfile,sizeof(tmpfile))>0) { tmpfile[sizeof(tmpfile)-1]='\0'; if(tmpfile[0]=='/') { /* absolute symlink, just copy it */ strncpy(tmplinkfilename,tmpfile,sizeof(tmplinkfilename)); tmplinkfilename[sizeof(tmplinkfilename)-1]='\0'; } else { char *aux; int off; /* relative symlink, put it after the path of paramfilename */ off=0; if((aux=mymemrchr(paramfilename,'/',strlen(paramfilename)))!=NULL) off=(aux-paramfilename+1); memcpy(tmplinkfilename,paramfilename,off); strncpy(tmplinkfilename+off,tmpfile,sizeof(tmplinkfilename)-off); tmplinkfilename[sizeof(tmplinkfilename)-1]='\0'; } filename=tmplinkfilename; } if((securesave_genname(filename,tmpfile,sizeof(tmpfile)))==NULL) { if(errordesc!=NULL) *errordesc="Malformed filename"; return(-1); /* malformed filename */ } memset(&statbuf,0,sizeof(statbuf)); if(stat(filename,&statbuf)!=0) statbuf.st_mode=0644; if((fd=open(tmpfile,O_WRONLY|O_TRUNC|O_CREAT,statbuf.st_mode))==-1) { if(errordesc!=NULL) *errordesc="Couldn't open file for writing"; return(-1); /* couldn't open file for writing */ } for(i=0;i<redata->sizechunks;i++) { if(redata->chunks[i]->useddata==0) continue; if((n=write(fd,redata->chunks[i]->data,redata->chunks[i]->useddata))==-1 || n!=redata->chunks[i]->useddata) { close(fd),fd=-1; unlink(tmpfile); if(errordesc!=NULL) *errordesc="Short write saving file"; return(-1); /* short write */ } } close(fd),fd=-1; if(rename(tmpfile,filename)!=0) { unlink(tmpfile); if(errordesc!=NULL) *errordesc="Couldn't overwrite old file"; return(-1); /* couldn't overwrite old file */ } for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].postsave!=NULL) redata->plugins[i].postsave(redata,redata->plugins+i,redata->filename,paramfilename); } strncpy(redata->filename,paramfilename,sizeof(redata->filename)); redata->filename[sizeof(redata->filename)-1]='\0'; /* mark as not modified */ redata->needs_saving=0; return(0); } int redata_needssaving(redata_t *redata) { if(redata==NULL) return(0); return( (redata->needs_saving)?1:0 ); } int redata_needssaving_reset(redata_t *redata) { if(redata==NULL) return(-1); redata->needs_saving=0; return(0); } int redata_undobuf_reserve(redata_t *redata, undostack_t *stack, int minavail) { int unused; int newsize; char *newptr; if(redata==NULL || minavail<0 || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(-1); /* sanity check failed */ unused=stack->sizebuf-stack->usedbuf; if(unused<minavail) { newsize=(stack->sizebuf+minavail+UNDOGROWSIZE-1)/UNDOGROWSIZE; newsize*=UNDOGROWSIZE; if((newptr=realloc(stack->buf,newsize))==NULL) return(-1); /* insuf. mem. */ stack->buf=newptr; stack->sizebuf=newsize; } return(0); } undo_t * redata_undo_new(redata_t *redata, undostack_t *stack, char type) { int newsize; undo_t *newptr,*undo; if(redata==NULL || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(NULL); /* sanity check failed */ if(stack->sizeundo==stack->usedundo) { newsize=(stack->sizeundo+1+UNDOBLOCK-1)/UNDOBLOCK; newsize*=UNDOBLOCK; if((newptr=realloc(stack->undo,sizeof(undo_t)*newsize))==NULL) return(NULL); stack->undo=newptr; memset(stack->undo+stack->sizeundo,0,sizeof(undo_t)*(newsize-stack->sizeundo)); stack->sizeundo=newsize; } undo=stack->undo+stack->usedundo; stack->usedundo++; undo->type=type; undo->off=stack->usedbuf; undo->len=0; return(undo); } undo_t * redata_undo_newfromchunks(redata_t *redata, undostack_t *stack, char type, int pos1, int len) { int startchunkno,endchunkno; int startoff,endoff; undo_t *undo; int k; long used; long copyfrom,copysize; if(redata==NULL || len<=0 || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(NULL); /* sanity check failed */ if(redata_getposptr(redata,pos1,&startchunkno,&startoff)==-1 || redata_getposptr(redata,pos1+len,&endchunkno,&endoff)==-1) { return(NULL); /* chunk data out of bounds */ } if(redata_undobuf_reserve(redata,stack,len)!=0) return(NULL); /* insuf. mem. */ if((undo=redata_undo_new(redata,stack,type))==NULL) return(NULL); /* insuf. mem. */ undo->off=stack->usedbuf; /* copy contents */ for(k=startchunkno,used=0;k<=endchunkno;k++) { if(k==startchunkno && k==endchunkno) { copyfrom=startoff; copysize=endoff-startoff; } else if(k==startchunkno) { copyfrom=startoff; copysize=redata->chunks[k]->useddata-startoff; } else if(k==endchunkno) { copyfrom=0; copysize=endoff; } else { copyfrom=0; copysize=redata->chunks[k]->useddata; } memcpy(stack->buf+stack->usedbuf,redata->chunks[k]->data+copyfrom,copysize); stack->usedbuf+=copysize; used+=copysize; } undo->len=used; return(undo); } undo_t * redata_undo_newfrombuf(redata_t *redata, undostack_t *stack, char type, char *buf, int buflen) { undo_t *undo; if(redata==NULL || buflen<=0 || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(NULL); /* sanity check failed */ if(redata_undobuf_reserve(redata,stack, buflen)!=0) return(NULL); /* insuf. mem. */ if((undo=redata_undo_new(redata,stack, type))==NULL) return(NULL); /* insuf. mem. */ undo->off=stack->usedbuf; memcpy(stack->buf+stack->usedbuf,buf,buflen); stack->usedbuf+=buflen; undo->len=buflen; return(undo); } int redata_undo_movelast(redata_t *redata, undostack_t *from, undostack_t *to) { undo_t *undofrom; undo_t *undoto; if(redata==NULL || from==to || (from!=&(redata->undostack) && from!=&(redata->redostack)) || (to!=&(redata->undostack) && to!=&(redata->redostack)) || from->usedundo<1) return(-1); /* sanity check failed */ undofrom=from->undo+from->usedundo-1; if((undoto=redata_undo_newfrombuf(redata,to,undofrom->type,from->buf+undofrom->off,undofrom->len))==NULL) return(-1); /* insuf. mem. */ undoto->posorig=undofrom->posorig; undoto->posdest=undofrom->posdest; #ifdef REDATA_HASHUNDO memcpy(undoto->prehash,undofrom->prehash,sizeof(undoto->prehash)); memcpy(undoto->posthash,undofrom->posthash,sizeof(undoto->posthash)); #endif redata_undo_removelast(redata, from); return(0); } int redata_undo_removelast(redata_t *redata, undostack_t *stack) { undo_t *undo; if(redata==NULL || (stack!=&(redata->undostack) && stack!=&(redata->redostack)) || stack->usedundo<1) return(-1); /* sanity check failed */ undo=stack->undo+stack->usedundo-1; stack->usedbuf-=undo->len; memset(undo,0,sizeof(undo_t)); stack->usedundo--; return(0); } int redata_undo_wipe(redata_t *redata, undostack_t *stack) { if(redata==NULL || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(-1); /* sanity check failed */ memset(stack->undo,0,sizeof(undo_t)*stack->usedundo); stack->usedundo=0; memset(stack->buf,0,stack->usedbuf); stack->usedbuf=0; return(0); } int redata_undo_inactivatelast(redata_t *redata, undostack_t *stack) { if(redata==NULL || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(-1); /* sanity check failed */ if(stack->usedundo==0) return(-1); stack->usedundo--; stack->usedbuf-=stack->undo[stack->usedundo].len; return(0); } int redata_undo_reactivatelast(redata_t *redata, undostack_t *stack) { if(redata==NULL || (stack!=&(redata->undostack) && stack!=&(redata->redostack))) return(-1); /* sanity check failed */ if(stack->usedundo==stack->sizeundo) return(-1); stack->usedbuf+=stack->undo[stack->usedundo].len; stack->usedundo++; return(0); } int redata_undo_groupinit(redata_t *redata, undostack_t *stack) { /* stores the current position in (undostack).groupinit */ #warning TODO return(-1); } int redata_undo_groupcommit(redata_t *redata, undostack_t *stack) { /* marks as hint_groupedwithnext from stored groupinit position in undostack to last-1 position (if groupinit pos is the last pos, do nothing)*/ #warning TODO return(-1); } int redata_op_add(redata_t *redata, long insertpos, char *buf, long buflen, undostack_t *fromhere) { int chunkno; int offset; undo_t *undo; rechunk_t *chunk,*nextchunk; long avail,nextavail; int i; int needed; char *ptr; if(redata==NULL || buf==NULL || buflen<0 || insertpos<0 || insertpos>redata_getused(redata) || (fromhere!=NULL && (fromhere!=&(redata->undostack) && fromhere!=&(redata->redostack)))) return(-1); /* sanity check failed */ if(insertpos==0 && redata->sizechunks==0) { /* add the first chunk */ redata_preallocate(redata,buflen); } if(redata_getposptr(redata,insertpos,&chunkno,&offset)==-1) return(-1); /* invalid pos */ if(fromhere!=&(redata->undostack)) { if((undo=redata_undo_newfrombuf(redata,&(redata->undostack),'A',buf,buflen))==NULL) return(-1); /* couldn't create undo struct */ #ifdef REDATA_HASHUNDO redata_hash(redata,undo->prehash); #endif } else { undo=NULL; } chunk=redata->chunks[chunkno]; avail=redata->chunkdatasize-chunk->useddata; nextchunk=((chunkno+1)<redata->sizechunks)?redata->chunks[chunkno+1]:NULL; nextavail=(nextchunk==NULL)?0:redata->chunkdatasize-nextchunk->useddata; if(avail>=buflen) { /* fits in current chunk */ redata_chunk_insertdata(redata,chunkno,offset,buf,buflen); redata_fix_nl(redata,chunkno); nextchunk=chunk; } else if((avail+nextavail)>=buflen) { /* part fits in current chunk, part in next chunk */ int bothering; bothering=chunk->useddata-offset; bothering=(bothering>nextavail)?nextavail:bothering; redata_chunk_movedata(redata,chunkno,chunk->useddata-bothering,chunkno+1,0,bothering); avail=redata->chunkdatasize-chunk->useddata; avail=(avail>buflen)?buflen:avail; redata_chunk_insertdata(redata,chunkno,offset,buf,avail); redata_chunk_insertdata(redata,chunkno+1,0,buf+avail,buflen-avail); } else { /* will need to add more chunks */ needed=(buflen+redata->chunkdatasize-1)/redata->chunkdatasize; needed*=redata->chunkdatasize; needed+=(chunk->useddata-offset); if(redata_preallocate(redata,redata_getsize(redata)+needed)!=0) { if(undo!=NULL) { redata->undostack.usedundo--; redata->undostack.usedbuf-=buflen; memset(undo,0,sizeof(undo_t)); } return(-1); /* insuf. mem. */ } redata_chunk_insertnew(redata,chunkno); redata_chunk_movedata(redata,chunkno,offset,chunkno+1,0,chunk->useddata-offset); nextchunk=redata->chunks[chunkno+1]; avail=redata->chunkdatasize-chunk->useddata; avail=(avail>buflen)?buflen:avail; redata_chunk_insertdata(redata,chunkno,chunk->useddata,buf,avail); for(ptr=buf+avail,i=chunkno;(ptr-buf)<buflen;i++,ptr+=avail) { redata_chunk_insertnew(redata,i); avail=redata->chunkdatasize-redata->chunks[i+1]->useddata; avail=(avail>(buflen-(ptr-buf)))?(buflen-(ptr-buf)):avail; redata_chunk_insertdata(redata,i+1,0,ptr,avail); } } /* fix nl until nextchunk (...fix_nl can insert chunks) */ for(i=chunkno;i<redata->sizechunks;i++) { redata_fix_nl(redata,i); if(redata->chunks[i]==nextchunk) break; } /* activate undo */ if(undo!=NULL) { /* new or from redo stack */ undo->posorig=undo->posdest=insertpos; #ifdef REDATA_HASHUNDO redata_hash(redata,undo->posthash); #endif if(fromhere==&(redata->redostack)) redata_undo_removelast(redata,&(redata->redostack)); else redata_undo_wipe(redata,&(redata->redostack)); } else { /* from undo stack */ redata_undo_movelast(redata,&(redata->undostack),&(redata->redostack)); } /* add to plugins (unsaved,...) */ for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].add_or_unadd!=NULL) { redata->plugins[i].add_or_unadd(redata,redata->plugins+i ,(undo!=NULL)?redata->undostack.undo+redata->undostack.usedundo-1 :redata->redostack.undo+redata->redostack.usedundo-1 ,(undo!=NULL)?0:1); } } /* compact if needed */ if(redata_getsize(redata)>(redata->chunkdatasize) && redata_getavailable(redata)>(redata_getsize(redata)/2)) redata_compact(redata); /* mark as modified */ redata->needs_saving=1; return(0); } int redata_op_addn(redata_t *redata, long pos, char character, long n, undostack_t *fromhere) { if(redata==NULL || n<0) return(-1); /* sanity check failed */ if(n==0) return(0); /* nothing to do */ if(redata->sizeaddnbuf<n) { char *newptr; long newsize; newsize=(n+1+ADDNBLOCK)/ADDNBLOCK; newsize*=ADDNBLOCK; if((newptr=realloc(redata->addnbuf,newsize))==NULL) return(-1); /* insuf. mem. */ redata->addnbuf=newptr; redata->sizeaddnbuf=newsize; } memset(redata->addnbuf,character,n); redata->addnbuf[n]='\0'; return(redata_op_add(redata, pos, redata->addnbuf, n, fromhere)); } int redata_op_del(redata_t *redata, long delpos, long size, undostack_t *fromhere) { int chunkno,curchunk; int offset; undo_t *undo; rechunk_t *chunk; long ndel; long curpos,curdel; int i; if(redata==NULL || size<0 || delpos<0 || (delpos+size)>redata_getused(redata) || (fromhere!=NULL && (fromhere!=&(redata->undostack) && fromhere!=&(redata->redostack)))) return(-1); /* sanity check failed */ if(redata_getposptr(redata,delpos,&chunkno,&offset)==-1) return(-1); /* invalid pos */ if(fromhere!=&(redata->undostack)) { if((undo=redata_undo_newfromchunks(redata,&(redata->undostack),'D',delpos,size))==NULL) return(-1); /* couldn't create undo struct */ #ifdef REDATA_HASHUNDO redata_hash(redata,undo->prehash); #endif } else { undo=NULL; } for(curchunk=chunkno,ndel=0;ndel<size;curchunk++) { curpos=(curchunk==chunkno)?offset:0; curdel=redata->chunks[curchunk]->useddata-curpos; curdel=(curdel>(size-ndel))?(size-ndel):curdel; redata_chunk_deletedata(redata,curchunk,curpos,curdel); ndel+=curdel; } /* fix nl and delete unused chunks */ for(curchunk--;curchunk>=chunkno;curchunk--) { chunk=redata->chunks[curchunk]; if(chunk->useddata==0) { /* move this chunk to the end */ redata_chunk_deletechunk(redata,curchunk); } else redata_fix_nl(redata,curchunk); } /* activate undo */ if(undo!=NULL) { /* new or from redo stack */ undo->posorig=undo->posdest=delpos; #ifdef REDATA_HASHUNDO redata_hash(redata,undo->posthash); #endif if(fromhere==&(redata->redostack)) redata_undo_removelast(redata,&(redata->redostack)); else redata_undo_wipe(redata,&(redata->redostack)); } else { /* from undo stack */ redata_undo_movelast(redata,&(redata->undostack),&(redata->redostack)); } /* add to plugins (unsaved,...) */ for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].add_or_unadd!=NULL) { redata->plugins[i].add_or_unadd(redata,redata->plugins+i ,(undo!=NULL)?redata->undostack.undo+redata->undostack.usedundo-1 :redata->redostack.undo+redata->redostack.usedundo-1 ,(undo!=NULL)?0:1); } } /* compact if needed */ if(redata_getsize(redata)>(redata->chunkdatasize) && redata_getavailable(redata)>(redata_getsize(redata)/2)) redata_compact(redata); /* mark as modified */ redata->needs_saving=1; return(0); } int redata_op_move(redata_t *redata, long posorig, long size, long posdest, undostack_t *fromhere) { int chunkno,dchunkno,curchunk; int offset,doffset; undo_t *undo; rechunk_t *chunk; int i; if(redata==NULL || size<0 || posorig<0 || (posorig+size)>redata_getused(redata) || posdest<0 || (posdest>=posorig && posdest<(posorig+size)) || (fromhere!=NULL && (fromhere!=&(redata->undostack) && fromhere!=&(redata->redostack)))) return(-1); /* sanity check failed */ if(redata_getposptr(redata,posorig,&chunkno,&offset)==-1) return(-1); /* invalid pos */ if(redata_getposptr(redata,posdest,&dchunkno,&doffset)==-1) return(-1); /* invalid pos */ if(fromhere!=&(redata->undostack)) { if((undo=redata_undo_newfromchunks(redata,&(redata->undostack),'M',posorig,size))==NULL) return(-1); /* couldn't create undo struct */ /* inactivate the undo so we are able to use return(-1) without removing it */ redata_undo_inactivatelast(redata,&(redata->undostack)); #ifdef REDATA_HASHUNDO redata_hash(redata,undo->prehash); #endif } else { undo=NULL; } if((offset+size)<=redata->chunks[chunkno]->useddata && (chunkno==dchunkno || (redata->chunkdatasize-redata->chunks[dchunkno]->useddata)>=size)) { /* trivial case: (all the data is in the same chunk) AND (it is intra-chunk move or destination chunk has enough avail. space) */ redata_chunk_movedata(redata, chunkno, offset, dchunkno, doffset, size); } else { /* data spans several chunks, no space on dest, etc: do it the hard way */ /* separate the selected data into its own chunk(s) */ /* lower positions have to make the boundary first (or risk undoing it with next create boundary) */ if(posdest<posorig) { /* make a chunk boundary in posdest */ if(redata_getposptr(redata,posdest,&chunkno,&offset)==-1 || redata_chunk_splithere(redata,chunkno,offset)!=0) return(-1); /* invalid pos or insuf. mem */ } /* make a chunk boundary in posorig and in posorig+size */ if(redata_getposptr(redata,posorig,&chunkno,&offset)==-1 || redata_chunk_splithere(redata,chunkno,offset)!=0) return(-1); /* invalid pos or insuf. mem */ if(redata_getposptr(redata,posorig+size,&chunkno,&offset)==-1 || redata_chunk_splithere(redata,chunkno,offset)!=0) return(-1); /* invalid pos or insuf. mem */ if(posdest>posorig) { /* make a chunk boundary in posdest */ if(redata_getposptr(redata,posdest,&chunkno,&offset)==-1 || redata_chunk_splithere(redata,chunkno,offset)!=0) return(-1); /* invalid pos or insuf. mem */ } /* reorder the chunks */ { int schunkno; int soffset; if(redata_getposptr(redata,posorig,&chunkno,&offset)==-1) return(-1); /* invalid pos or insuf. mem */ if(redata_getposptr(redata,posorig+size,&schunkno,&soffset)==-1) return(-1); /* invalid pos or insuf. mem */ if(redata_getposptr(redata,posdest,&dchunkno,&doffset)==-1) return(-1); /* invalid pos or insuf. mem */ if(offset!=0) chunkno++; if(soffset!=0) schunkno++; if(doffset!=0) dchunkno++; if(chunkno<0 || schunkno<0 || dchunkno<0 || chunkno>=redata->sizechunks || schunkno>=redata->sizechunks || dchunkno>=redata->sizechunks) return(-1); /* ERROR: INTERNAL ERROR */ /* reorder inplace inverting the bytes (as in flipping a image) */ if(chunkno<dchunkno) { meminvert(redata->chunks+chunkno,redata->chunks+dchunkno); meminvert(redata->chunks+dchunkno-(schunkno-chunkno),redata->chunks+dchunkno); meminvert(redata->chunks+chunkno,redata->chunks+dchunkno-(schunkno-chunkno)); } else { meminvert(redata->chunks+dchunkno,redata->chunks+schunkno); meminvert(redata->chunks+dchunkno,redata->chunks+dchunkno+(schunkno-chunkno)); meminvert(redata->chunks+dchunkno+(schunkno-chunkno),redata->chunks+schunkno); } } } /* fix nl and delete unused chunks */ if(posorig<posdest) { if(redata_getposptr(redata,posorig,&chunkno,&offset)==-1) return(-1); /* invalid pos */ if(redata_getposptr(redata,posdest,&dchunkno,&doffset)==-1) return(-1); /* invalid pos */ } else { /* posorig>posdest */ if(redata_getposptr(redata,posdest,&chunkno,&offset)==-1) return(-1); /* invalid pos */ if(redata_getposptr(redata,posorig+size,&dchunkno,&doffset)==-1) return(-1); /* invalid pos */ } for(curchunk=dchunkno;curchunk>=chunkno;curchunk--) { chunk=redata->chunks[curchunk]; if(chunk->useddata==0) { /* move this chunk to the end */ redata_chunk_deletechunk(redata,curchunk); } else redata_fix_nl(redata,curchunk); } /* activate undo */ if(fromhere!=&(redata->undostack)) { /* reactivate the undo, now it is fine */ redata_undo_reactivatelast(redata,&(redata->undostack)); } if(undo!=NULL) { /* new or from redo stack */ undo->posorig=posorig; undo->posdest=posdest; #ifdef REDATA_HASHUNDO redata_hash(redata,undo->posthash); #endif if(fromhere==&(redata->redostack)) redata_undo_removelast(redata,&(redata->redostack)); else redata_undo_wipe(redata,&(redata->redostack)); } else { /* from undo stack */ redata_undo_movelast(redata,&(redata->undostack),&(redata->redostack)); } /* add to plugins (unsaved,...) */ for(i=0;i<redata->sizeplugins;i++) { if(redata->plugins[i].add_or_unadd!=NULL) { redata->plugins[i].add_or_unadd(redata,redata->plugins+i ,(undo!=NULL)?redata->undostack.undo+redata->undostack.usedundo-1 :redata->redostack.undo+redata->redostack.usedundo-1 ,(undo!=NULL)?0:1); } } /* compact if needed */ if(redata_getsize(redata)>(redata->chunkdatasize) && redata_getavailable(redata)>(redata_getsize(redata)/2)) redata_compact(redata); /* mark as modified */ redata->needs_saving=1; return(0); } int redata_op_undo(redata_t *redata, long *newcursorpos) { undo_t *undo; long newpos=0; if(redata==NULL || redata->undostack.usedundo<1) return(-1); /* sanity check failed */ undo=redata->undostack.undo+redata->undostack.usedundo-1; if(undo->type=='A') { /* ADD */ newpos=undo->posorig; redata_op_del(redata,undo->posorig,undo->len,&(redata->undostack)); } else if(undo->type=='D') { /* DEL */ newpos=undo->posorig+undo->len; redata_op_add(redata,undo->posorig,redata->undostack.buf+undo->off,undo->len,&(redata->undostack)); } else if(undo->type=='M') { /* MOVE */ newpos=undo->posorig+undo->len; if(undo->posorig<undo->posdest) redata_op_move(redata,undo->posdest-undo->len, undo->len, undo->posorig,&(redata->undostack)); else redata_op_move(redata,undo->posdest, undo->len, undo->posorig+undo->len,&(redata->undostack)); } else return(-1); /* unknown operation */ if(newcursorpos!=NULL) *newcursorpos=newpos; /* mark as modified */ redata->needs_saving=1; return(0); } int redata_op_redo(redata_t *redata, long *newcursorpos) { undo_t *undo; long newpos; if(redata==NULL || redata->redostack.usedundo<1) return(-1); /* sanity check failed */ undo=redata->redostack.undo+redata->redostack.usedundo-1; if(undo->type=='A') { /* ADD */ newpos=undo->posorig+undo->len; redata_op_add(redata,undo->posorig,redata->redostack.buf+undo->off,undo->len,&(redata->redostack)); } else if(undo->type=='D') { /* DEL */ newpos=undo->posorig; redata_op_del(redata,undo->posorig,undo->len,&(redata->redostack)); } else if(undo->type=='M') { /* MOVE */ newpos=undo->posorig+undo->len; redata_op_move(redata,undo->posorig, undo->len, undo->posdest,&(redata->redostack)); } else return(-1); /* unknown operation */ #warning TODO: Is it neccessary to do an unadd to the plugins? if(newcursorpos!=NULL) *newcursorpos=newpos; /* mark as modified */ redata->needs_saving=1; return(0); } int redata_data_compare(redata_t *redata, long cmppos, char *buf, long buflen) { int chunkno; int offset; long compared; long n; int res; if(redata==NULL || cmppos<0 || buf==NULL || buflen<0 || cmppos>redata_getused(redata) || (cmppos+buflen)>redata_getused(redata)) return(-1); /* sanity check failed */ if(redata_getposptr(redata,cmppos,&chunkno,&offset)==-1) return(-1); /* invalid pos */ for(compared=0,n=0;compared<buflen && chunkno<redata->sizechunks;chunkno++,offset=0,compared+=n) { n=redata->chunks[chunkno]->useddata-offset; n=(n<0)?0:((compared+n)>buflen)?(buflen-compared):n; if((res=memcmp(redata->chunks[chunkno]->data+offset,buf+compared,n))!=0) return(res); } if(compared<buflen) return(1); return(0); } int redata_compact(redata_t *redata) { /* compact and free surplus chunks */ /* criterion: */ /* 1. if two neighbouring chunks could join with 10% free in result chunk, do it */ /* 2. if there are more than 2 unused chunks free at the end, free all unused chunks except two */ int i,l; rechunk_t *chunk,**newchunks; if(redata==NULL) return(-1); /* sanity check failed */ /* skip free chunks at end */ for(i=redata->sizechunks-1;i>=0;i--) { if(redata->chunks[i]->useddata!=0) break; } /* join neighbouring chunks where appropiate */ l=redata->chunkdatasize-(redata->chunkdatasize)/10; for(;i>0;i--) { if((redata->chunks[i]->useddata+redata->chunks[i-1]->useddata)>=l) continue; /* move data to prev. chunk, move chunk to end */ redata_chunk_fillfromnext(redata,i-1,redata->chunks[i]->useddata); if(redata->chunks[i]->useddata>0) continue; /* couldn't move data */ chunk=redata->chunks[i]; memmove(redata->chunks+i,redata->chunks+i+1,sizeof(rechunk_t *)*(redata->sizechunks-i-1)); redata->chunks[redata->sizechunks-1]=chunk; } /* free unused chunks at end (leave two empty chunks, free the rest) */ for(i=redata->sizechunks-1;i>=0;i--) { if(redata->chunks[i]->useddata!=0) break; } l=i+3; if(l<redata->sizechunks) { for(i=l;i<redata->sizechunks;i++) { free(redata->chunks[i]),redata->chunks[i]=NULL; redata->available-=redata->chunkdatasize; } if((newchunks=realloc(redata->chunks,l*sizeof(rechunk_t *)))!=NULL) redata->chunks=newchunks; redata->sizechunks=l; } return(0); } int redata_hash(redata_t *redata, char *resbuf129bytes) { return(redata_hash_gen(redata,NULL,NULL,0,resbuf129bytes)); } int redata_filehash(redata_t *redata, char *filename, char *resbuf129bytes) { if(resbuf129bytes!=NULL) *resbuf129bytes='\0'; if(filename==NULL) return(-1); return(redata_hash_gen(NULL,filename,NULL,0,resbuf129bytes)); } int redata_memhash(redata_t *redata, char *buf, long buflen, char *resbuf129bytes) { if(resbuf129bytes!=NULL) *resbuf129bytes='\0'; if(buf==NULL || buflen<0) return(-1); return(redata_hash_gen(NULL,NULL,buf,buflen,resbuf129bytes)); } undostack_t * redata_getstack(redata_t *redata, undo_t *undo) { if(redata==NULL || undo==NULL) return(NULL); /* sanity check failed */ if(undo>=redata->undostack.undo && undo<(redata->undostack.undo+redata->undostack.sizeundo)) return(&(redata->undostack)); if(undo>=redata->redostack.undo && undo<(redata->redostack.undo+redata->redostack.sizeundo)) return(&(redata->redostack)); return(NULL); /* unknown stack */ } char * redata_generic_genname(char *filename,char *prefix, char *postfix, char *buf, int bufsize) { char *name,*ptr; int filenamelen; int prelen,postlen,finallen,off; if(filename==NULL || prefix==NULL || postfix==NULL) return(NULL); filenamelen=strlen(filename); prelen=strlen(prefix); postlen=strlen(postfix); finallen=filenamelen+prelen+postlen+1; if(buf==NULL) { if((name=malloc(finallen))==NULL) return(NULL); } else { if(bufsize<filenamelen) return(NULL); name=buf; } for(ptr=filename+strlen(filename);ptr>filename && ptr[-1]!='/';ptr--) ; off=0; memcpy(name+off,filename,ptr-filename); off+=ptr-filename; memcpy(name+off,prefix,prelen); off+=prelen; memcpy(name+off,ptr,filenamelen-(ptr-filename)); off+=filenamelen-(ptr-filename); memcpy(name+off,postfix,postlen); off+=postlen; name[off]='\0'; return(name); } long redata_searchforward(redata_t *redata, long posini, char *str, int len) { int numchunk; int offset; int avail; long chunkstartpos; rechunk_t *chunk; char *ptr; if(redata==NULL || posini<0 || (posini+len)>redata_getsize(redata) || len<0 || (str==NULL && len>0)) return(-1); /* sanity check failed */ if(len==0) return(posini); /* nothing to do, empty string is always equal */ if(redata_getposptr(redata,posini,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ chunkstartpos=posini-offset; for(;numchunk<redata->sizechunks ;chunkstartpos+=chunk->useddata,numchunk++,offset=0) { chunk=redata->chunks[numchunk]; avail=chunk->useddata-offset; while(avail>0 && (ptr=memchr(chunk->data+offset,str[0],avail))!=NULL) { offset=ptr-((char *)(chunk->data)); if(redata_memcmp(redata,chunkstartpos+offset,str,len)==0) return(chunkstartpos+offset); offset++; avail=chunk->useddata-offset; } } return(-1); /* not found */ } long redata_searchbackwards(redata_t *redata, long posini, char *str, int len) { int numchunk; int offset; long chunkstartpos; rechunk_t *chunk; char *ptr; if(redata==NULL || posini<0 || (posini+len)>redata_getsize(redata) || len<0 || (str==NULL && len>0)) return(-1); /* sanity check failed */ if(len==0) return(posini); /* nothing to do, empty string is always equal */ if(redata_getposptr(redata,posini,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ while((numchunk+1)<redata->sizechunks && offset==redata->chunks[numchunk]->useddata) { numchunk++; offset=0; } chunkstartpos=posini-offset; for(offset++;numchunk>=0 ;numchunk-- ,offset=(numchunk>=0)?redata->chunks[numchunk]->useddata:0 ,chunkstartpos-=(numchunk>=0)?redata->chunks[numchunk]->useddata:0) { chunk=redata->chunks[numchunk]; while(offset>0 && (ptr=mymemrchr(chunk->data,str[0],offset))!=NULL) { offset=ptr-((char *)(chunk->data)); if(redata_memcmp(redata,chunkstartpos+offset,str,len)==0) return(chunkstartpos+offset); } } return(-1); /* not found */ } int redata_memcmp(redata_t *redata, long pos, char *str, int len) { int numchunk; int offset; int avail; int ncompared; rechunk_t *chunk; int res; if(redata==NULL || pos<0 || (pos+len)>redata_getsize(redata) || len<0 || (str==NULL && len>0)) return(-1); /* sanity check failed, return "this is smaller" */ if(len==0) return(0); /* nothing to do, empty string is always equal */ if(redata_getposptr(redata,pos,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ for(ncompared=0;numchunk<redata->sizechunks;numchunk++,offset=0) { chunk=redata->chunks[numchunk]; avail=chunk->useddata-offset; avail=(avail>(len-ncompared))?(len-ncompared):avail; if((res=memcmp(chunk->data+offset,str+ncompared,avail))!=0 || (ncompared+avail)==len) return(res); /* comparison finished */ ncompared+=avail; } return(-1); /* not enough data in chunks */ } int redata_getutf8char(redata_t *redata, long pos, char *buf, int len, int *usedbuf) { int numchunk; int offset; rechunk_t *chunk; int ooff; int req; int c; if(redata==NULL || pos<0 || pos>redata_getsize(redata) || buf==NULL || len<1 || usedbuf==NULL) return(-1); /* sanity check failed */ ooff=0; if(redata_getposptr(redata,pos,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ while(numchunk<redata->sizechunks && redata->chunks[numchunk]!=NULL && offset>=redata->chunks[numchunk]->useddata) { numchunk++; offset=0; } if(numchunk>=redata->sizechunks) return(-1); /* at end; no data */ chunk=redata->chunks[numchunk]; c=chunk->data[offset]; ((unsigned char *)buf)[ooff++]=c; if(UTF8_IS_ASCII(c) || UTF8_IS_MULTIBYTECONT(c)) req=1; else req=UTF8_MULTIBYTESTART2LEN(c); offset++; for(;ooff<len && ooff<req && numchunk<redata->sizechunks ;numchunk++,chunk=redata->chunks[numchunk],offset=0) { if(offset>=chunk->useddata) continue; c=0x80|0x40; /* initialize c to whatever multibycont for the check after the while */ while(ooff<len && ooff<req && (c=chunk->data[offset])!=0 && UTF8_IS_MULTIBYTECONT(c)) { ((unsigned char *)buf)[ooff++]=c; } if(!UTF8_IS_MULTIBYTECONT(c)) break; } *usedbuf=ooff; return(0); } int redata_getprevutf8char(redata_t *redata, long pos, char *buf, int len, int *usedbuf) { int numchunk; int offset; int ooff; int c; int n; char tmpchar; if(redata==NULL || pos<=0 || pos>redata_getsize(redata) || buf==NULL || len<1 || usedbuf==NULL) return(-1); /* sanity check failed */ if(redata_getposptr(redata,pos,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ while(numchunk>0 && offset==0) { numchunk--; offset=redata->chunks[numchunk]->useddata; } if(offset==0) return(-1); /* at start, no data */ ooff=0; c=redata->chunks[numchunk]->data[offset-1]; offset--; ((unsigned char *)buf)[ooff++]=c; while(ooff<len && (!UTF8_IS_ASCII(c) || UTF8_IS_MULTIBYTECONT(c))) { while(numchunk>0 && offset==0) { numchunk--; offset=redata->chunks[numchunk]->useddata; } if(numchunk==0 && offset==0) break; c=redata->chunks[numchunk]->data[offset-1]; offset--; ((unsigned char *)buf)[ooff++]=c; } *usedbuf=ooff; /* invert the data in buf */ for(n=0;(ooff-1-n)>n;n++) { tmpchar=buf[n]; buf[n]=buf[ooff-1-n]; buf[ooff-1-n]=tmpchar; } return(0); } int redata_getsubstr(redata_t *redata, long posini, long posend, char *buf, int len, int *usedbuf) { int numchunk; int offset; int ooff; long pos; if(redata==NULL || posini<0 || posini>redata_getsize(redata) || posend<=0 || posend>redata_getsize(redata) || buf==NULL || len<1 || usedbuf==NULL) return(-1); /* sanity check failed */ if(redata_getposptr(redata,posini,&numchunk,&offset)!=0) return(-1); /* couldn't get pos */ ooff=0; pos=posini; while(ooff<len && pos<posend) { while(numchunk<redata->sizechunks && redata->chunks[numchunk]!=NULL && offset>=redata->chunks[numchunk]->useddata) { numchunk++; offset=0; } if(numchunk>=redata->sizechunks) return(-1); /* at end; no data */ buf[ooff++]=redata->chunks[numchunk]->data[offset++]; pos++; } *usedbuf=ooff; return(0); } int redata_generic_utf8len(char *ptr, int size) { int len,i; /* calculate the number of utf8-charaters in buffer */ if(size<0 || (ptr==NULL && size!=0)) return(-1); /* for now we only count the number of code points */ /* in UTF8: 0x00-0x7f single byte chars * 0xc0-0xff leading bytes * 0x80-0xbf continuation bytes (ignore these for len)*/ /*#warning TODO: XXX support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */ for(len=0,i=0;i<size;i++) len+=((ptr[i]&0xc0)!=0x80)?1:0; return(len); /*#warning TODO: XXX Also consider tabs*/ } int redata_generic_utf8lenincomplete(char *ptr, int size, int *nstartincomplete,int *nendincomplete, int *nendrequired) { int len,i; int lastistart,lastclen; /*#warning TODO: XXX support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */ if(size<0 || (ptr==NULL && size!=0) || nstartincomplete==NULL || nendincomplete==NULL) return(-1); /* from RFC2279/RFC3629, one character is up to 6 bytes: first last Byte1 Byte2 Byte3 Byte4 U+0000 U+007F 0xxxxxxx U+0080 U+07FF 110xxxxx 10xxxxxx U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ... */ i=0; len=0; /* nstartincomplete */ *nstartincomplete=0; while(i<size && (ptr[i]&(0x80|0x40))==0x80) { (*nstartincomplete)++; i++; } /* len */ lastistart=-1; lastclen=0; for(;i<size;i++) { len+=((ptr[i]&0xc0)!=0x80)?1:0; if((ptr[i]&0x80)==0x00) { /* ASCII */ lastistart=i; lastclen=1; } else if((ptr[i]&0xc0)!=0x80) { /* multibytechar start (as ASCII case has already been processed) */ lastistart=i; lastclen=((ptr[i]&0xe0)==0xc0)?2: ((ptr[i]&0xf0)==0xe0)?3: ((ptr[i]&0xf8)==0xf0)?4: ((ptr[i]&0xfc)==0xf8)?5: ((ptr[i]&0xfe)==0xfc)?6: 1; /* unknown type of multibytechar */ } } /* nendincomplete */ *nendincomplete=0; *nendrequired=0; if(lastistart!=-1 && (lastistart+lastclen)>size) { *nendrequired=lastclen; *nendincomplete=(lastistart+lastclen-size); len--; } /* all done */ return(len); /*#warning TODO: XXX Also consider tabs*/ } char * redata_generic_utf8col(char *ptr, int size, int col) { int len,i; /* return a pointer to the "n"th ("col"th) utf8-character in buffer */ if(size<0 || (ptr==NULL && size!=0)) return(NULL); /* sanity check failed */ /* see reui_utf8len() for explanation of algorithm */ /*#warning TODO: support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */ if(col>=size) return(NULL);/* col greater than maximum possible char. count */ /* skip "col" amount of single byte chars and leading bytes */ for(len=0,i=0;len<col && i<size;i++) len+=((ptr[i]&0xc0)!=0x80)?1:0; /* if we landed in a continuation byte, advance until next single byte chars or leading byte */ while(i<size && (ptr[i]&0xc0)==0x80) i++; if(i>=size) return(NULL); /* col is beyond end of string */ return(ptr+i); /*#warning TODO: XXX Also consider tabs*/ } int redata_generic_utf8charlen(char *ptr, int maxsize) { int i; /* returns the len in bytes of the character starting at ptr (zero on error)*/ if(ptr==NULL || maxsize<1) return(0); /* sanity check failed */ /*#warning TODO: support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */ if(((unsigned char *)ptr)[0]<0x80) return(1); /* single byte char */ if((ptr[0]&0xc0)==0x80) return(0); /* error: this is continuation, not leading byte */ for(i=1;i<maxsize && (ptr[i]&0xc0)==0x80;i++) ; return(i); } inline int redata_generic_utf8isstartbyte(int candidate) { if((candidate&0xc0)!=0x80) return(1); return(0); } static int redata_hash_gen(redata_t *redata, char *filename, char *buf, long buflen, char *resbuf129bytes) { static char conv[]={"0123456789ABCDEF"}; sha3_context sha3; unsigned char *hash; int i,c; int fd=-1; struct stat statbuf; if(resbuf129bytes==NULL) return(-1); /* sanity check failed */ if(resbuf129bytes!=NULL) *resbuf129bytes='\0'; if((redata==NULL && filename==NULL && buf==NULL) || (redata!=NULL && (filename!=NULL || buf!=NULL)) || (filename!=NULL && (redata!=NULL || buf!=NULL)) || (buf!=NULL && (redata!=NULL || filename!=NULL))) { return(-1); /* sanity check failed */ } if(filename!=NULL) { if((fd=open(filename,O_RDONLY))==-1 || fstat(fd,&statbuf)!=0 || !S_ISREG(statbuf.st_mode)) { if(fd!=-1) close(fd),fd=-1; return(-1); /* file not found, couldn't query size or not regular file */ } } sha3_Init512(&sha3); if(redata!=NULL) { for(i=0;i<redata->sizechunks;i++) sha3_Update(&sha3,(void *) redata->chunks[i]->data,redata->chunks[i]->useddata); } else if(filename!=NULL) { char buf[16384]; long totalread,nread; for(totalread=0,nread=0;totalread<statbuf.st_size;totalread+=nread,nread=0) { if((nread=read(fd,buf,sizeof(buf)))<=0) { close(fd),fd=-1; return(-1); /* short read */ } sha3_Update(&sha3,(void *) buf,nread); } close(fd),fd=-1; } else if(buf!=NULL) { sha3_Update(&sha3,(void *) buf,buflen); } hash=(unsigned char *)sha3_Finalize(&sha3); for(i=0;i<64;i++) { c=hash[i]; resbuf129bytes[i<<1]=conv[((c>>4)&0xf)]; resbuf129bytes[(i<<1)+1]=conv[(c&0xf)]; } resbuf129bytes[128]='\0'; /* NOTE this is SHA3-512, not keccak (empty result is a69f..cd26) */ return(0); } int redata_line_rawinfo(redata_t *redata, long pos, long *startpos, char **startptr, int *len, int *is_continuation) { long chunkpos,newpos,endpos; int nchunk; rechunk_t *chunk; if(redata==NULL || pos<0 || pos>=redata_getused(redata)) return(-1); for(nchunk=0,chunkpos=0 ;nchunk<redata->sizechunks ;chunkpos+=(chunk!=NULL)?chunk->useddata:0,nchunk++) { if((chunk=redata->chunks[nchunk])==NULL) continue; if(pos>=chunkpos && pos<(chunkpos+chunk->useddata)) break; } if(nchunk>=redata->sizechunks) return(-1); /* pos not found */ for(newpos=pos;newpos>chunkpos && chunk->data[newpos-chunkpos-1]!='\n';newpos--) ; for(endpos=pos;endpos<(chunkpos+chunk->useddata) && chunk->data[endpos-chunkpos]!='\n';endpos++) ; if(endpos==(chunkpos+chunk->useddata)) endpos--; if(startpos!=NULL) *startpos=newpos; if(startptr!=NULL) *startptr=(char *) (chunk->data+(newpos-chunkpos)); if(len!=NULL) *len=endpos-newpos+1; if(is_continuation!=NULL) { if(!(chunk->whatin_fresh)) redata_whatin_refresh(redata,nchunk); *is_continuation=(newpos==chunkpos && chunk->whatin.iscontinuation)?1:0; } return(0); } int redata_line_realstart(redata_t *redata, long pos, long *startpos) { int is_continuation; long nextpos,newpos; if(redata==NULL || pos<0) return(-1); /* sanity check failed */ nextpos=pos; do { if(redata_line_rawinfo(redata,nextpos,&newpos,NULL,NULL,&is_continuation)==-1) return(-1); nextpos=newpos-1; } while(is_continuation && newpos>0); if(startpos!=NULL) *startpos=newpos; return(0); } int redata_line_realend(redata_t *redata, long pos, long *endpos) { long nextpos,newpos; long datasize; char *ptr; int len; int has_nl; if(redata==NULL || pos<0) return(-1); /* sanity check failed */ nextpos=pos; if((datasize=redata_getused(redata))<=0) return(-1); /* couldn't get last pos or there is no data */ do { if(redata_line_rawinfo(redata,nextpos,&newpos,&ptr,&len,NULL)==-1 || len==0) return(-1); nextpos=newpos+len; has_nl=(len>0 && ptr[len-1]=='\n')?1:0; } while(!has_nl && nextpos<datasize); if(endpos!=NULL) *endpos=newpos+len-1; return(0); } int redata_line_prevrealstart(redata_t *redata, long pos, long *startpos) { long newpos,prevpos; if(redata==NULL || pos<0) return(-1); if(redata_line_realstart(redata,pos,&newpos)==-1) return(-1); /* couldn't get start of line */ if(redata_line_realstart(redata,newpos-1,&prevpos)==-1) return(-1); /* couldn't get start of line */ if(startpos!=NULL) *startpos=prevpos; return(0); } int redata_line_nextrealstart(redata_t *redata, long pos, long *startpos) { long newpos,nextpos; if(redata==NULL || pos<0) return(-1); if(redata_line_realend(redata,pos,&newpos)==-1) return(-1); /* couldn't get end of line */ if(redata_line_realstart(redata,newpos+1,&nextpos)==-1) return(-1); /* couldn't get start of line */ if(startpos!=NULL) *startpos=nextpos; return(0); } int redata_line_inccol(redata_t *redata, long pos, int ncolrequest, long *newpos, int *ncoldone) { long curpos,startpos; char *ptr; int len; int n,i; int done; if(redata==NULL || pos<0 || ncolrequest<0) return(-1); /* sanity check failed */ if(ncolrequest==0) { if(newpos!=NULL) *newpos=pos; if(ncoldone!=NULL) *ncoldone=0; return(0); /* nothing to do */ } for(n=0,curpos=pos;n<=ncolrequest;) { if(redata_line_rawinfo(redata,curpos,&startpos,&ptr,&len,NULL)==-1 || len==0) return(-1); /* couldn't get current line data */ done=0; /* advance until we are positioned on the next char to the requested one */ for(i=startpos-curpos;i<len;i++) { if(redata_generic_utf8isstartbyte(ptr[i])) n++; if(ptr[i]=='\n' || n>ncolrequest) { done=1; break; } } curpos=startpos+i; if(done) break; } if(newpos!=NULL) *newpos=curpos; if(ncoldone!=NULL) *ncoldone=n-1; return(0); } int redata_pos2linecol(redata_t *redata, long pos, int *resline, int *rescol) { if(redata==NULL || pos<0 || pos>=redata_getused(redata)) return(-1); /* calculate line */ if(resline!=NULL) { long chunkpos,newpos; int startline; int nchunk; rechunk_t *chunk; int i; for(nchunk=0,chunkpos=0,startline=0 ;nchunk<redata->sizechunks ;chunkpos+=(chunk!=NULL)?chunk->useddata:0 ,startline+=(chunk!=NULL)?chunk->whatin.nlcount:0,nchunk++) { if((chunk=redata->chunks[nchunk])==NULL) continue; if(!(chunk->whatin_fresh)) redata_whatin_refresh(redata,nchunk); if(pos>=chunkpos && pos<(chunkpos+chunk->useddata)) break; } if(nchunk>=redata->sizechunks) return(-1); /* pos not found */ for(newpos=chunkpos,i=0;newpos<pos && i<chunk->useddata;newpos++,i++) { if(chunk->data[i]=='\n') startline++; } *resline=startline; } /* calculate col */ if(rescol!=NULL) { long realstart,curpos,startpos; char *ptr; int len; int n,i; int done; if(redata_line_realstart(redata,pos,&realstart)==-1) return(-1); /* startpos for pos not found */ for(n=0,curpos=realstart;curpos<pos;) { if(redata_line_rawinfo(redata,curpos,&startpos,&ptr,&len,NULL)==-1 || len==0) return(-1); /* couldn't get current line data */ done=0; for(i=0;i<len && (startpos+i)<pos;i++) { if(redata_generic_utf8isstartbyte(ptr[i])) n++; if(ptr[i]=='\n') { done=1; break; } } curpos=startpos+i; if(done) break; } *rescol=n; } return(0); } int redata_linecol2pos(redata_t *redata, int line, int colrequest, long *pos, int *coldone) { long chunkpos,realstart,curpos,startpos; int startline; int nchunk; rechunk_t *chunk; int i,n; char *ptr; int len; int done; if(redata==NULL || line<0 || colrequest<0) return(-1); /* find line */ for(nchunk=0,chunkpos=0,startline=0 ;nchunk<redata->sizechunks ;chunkpos+=(chunk!=NULL)?chunk->useddata:0 ,startline+=(chunk!=NULL)?chunk->whatin.nlcount:0,nchunk++) { if((chunk=redata->chunks[nchunk])==NULL) continue; if(!(chunk->whatin_fresh)) redata_whatin_refresh(redata,nchunk); if(line>=startline && line<=(startline+chunk->whatin.nlcount)) break; } if(nchunk>=redata->sizechunks) return(-1); /* line not found */ for(i=0;line!=startline && i<chunk->useddata;i++) { if(chunk->data[i]=='\n') startline++; } if(i>chunk->useddata) return(-1); /* line not found */ realstart=chunkpos+i; /* trivial case: col 0 */ if(colrequest==0) { if(coldone!=NULL) *coldone=0; if(pos!=NULL) *pos=realstart; return(0); } /* find col, stopping at the start of next char */ for(n=-1,curpos=realstart;n<colrequest;) { if(redata_line_rawinfo(redata,curpos,&startpos,&ptr,&len,NULL)==-1 || len==0) return(-1); /* couldn't get current line data */ done=0; for(i=0;i<len && n<colrequest;i++,curpos++) { if(ptr[i]=='\n') { n++; done=1; break; } else if(UTF8_IS_ASCII_OR_START(ptr[i])) { n++; if(n==colrequest) break; } } if(done) break; } if(coldone!=NULL) *coldone=n; if(pos!=NULL) *pos=curpos; return(0); } int redata_line_total(redata_t *redata) { long chunkpos; int nchunk; int startline; rechunk_t *chunk,*lastchunkwithdata; if(redata==NULL) return(-1); /* find line */ lastchunkwithdata=NULL; for(nchunk=0,chunkpos=0,startline=0 ;nchunk<redata->sizechunks ;chunkpos+=(chunk!=NULL)?chunk->useddata:0 ,startline+=(chunk!=NULL)?chunk->whatin.nlcount:0,nchunk++) { if((chunk=redata->chunks[nchunk])==NULL) continue; if(!(chunk->whatin_fresh)) redata_whatin_refresh(redata,nchunk); if(chunk->useddata>0) lastchunkwithdata=chunk; } if(lastchunkwithdata!=NULL && lastchunkwithdata->data[lastchunkwithdata->useddata-1]!='\n') startline++; return(startline); } int redata_line_getendstr(redata_t *redata, int line, char *buf, int sizebuf) { return(redata_line_getendstrtrimmed(redata,line,buf,sizebuf,NULL)); } int redata_line_getendstrtrimmed(redata_t *redata, int line, char *buf, int sizebuf, char *trimchars) { long startpos,endpos; int coldone; if(redata==NULL || line<0 || buf==NULL || sizebuf<1) return(-1); /* sanity check error */ if(redata_linecol2pos(redata,line,0,&startpos,&coldone)!=0 || redata_line_realend(redata,startpos,&endpos)!=0) { return(-1); /* line not found */ } if(endpos>startpos && redata_getchar(redata,endpos-1)=='\n') endpos--; if(trimchars!=NULL) { while(endpos>startpos && strchr(trimchars,redata_getchar(redata,endpos))!=NULL) endpos--; } if((endpos-startpos)>(sizebuf-1)) startpos=endpos-(sizebuf-1); redata_getdata(redata,startpos,endpos-startpos,buf); buf[endpos-startpos]='\0'; return(0); } int redata_line_getsize(redata_t *redata, int line) /* includes the \n */ { long startpos,endpos; int coldone; if(redata==NULL || line<0) return(-1); /* sanity check error */ if(redata_linecol2pos(redata,line,0,&startpos,&coldone)!=0 || redata_line_realend(redata,startpos,&endpos)!=0) { return(-1); /* line not found */ } return(endpos-startpos); } int redata_line_getstartstr(redata_t *redata, int line, char *buf, int sizebuf) { return(redata_line_getstartstrtrimmed(redata,line,buf,sizebuf,NULL)); } int redata_line_getstartstrtrimmed(redata_t *redata, int line, char *buf, int sizebuf, char *trimchars) { long startpos,endpos; int coldone; if(redata==NULL || line<0 || buf==NULL || sizebuf<1) return(-1); /* sanity check error */ if(redata_linecol2pos(redata,line,0,&startpos,&coldone)!=0 || redata_line_realend(redata,startpos,&endpos)!=0) { return(-1); /* line not found */ } if(endpos>startpos && redata_getchar(redata,endpos-1)=='\n') endpos--; if(trimchars!=NULL) { while(startpos<endpos && strchr(trimchars,redata_getchar(redata,startpos))!=NULL) startpos++; } if((endpos-startpos)>(sizebuf-1)) endpos=startpos+(sizebuf-1); redata_getdata(redata,startpos,endpos-startpos,buf); buf[endpos-startpos]='\0'; return(0); } static char * securesave_genname(char *filename, char *buf, int bufsize) { static char pre[]={SECURESAVEPREFIX}; static char post[]={SECURESAVEPOSTFIX}; return(redata_generic_genname(filename,pre,post,buf,bufsize)); } static void * mymemrchr(const void *s, int c, size_t n) { long i; void *res=NULL; unsigned char b; b=(*((unsigned int *)(&c)))&0xff; for(i=0;i<n;i++) { if(((unsigned char *)s)[i]==b) res=(((unsigned char *)s)+i); } return(res); } static void meminvert(void *start, void *end) { unsigned char *a=(unsigned char *)start; unsigned char *b=(unsigned char *)end; unsigned char t; for(b=b-1;a<b;a++,b--) { t=*a; *a=*b; *b=t; } } static size_t memrchroffset(char *ptr, int c, size_t n) { size_t i; for(i=n-1;i>=0;i--) { if(((unsigned char *)ptr)[i]==c) return(i); } return(-1); }