#include "defines.h"
/*     Bed a menu-driven multi dataformat binary editor for Linux            */
/*     Copyright (C) 1998 Jaap Korthals Altes <jkaltes@hetnet.nl>       */
/*                                                                           */
/*     This program is free software; you can redistribute it and/or modify  */
/*     it under the terms of the GNU General Public License as published by  */
/*     the Free Software Foundation; either version 2 of the License, or     */
/*     (at your option) any later version.                                   */
/*                                                                           */
/*     This program is distributed in the hope that it will be useful,       */
/*     but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
/*     GNU General Public License for more details.                          */
/*                                                                           */
/*     You should have received a copy of the GNU General Public License     */
/*     along with this program; if not, write to the Free Software           */
/*     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             */
/* Fri Dec  8 22:14:27 2000                                                  */
#include "editor.h"
#include "main.h"
#include "getfile.h"
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include "screenpart.h"
#include "system.h"
#include "marks.h"
#include "screen.h"
#include "config.h"
#define MAXMARKALL 58
#define MAXMARKDAT (MAXMARKALL-maxnum-3)
const int MAXOFFTYPE=(sizeof(OFFTYPE)<8?8:sizeof(OFFTYPE));
#define marx (*marksptr)
int Editor::setmark(unsigned char *keys,int nr,OFFTYPE pos) {
	char *des=myallocarp(char *,char,MAXMARKDAT+1);
	if((pos+MAXMARKDAT+1)>filesize()) {
		int atfile= filesize()-pos;
		getmem(pos,des,atfile);
		memset(des+atfile,0,MAXMARKDAT+1-atfile);
		}
	else {
		getmem(pos,des,MAXMARKDAT);
		des[MAXMARKDAT]='\0';
		}
	return marx.putkeys(keys, nr, pos,des) ;
}
extern int mywgetch(WINDOW *win)  ;
extern bool mykbhit(void) ;
int Editor::keymark(void) {
	int i,key;
	unsigned char keys[100];
	output("Mark: Type key");
	wrefresh(editscreen);
	keys[0]=mywgetch(stdscr);
//#ifdef USE_KEYPAD
#if 1
	i=0;
	do {
		i++;
		keys[i]=(unsigned char)ERR;
		for(int z=0;z<2;z++) {
			if(mykbhit()) {
				keys[i]=mywgetch(stdscr);
				break;
				}
			usleep(5000);
			}
		} while((unsigned char)keys[i]!=(unsigned char)ERR);
	flushinp();
#else
	halfdelay(1); 
	wrefresh(stdscr);
	for(i=1;(key=mywgetch(stdscr))!=ERR;i++) {
		halfdelay(1);  /*mywgetch does nocbreak */
	 	keys[i]=(unsigned char)key;
		}
	if(i==1&&*keys==27)
		keys[i++]=secondescape;
	flushinp();
	nocbreak();
	cbreak();
	refresh();
#endif
	output("Mark made");
#ifdef USETHREADS
	sem_wait(&sema);
#endif
	key= setmark(keys,i,filepos+editpos);
#ifdef USETHREADS
	sem_post(&sema);
#endif
	return key;
	};
int Editor::tomark(void) {
	int key;
	struct keylookup::mark *mark;
	output("Goto mark: type key");
	wrefresh(editscreen);

while(1) {
	key=ggetch(stdscr);
	#ifdef USETHREADS
		sem_wait(&sema);
	#endif
	if((mark=marx.lookup(key)))
		break;
	if(marx.isstart()) {
		flushinp();
		beep();
		output("Goto mark: Unknown key");
		#ifdef USETHREADS
			sem_post(&sema);
		#endif
		return -1;
		};
	#ifdef USETHREADS
		sem_post(&sema);
	#endif
	}
	#ifdef USETHREADS
		sem_post(&sema);
	#endif
	flushinp();
	refresh();
	output("Goto mark: Moved");
	return topos(mark->pos);
	};
