/*
* 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 "recenteditor_data.h"
#include "keccak/KeccakHash.h"
#define CHUNKSIZE 32768
#define UNSAVEDPREFIX "."
#define UNSAVEDPOSTFIX ".reu"
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_load(redata_t *redata, char *filename, int use_unsaved)
{
#warning TODO: use_unsaved
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 )) {
if(fd!=-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) {
if(fd!=-1)
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) {
if(fd!=-1)
close(fd),fd=-1;
return(-1); /* short read */
}
chunk->useddata+=nread;
redata->available-=nread;
redata_fill_whatin(redata,chunkno);
}
/* prepare unsaved */
/* 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)
{
}
int
redata_op_del(redata_t *redata, int pos, int size)
{
}
int
redata_op_move(redata_t *redata, int posorig, int size, int posdest)
{
}
int
redata_hash(redata_t *redata, char *resbuf129bytes)
{
static char conv[]={"0123456789ABCDEF"};
Keccak_HashInstance hash;
int i,c;
if(resbuf129bytes!=NULL)
*resbuf129bytes='\0';
if(redata==NULL || resbuf129bytes==NULL) {
return(-1); /* sanity check failed */
}
if(Keccak_HashInitialize_SHA3_512(&hash)!=SUCCESS)
return(-1); /* init failed */
for(i=0;i<redata->sizechunks;i++) {
if(Keccak_HashUpdate(&hash, (void *) redata->chunks[i]->data,redata->chunks[i]->useddata)!=SUCCESS)
return(-1); /* hash calc. error */
}
if(Keccak_HashFinal(&hash, (void *) resbuf129bytes)!=SUCCESS) {
*resbuf129bytes='\0';
return(-1); /* hash final calc. error */
}
resbuf129bytes[128]='\0';
for(i=63;i>=0;i--) {
c=((unsigned char *)resbuf129bytes)[i];
resbuf129bytes[i<<1]=conv[((c>>4)&0xf)];
resbuf129bytes[(i<<1)+1]=conv[(c&0xf)];
}
#warning TODO: result is not as expected
return(0);
}