/* * recenteditor_data.c * * A programmers editor * * Structures to hold the current file contents. * * Author: Dario Rodriguez dario@softhome.net * 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 "recenteditor_data.h" #include "sha3/sha3.h" #define CHUNKSIZE 32768 #define UNSAVEDPREFIX "." #define UNSAVEDPOSTFIX ".reu" static int redata_hash_gen(redata_t *redata, char *filename, char *resbuf129bytes); static char *unsaved_genname(char *filename, char *buf, int bufsize); redata_t * redata_init(void) { redata_t *redata; if((redata=malloc(sizeof(redata_t)))==NULL) return(NULL); /* sanity check failed */ memset(redata,0,sizeof(redata_t)); /* data */ redata->sizechunks=0; redata->chunks=NULL; redata->chunkdatasize=CHUNKSIZE; redata->available=0; /* undo */ redata->sizeundo=0; redata->usedundo=0; redata->curundo=0; redata->undo=NULL; redata->undobuf=NULL; /* unsaved */ redata->unsavedfd=-1; redata->flag_unsaveddata=0; /* all done */ return(redata); } void redata_free(redata_t *redata) { int i; if(redata==NULL) return; /* nothing to do */ /* 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; /* undo */ if(redata->undo!=NULL) free(redata->undo),redata->undo=NULL; if(redata->undobuf!=NULL) free(redata->undobuf),redata->undobuf=NULL; /* unsaved */ if(redata->unsavedfd!=-1) close(redata->unsavedfd),redata->unsavedfd=-1; if(redata->unsavedfilename!=NULL) { if(redata->unsavedfilename[0]!='\0') { /* remove the file, if here, the user has validated exiting without saving */ unlink(redata->unsavedfilename); } free(redata->unsavedfilename),redata->unsavedfilename=NULL; } /* free main struct */ free(redata),redata=NULL; return; } int redata_getsize(redata_t *redata) { if(redata==NULL) return(0); /* sanity check failed */ return(redata->chunkdatasize*redata->sizechunks); } int redata_getused(redata_t *redata) { if(redata==NULL) return(0); /* sanity check failed */ return(redata_getsize(redata)-redata_getavailable(redata)); } int redata_getavailable(redata_t *redata) { if(redata==NULL) return(0); /* sanity check failed */ return(redata->available); } 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); /* unsaved */ if(redata->unsavedfd!=-1) close(redata->unsavedfd),redata->unsavedfd=-1; if(redata->unsavedfilename!=NULL) { if(redata->unsavedfilename[0]!='\0') { /* remove the file, if here, the user has validated exiting without saving */ unlink(redata->unsavedfilename); } free(redata->unsavedfilename),redata->unsavedfilename=NULL; } redata->flag_unsaveddata=0; /* all done */ return(0); } int redata_preallocate(redata_t *redata, int newsize) { int cursize; int nchunks; int i; int rechunksize; rechunk_t **newchunks,*chunk; int oldsizechunks; if(redata==NULL || redata->chunkdatasize==0 || newsize<0) return(-1); /* sanity check failed */ if((cursize=redata_getsize(redata))>=newsize) return(0); /* all done */ nchunks=(newsize-cursize+redata->chunkdatasize-1)/redata->chunkdatasize; rechunksize=sizeof(rechunk_t)-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_fill_whatin(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]; 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; return(0); } int redata_unsaved_exists(redata_t *redata, char *filename) { char unsname[PATH_MAX+1]; int fd; if((unsaved_genname(filename,unsname,sizeof(unsname)))==NULL) return(-1); if((fd=open(unsname,O_RDONLY))==-1) return(-1); close(fd),fd=-1; return(0); } int redata_unsaved_check(redata_t *redata, char *filename) { #warning TODO return(-1); } int redata_unsaved_loadappend(redata_t *redata, char *filename) { #warning TODO return(-1); } int redata_unsaved_trunc(redata_t *redata, char *filename) { #warning TODO return(-1); } int redata_unsaved_add(redata_t *redata, undo_t *undo) { #warning TODO return(-1); } int redata_unsaved_unadd(redata_t *redata, undo_t *undo) { #warning TODO return(-1); } int redata_load(redata_t *redata, char *filename, int use_unsaved) { #warning TODO: use_unsaved (apply unsaved changes after load) int fd,nread,totalread; int chunkno, avail; struct stat statbuf; rechunk_t *chunk; redata_wipe(redata); 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 */ } /* preallocate 10% more than needed */ if(redata_preallocate(redata,statbuf.st_size+(statbuf.st_size/10)+1 )) { close(fd),fd=-1; 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->sizechunks+1)*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; return(-1); /* short read */ } chunk->useddata+=nread; redata->available-=nread; redata_fill_whatin(redata,chunkno); } close(fd),fd=-1; /* unsaved */ if(use_unsaved) { /* apply missing changes and append new changes to unsaved */ redata_unsaved_loadappend(redata,filename); } else { /* nuke existing unsaved (if exists) and prepare new unsaved */ redata_unsaved_trunc(redata,filename); } /* all done */ return(0); } int redata_save(redata_t *redata, char *filename) { #warning TODO return(-1); } int redata_op_add(redata_t *redata, char *buf, int sizebuf, int pos) { #warning TODO return(-1); } int redata_op_del(redata_t *redata, int pos, int size) { #warning TODO return(-1); } int redata_op_move(redata_t *redata, int posorig, int size, int posdest) { #warning TODO return(-1); } int redata_hash(redata_t *redata, char *resbuf129bytes) { return(redata_hash_gen(redata,NULL,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(redata,filename,resbuf129bytes)); } static int redata_hash_gen(redata_t *redata, char *filename, char *resbuf129bytes) { static char conv[]={"0123456789ABCDEF"}; sha3_context sha3; unsigned char *hash; int i,c; int fd=-1; struct stat statbuf; if(resbuf129bytes!=NULL) *resbuf129bytes='\0'; if(redata==NULL || resbuf129bytes==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(filename==NULL) { for(i=0;i<redata->sizechunks;i++) sha3_Update(&sha3,(void *) redata->chunks[i]->data,redata->chunks[i]->useddata); } else { char buf[16384]; int 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; } 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); } static char * unsaved_genname(char *filename, char *buf, int bufsize) { char *name,*ptr; int filenamelen; int len,off; static char pre[]={UNSAVEDPREFIX}; static char post[]={UNSAVEDPOSTFIX}; if(filename==NULL) return(NULL); filenamelen=strlen(filename); len=filenamelen+sizeof(pre)-1+sizeof(post)-1+1; if(buf==NULL) { if((name=malloc(len))==NULL) return(NULL); } else { if(bufsize<filenamelen) return(NULL); name=buf; } for(ptr=filename+strlen(filename);ptr>0 && ptr[-1]!='/';ptr--) ; off=0; memcpy(name+off,filename,ptr-filename); off+=ptr-filename; memcpy(name+off,pre,sizeof(pre)-1); off+=sizeof(pre)-1; memcpy(name+off,ptr,filenamelen-(ptr-filename)); off+=filenamelen-(ptr-filename); memcpy(name+off,post,sizeof(post)-1); off+=sizeof(post)-1; name[off]='\0'; return(name); }