int Editor::nextmark(void) {
#ifdef USETHREADS
	sem_wait(&sema);
#endif
	struct keylookup::mark *mark;
	do {
		mark=marx.next();
		if(!mark)  {
			beep();
			#ifdef USETHREADS
			sem_post(&sema);
			#endif
			return -1;
			};
		} while((editpos+filepos)==(OFFTYPE)mark->pos);
#ifdef USETHREADS
sem_post(&sema);
#endif
	return topos(mark->pos);
	};

int Editor::prevmark(void) {
#ifdef USETHREADS
	sem_wait(&sema);
#endif
	struct keylookup::mark *mark;
	do {
		mark=marx.prev();
		if(!mark)  {
			beep();
			#ifdef USETHREADS
			sem_post(&sema);
			#endif
			return -1;
			};
		} while((editpos+filepos)==(OFFTYPE)mark->pos);
#ifdef USETHREADS
sem_post(&sema);
#endif
	return topos(mark->pos);
	};
#define STARTSHOW 10

extern int getindatatype(ScreenPart *thesub) ;
int Editor::showmarks(void) {
	static int size=STARTSHOW;
	static char **names=myallocarpr(char **,char*,STARTSHOW);
	struct keylookup::mark *mark,*first;
	int i,take=0,ant,tot,ret=-1;
	ScreenPart *activepart=parts[mode]->getsubpart();
#ifdef USETHREADS
	sem_wait(&sema);
#endif
	first=marx.next();
	marx.tobegin();
      int aloc=maxnum(((MAXMARKDAT/activepart->bytes)+1)*(activepart->maxres+activepart->apart+1),MAXFILES)+1+sizeof(mark);
	for(i=0;(mark=marx.next());i++) {
		if(first==mark)
			take=i;
		if(i>=size) {
			int oldsize=size;
			size*=2;
			names=myrealloc(char **,names,char*,size,oldsize);
			}
		names[i]=myallocar(char,aloc);
		int slen=activepart->fromascii((unsigned char*)names[i], (unsigned char *)mark->des,MAXMARKDAT);
		if(slen<MAXMARKDAT)
			for(int s=slen;s<MAXMARKDAT;s++)
				names[i][s]=' ';
		for(int s=MAXMARKDAT;s<(MAXMARKDAT+3);s++)
			names[i][s]=' ';
		snprintf(names[i]+MAXMARKDAT+3,MAXFILES,numstr  ,mark->pos); 
		memcpy(names[i]+MAXFILES+1,((char *)&mark),sizeof(mark));
		}
#ifdef USETHREADS
sem_post(&sema);
#endif
	tot=i;
	while((ant=selectitemindex("Select mark",names,i,take))!=LONG_MAX) {
			if(ant==(LONG_MAX-1)) {
				menuresized();
				wrefresh(editscreen);
				}
			else {
				memcpy((char *)&first	,names[ant]+MAXFILES+1,sizeof(first));
				int off=getindatatype(activepart);
				if(off<first->pos)
					ret=toborderpos(first->pos-off) ;
				else
					ret=topos(first->pos) ;
				goto end;
				break; 
				 }
		}
end:
	marx.select(first);
	marx.prev();
	for(i=0;i<tot;i++)
		myrefree(names[i]);

	editup();
	return ret;
	}


int Editor::clearmarks(void) {
	int nr;
	if(marx.head==&marx.begin) {
		message("No marks to clear");
		return -1;
		}
	switch(getkeyinput("You really want to clear all marks? (y/n)")) {
		case 'y':
		case 'Y': 
#ifdef USETHREADS
	sem_wait(&sema);
#endif
	nr=marx.deleteall() ; 
#ifdef USETHREADS
sem_post(&sema);
#endif
			message("%d marks deleted",nr);
			break;
		default: message("Cancelled!");
		}
	return 0;
	}
