/*     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 "global.h"
BEDPLUGIN
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include "varmacro.h"

extern Editor	*newfile(const char *);


#define DEBUG(x) 

static pid_t gdbpid=0;
#define READ 0
#define WRITE 1
static int tochild[2]={0,0};
static int fromchild[2]={0,0};
#ifdef STATIC
char lookfile[]= CONFDIRLINK "/disass-use";
#else
char lookfile[]=EXAMPLEDIR "/disass-use";
#endif
   #include <string.h>
//static struct sigaction oldsact,newsact;
void (*oldsighandler)(int)=SIG_DFL;
static void pipehandler(int signr) {
	if(gdbpid) {
		close(tochild[WRITE]);
		close(fromchild[READ]);
		kill(gdbpid,9);
		}
	gdbpid=0;
	signal(signr,oldsighandler);
	}







#ifdef OLDGCCVARMACRO
#define	debugmessage(mes...) 
#define fprintf(x,mes...) {message(mes);erefresh();}
#else
#define	debugmessage(...) 
#define fprintf(x,...) {message(__VA_ARGS__);erefresh();}
#endif

//#define debugmessage(...) fprintf(stderr,__VA_ARGS__)

     /* Read characters from the pipe and echo them to `stdout'. */
//#define DEBUG(x) write(STDERR_FILENO,x,strlen(x));
//
static OFFTYPE waspos;
static int waslen;
class Editor {

public:
#include "faked.h"
int gdbstartup (void) {
	waspos=-1;
	waslen=-1;
	/*
	newsact.sa_handler=pipehandler;
	sigemptyset(&newsact.sa_mask);
	newsact.sa_flags=SA_RESTART;
	sigaction(SIGPIPE,&newsact,&oldsact);
	*/
	oldsighandler=signal(SIGPIPE,pipehandler);
     	if(pipe(tochild)==-1) 
     		return -1;
     	if(pipe(fromchild)==-1) 
     		return -1;
     	message("Starting gdb. Wait ...");
     	erefresh();
       gdbpid = fork ();
       if (gdbpid == (pid_t) 0)
         {
	close(tochild[WRITE]);
	close(fromchild[READ]);
         close(STDIN_FILENO);
         if(  dup(tochild[READ])<0) {
//s/sys_errlist\[errno\]/strerror(errno)/g
         	fprintf(stderr,"dup2(tochild[READ],STDIN_FILENO)<0 failt: %s", strerror(errno) );
         	return -1;
          	}
          close(STDOUT_FILENO);
          if(dup(fromchild[WRITE])<0) {
         	fprintf(stderr,"dup2(fromchild[WRITE],STDOUT_FILENO)<0 failt: %s", strerror(errno) );
         	return -1;
          	}
#ifdef __CYGWIN__
         if(execlp("gdb","gdb",lookfile, "--nw","-q",NULL)<0) {
#else
         if(execlp("gdb","gdb",lookfile, "-q",NULL)<0) {
#endif
         	fprintf(stderr,"execlp: %s", strerror(errno) );
         	return -1;
           	}
           return -1;
         }
       else if (gdbpid < (pid_t) 0)
         {
           /* The fork failed. */
           fprintf (stderr, "Fork failed.");
           return -1;
         }
       else
         {
         int size,bufsize=1024,i,oldflags;
         char buf[bufsize],*ptr,*newptr;
	 char breaks[]="break main\n";
	 char run[]="run\n",ch;
	 close(tochild[READ]);
	 close(fromchild[WRITE]);
	size=read(fromchild[READ],buf,bufsize);
	while(size==0) {
		write(tochild[WRITE],run,strlen(run));
		size=read(fromchild[READ],buf,bufsize);
		}; 
	if(size<0){
		fprintf(stderr,"gdb doesn't answer");
		return -1;
		}
	debugmessage("Processing answer");erefresh();
	while(size<5)
		size+=read(fromchild[READ],buf+size,bufsize-size);
	ch=buf[size];
	buf[size]=0;
	if(!(ptr=strstr(buf,"(gdb)"))) {
		message("gdb doesn't start with (gdb)");
		return -1;
		}
	buf[size]=ch;
	ptr+=5;
	debugmessage("Found (gdb)");erefresh();
/*
	while(!(newptr=strchr(ptr,'\n'))) {
		debugmessage("read %d: %s",size,buf);erefresh();
		buf[size]=ch;
		if(ch=='\n') {
			newptr=buf+size;
			break;
			}
		size+=read(fromchild[READ],buf+size,bufsize);
		ch=buf[size];
		buf[size]='\0';
		}
	ptr=newptr;
	buf[size]=ch;
*/
	write(tochild[WRITE],breaks,strlen(breaks));
	debugmessage("break main");
     	erefresh();
	size+=read(fromchild[READ],buf+size,bufsize-size);
	if((buf+size)==ptr) {
		message("failt reading from gdb pid=%d",gdbpid);
		return -1;
		}
	ch=buf[size];
	buf[size]='\0';
	if((newptr=strstr(ptr,breaks))) {
		debugmessage("Found break setting 1\n");erefresh();
		ptr=newptr+11;
		}
//	buf[size]=ch;
	while((buf+size-ptr)<10) {
		if((newptr=strstr(ptr,"(gdb)"))) {
			message("Error: look should be compiled with -g");
			return -1;
			}
		debugmessage("Read %d after:%s:\n",buf+size-ptr,ptr); ;erefresh();
		buf[size]=ch;
		size+=read(fromchild[READ],buf+size,bufsize-size);
		ch=buf[size];
		buf[size]='\0';
		if((newptr=strstr(ptr,"break mai"))) {
			debugmessage("Found break setting 2\n"); ;erefresh();
			while((buf+size-newptr)<sizeof(breaks)) 
				size+=read(fromchild[READ],buf+size,bufsize-size);
			ptr=newptr+(sizeof(breaks)-1);
			}
		}
	buf[size]=ch;

//	size+=read(fromchild[READ],buf+size,bufsize);
	buf[size]='\0';
	
	debugmessage("Before Breakpoint with:%s:",ptr); ;erefresh();
	if((newptr=strstr(ptr,"Function"))) {
		debugmessage("Recompile with -g: %s",ptr);
		erefresh();
		return -1;
		}
	newptr=ptr;
	if(!(ptr=strstr(ptr,"Breakpoint"))) {
	//	size+=read(fromchild[READ],buf+size,bufsize);
	//	buf[size]='\0';
		message("Failt setting breakpoint: %s",newptr);erefresh();
		return -1;
		}
	buf[size]=ch;
	ptr+=10;
	while(!strstr(ptr,"(gdb)")) {
		size+=read(fromchild[READ],buf+size,bufsize);
		buf[size]='\0';
		}

	write(tochild[WRITE],run,strlen(run));
	size=read(fromchild[READ],buf,bufsize);
	if(size<0) {
		message("failt reading from gdb pid=%d",gdbpid);
		return -1;
		}
	while(!strstr(buf,"Starting")){
		size+=read(fromchild[READ],buf+size,bufsize);
		buf[size]='\0';
	//	fprintf(stderr,"Cant execute help program");
	//	return -1;
		}
	buf[size]='\0';
	while(!strstr(buf,"(gdb)")) {
		size+=read(fromchild[READ],buf+size,bufsize);
		buf[size]='\0';
		}
	DEBUG("END Startup")
	return 0;
         }
      return -1;
     }
int gdbsane(void) {
	int bufsize=1024,size;
	char out[]="ptype cursor\n";
	char in[]="type = long",*res;
	char buf[bufsize];
         if(!gdbpid) {
			if(gdbstartup()<0)  {
				close(tochild[WRITE]);
				close(fromchild[READ]);
				if(gdbpid) {
					kill(gdbpid,9);
					}
				gdbpid=0;
				return -1;
				}
         	}
        lseek(fromchild[READ],0L,SEEK_END);
	write(tochild[WRITE],out,strlen(out));
	size=read(fromchild[READ],buf,bufsize);
	if(size<0) {
		message("gdbsane: failt reading from gdb pid=%d. Try again",gdbpid);
		return -1;
		}
	buf[size]='\0';
	while(!strstr(buf,"(gdb)")) {
		size+=read(fromchild[READ],buf+size,bufsize);
		buf[size]='\0';
		}
	res= strstr(buf,in);
	if(!res)
		message("%s in stead of \'type = long\'",buf);
	return 0;
	}
/*
int gdbagain(char *inp) {
	int size,bufsize=1024;
         char buf[bufsize],*ptr,*start,*end;
         if(gdbsane()!=0) {
         	return -1;
         	}
	unsigned long *gegs=(unsigned long *)inp;

	sprintf( buf,"p cursor[0]=0x%x\np cursor[1]=0x%x\np cursor[2]=0x%x\np cursor[3]=0x%x\np cursor[4]=0x%x\ndisassemble cursor cursor+4\n",gegs[0],gegs[1],gegs[2],gegs[3],gegs[4]);
	write(tochild[WRITE],buf,strlen(buf));
	ptr=NULL;
	DEBUG("before read")
	start=buf;
	while(((size=read(fromchild[READ],start,bufsize))>=0)) {
//		write(STDOUT_FILENO,buf,size);
		start=start+size;
		start[0]='\0';
	//	DEBUG(buf)
		if((ptr=strstr(buf,"End of assembler dump.")))
			break;
		}
	if(ptr) {
		int len=0xffff;
		start=strstr(buf,"Dump of assembler code from");
		start=index(start,':')+2;
		end=index(start,'>');
		ptr=index(end,'\n');
		*ptr++='\0';
		if((ptr=index(ptr,'+'))) 
			sscanf(++ptr,"%d",&len);
		if((end=index(end,'<'))&& (ptr=index(end,'>'))) {
			*end='\0';
			message("%s%s		(%d bytes)",start,++ptr,len);
			}
		else	
			message("%s		(%d bytes)",start,len);
		}
	DEBUG("end gdbagain")
	return 0;
	}
*/

int bin2ass(char *inp,char *output,int max) {
	int size,bufsize=1024;
         char buf[bufsize],*ptr,*start,*end;
         if(gdbsane()!=0) {
         	return -1;
         	}
	unsigned long *gegs=(unsigned long *)inp;

	sprintf( buf,"p cursor[0]=0x%x\np cursor[1]=0x%x\np cursor[2]=0x%x\np cursor[3]=0x%x\np cursor[4]=0x%x\ndisassemble cursor cursor+4\n",gegs[0],gegs[1],gegs[2],gegs[3],gegs[4]);
	write(tochild[WRITE],buf,strlen(buf));
	ptr=NULL;
	DEBUG("before read")
	start=buf;
	while(((size=read(fromchild[READ],start,bufsize))>=0)) {
//		write(STDOUT_FILENO,buf,size);
		start=start+size;
		start[0]='\0';
	//	DEBUG(buf)
		if((ptr=strstr(buf,"End of assembler dump.")))
			break;
		}
	if(ptr) {
		int len=0xffff;
		start=strstr(buf,"Dump of assembler code from");
		start=index(start,':')+2;
		end=index(start,'>');
		ptr=index(end,'\n');
		*ptr++='\0';
		if((ptr=index(ptr,'+'))) 
			sscanf(++ptr,"%d",&len);
		if((end=index(end,'<'))&& (ptr=index(end,'>'))) {
			*end='\0';
			snprintf(output,max, "%s%s",start,++ptr);
			}
		else	
			snprintf(output,max,"%s",start);
		return len;
		}
	DEBUG("end gdbagain")
	return 0;
	}
int gdbagain(char *inp) {
const int max=80;
	char output[max+1];
	int len= bin2ass(inp,output,max); 
	if(len>0) {
		message("%s		(%d bytes)",output,len);
		waslen=len;
		}
	else {
		waspos=-1;
		return -1;
		}
	return len;
	}
#define TAKE 25

int disassembler(void) {
	char buf[TAKE];
	memset(buf,'\0',TAKE);
	waspos=getfilepos()+geteditpos();
	getmem(waspos,  buf,TAKE); 
	gdbagain(buf);
	};

int nextdisassem(void) {
	const int max=1;
	char buf[TAKE];
	char output[max+1];
	OFFTYPE pos=getfilepos()+geteditpos();
	if(waspos!=pos) {
		getmem(pos,  buf,TAKE); 
		waslen= bin2ass(buf,output,max); 
		if(!waslen) {
			waspos=-1;
			message("conversion failt");return -1;
			}
		waspos=pos;
		}
	toborderpos(pos+waslen);
	return disassembler();
	}
};
sethelp(disassembler,"File content at cursor position is translated to assembler code.\nMakes use of gdb. Use nextdisassem to jump to the next machine code.\nUse gotosymol (elftables.plug) to jump to procedure using its\nprocedure name" )
sethelp(nextdisassem,"Jumps to next machine code instruction and disassemble it:\nCursor position is moved the length of the current machine code\nstatement further and disassembler is called." )
call(disassembler)
call(nextdisassem)