int Editor::deletemark(void) {
	static int size=STARTSHOW;
	static char **names=myallocarpr(char **,char*,STARTSHOW);
	struct keylookup::mark *mark,*first;
	int i,take=0,ant,tot,ret=-1;

	ScreenPart *activepart=parts[mode]->getsubpart();
#ifdef USETHREADS
	sem_wait(&sema);
#endif
	first=marx.next();
	marx.tobegin();
      int aloc=maxnum(((MAXMARKDAT/activepart->bytes)+1)*(activepart->maxres+activepart->apart+1),MAXFILES)+1+sizeof(mark);
	for(i=0;(mark=marx.next());i++) {
		if(first==mark)
			take=i;
		if(i>=size) {
			int oldsize=size;
			size*=2;
			names=myrealloc(char **,names,char*,size,oldsize);
			}
		names[i]=myallocar(char,aloc);
		int slen=activepart->fromascii((unsigned char*)names[i], (unsigned char *)mark->des,MAXMARKDAT);
		if(slen<MAXMARKDAT)
			for(int s=slen;s<MAXMARKDAT;s++)
				names[i][s]=' ';
		for(int s=MAXMARKDAT;s<(MAXMARKDAT+3);s++)
			names[i][s]=' ';
		snprintf(names[i]+MAXMARKDAT+3,MAXFILES,numstr  ,mark->pos); 
		memcpy(names[i]+MAXFILES+1,((char *)&mark),sizeof(mark));
		}
	tot=i;
	while((ant=selectitemindex("Delete mark",names,i,take))!=LONG_MAX) {
			if(ant==(LONG_MAX-1)) {
				menuresized();
				wrefresh(editscreen);
				}
			else {
			memcpy((char *)&first	,names[ant]+MAXFILES+1,sizeof(first));
			marx.select(first);
			marx.rmmark(first);
			marx.prev();
				goto end;
				break; /* Break is more harmful than goto when used instead of break */
				 }
		}
	marx.select(first);
	marx.prev();
end:
	for(i=0;i<tot;i++)
		myrefree(names[i]);

#ifdef USETHREADS
sem_post(&sema);
#endif
	editup();
	return ret;
	}

const int MARKFILEGEGSIZE=300;
char marksign[]="\276\320bed marks of ";
char marksend[]="\276\320bed end";
const int marklen=sizeof(marksign)-1;
const int marksendlen=sizeof(marksend)-1;

char markdirectory[MAXPATHLEN]=".";	
int Editor::savemarks(void) {
	char cwdold[MAXPATHLEN];
	char buf[MAXPATHLEN];
	const char *ant="*.bedm";
	int handle,nrmarks;
	strcpyn(buf,ant,MAXPATHLEN);
	getcwd(cwdold,MAXPATHLEN);
	chdir(markdirectory);
	do {
		ant=getfile("Save Marks",buf) ;
		editup();
		if(!ant) {
			getcwd(markdirectory,MAXPATHLEN);
			chdir(cwdold);
			return -1;
			}
		if((handle=openfile(ant))<0) {
			message("Can't open %s",ant);
			}
		} while(handle<0);
	getcwd(markdirectory,MAXPATHLEN);
	chdir(cwdold);
	struct keylookup::mark *mark,*first;
	first=marx.next();
	marx.tobegin();
	int emptylen= MARKFILEGEGSIZE;
	char *empty=myallocar(char,emptylen);
	memset(empty,0xAF,emptylen);



	write(handle,marksign,marklen);	
	int flen=strlen(mem.filename)+1;
	write(handle,mem.filename,flen);
	write(handle,empty,MARKFILEGEGSIZE-flen-marklen-28);
	char  pos[MAXOFFTYPE];
	memset(pos,0,MAXOFFTYPE);
	*((OFFTYPE *)pos)= filesize();

	write(handle,&pos,MAXOFFTYPE);
	long long tim=mem.statdata.st_mtime;
	write(handle,&tim,8);
	time((time_t *)&tim);
	write(handle,&tim,8);
	write(handle,empty,4);	
	
/*
4: Aantal marks
8: Datum
256: Filename
268
32
*/
	for(nrmarks=0;(mark=marx.next());nrmarks++) {
#ifdef USETHREADS
	sem_wait(&sema);
#endif
		*((OFFTYPE *)&pos	)=mark->pos;
		write(handle,pos, MAXOFFTYPE);
		write(handle,mark->des, MAXMARKDAT);
		write(handle,empty, MAXMARKALL-MAXMARKDAT);
		write(handle,&mark->nrkeys, 1);
		write(handle,mark->keys, mark->nrkeys);
		if(mark->nrkeys<10)
			write(handle,empty,10-mark->nrkeys);
#ifdef USETHREADS
sem_post(&sema);
#endif
		}
	write(handle,marksend,marksendlen);
	lseek(handle,MARKFILEGEGSIZE-4,SEEK_SET);
	write(handle,&nrmarks,4);
	close(handle);
	marx.select(first);
	marx.prev();
	editup();
	message("Marks saved to %s",ant);
	return 0;
	}
#include <sys/stat.h>
 #include <unistd.h> 


int Editor::readmarks(void) {
	char cwdold[MAXPATHLEN];
	char buf[MAXPATHLEN];
	const char *ant="*.bedm";
	int handle;
	strcpyn(buf,ant,MAXPATHLEN);

	getcwd(cwdold,MAXPATHLEN);
	chdir(markdirectory);
	do {
		ant=getfile("Save Marks",buf) ;
		editup();
		if(!ant) {
			getcwd(markdirectory,MAXPATHLEN);
			chdir(cwdold);
			return -1;
			}
		if((handle=open(ant,O_RDONLY BINARY_FLAG))<0) {
			message("Can't open %s",ant);
			}
		} while(handle<0);
	getcwd(markdirectory,MAXPATHLEN);
	chdir(cwdold);
	char regstr[marklen];
		
	if(read(handle,regstr,marklen)<marklen||memcmp(regstr,marksign,marklen)) {
		message("No bed marks file");
		close(handle);
		return -1;
		}
	struct stat sta;
        if(fstat(handle, &sta)==-1) {
		message("Stat failed"); return -1;
		}
	if(lseek(handle,sta.st_size-marksendlen,SEEK_SET)!=(sta.st_size-marksendlen)) {
		message("error seeking end of file");return -1;
		}
	char endstr[marksendlen];
	if(read(handle,endstr,marksendlen)<(marksendlen)||memcmp(endstr,marksend,marksendlen)) {
		message("Can't recognise find end marks file");
		close(handle);
		return -1;
		}
	int nrmarks;

	if(lseek(handle,MARKFILEGEGSIZE-4,SEEK_SET)!=(MARKFILEGEGSIZE-4)) {
		message("Lseek nr marks failed");return -1;
		}
	if(read(handle,&nrmarks,4)!=4) {
		message("Read nrmarks failed");return -1;
		}
	int alg= MAXOFFTYPE+MAXMARKALL+1;
	OFFTYPE pos,filsiz=filesize();
	char  *des;		
	char nrkeys;
	int errors=0;
	unsigned char keys[256];
	int i;
	for(i=0;read(handle,buf,alg)>=alg;i++) {
		pos=*((OFFTYPE*)buf);
		nrkeys=buf[MAXOFFTYPE+MAXMARKALL];
		if(pos<0||pos>=filsiz) {
			message("Offset " OFFPRINT " outside file limits",pos);
			lseek(handle,maxnum(10,nrkeys),SEEK_CUR);
			errors++;
			}
		else {
			des=myallocarp(char *,char,MAXMARKDAT+1);
			memcpy(des,buf+MAXOFFTYPE,MAXMARKDAT);
			des[MAXMARKDAT]='\0';
			if(read(handle,keys,nrkeys)<nrkeys) {
				message("Error reading keys for pos "   OFFPRINT,pos); 
				myfree (des);
				return -1;
				}
			if(nrkeys<10) {
				lseek(handle,10-nrkeys,SEEK_CUR);
				}
			marx.putkeys(keys, nrkeys, pos,des) ;
			}
		}
	close(handle);
	editup();
	if(i!=nrmarks) {
		message("Found %d instead of %d marks",i,nrkeys);
		}
	if(errors)
		message("%d marks: %d added, %d outside limits",nrmarks,nrmarks-errors,errors);
	else
		message("%d marks added ",nrmarks);
	return 0;
	}
