/*
Copyright (C) 2006 - 2009 Evan Teran
                   eteran@alum.rit.edu

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

// this code is common to all unix variants (linux/bsd/osx)

#include "DebuggerCoreUNIX.h"

#include <climits>
#include <cerrno>
#include <cstring>
#include <QStringList>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>

static int selfpipe[2];
static void (*old_sigchld_handler)(int sig, siginfo_t *, void *);

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
int DebuggerCoreUNIX::native_execvp(const char *file, char *const argv[]) {
	int ret;
	do {
		ret = ::execvp(file, argv);
	} while(ret == -1 && errno == EINTR);
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
ssize_t DebuggerCoreUNIX::native_read(int fd, void *buf, size_t count) {
	ssize_t ret;
	do {
		ret = ::read(fd, buf, count);
	} while(ret == -1 && errno == EINTR);
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
ssize_t DebuggerCoreUNIX::native_write(int fd, const void *buf, size_t count) {
	ssize_t ret;
	do {
		ret = ::write(fd, buf, count);
	} while(ret == -1 && errno == EINTR);
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
int DebuggerCoreUNIX::native_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) {
	int ret;
	do {
		ret = ::select(nfds, readfds, writefds, exceptfds, timeout);
	} while(ret == -1 && errno == EINTR);
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
pid_t DebuggerCoreUNIX::native_waitpid(pid_t pid, int *status, int options) {
	pid_t ret;
	do {
		ret = ::waitpid(pid, status, options);
	} while(ret == -1 && errno == EINTR);
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
static void sigchld_handler(int sig, siginfo_t *info, void *p) {
	if(sig == SIGCHLD) {
		DebuggerCoreUNIX::native_write(selfpipe[1], "", 1);
	}
	
	if(old_sigchld_handler != 0) {
		old_sigchld_handler(sig, info, p);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
pid_t DebuggerCoreUNIX::waitpid_timeout(pid_t pid, int *status, int options, int msecs, bool &timeout) {
	if(msecs == 0) {
		timeout = false;
		return native_waitpid(pid, status, options);
	}
	
    struct timeval tv;
	
	timeout = false;

    tv.tv_sec	= (msecs / 1000);
    tv.tv_usec	= (msecs % 1000) * 1000;
	
	fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(selfpipe[0], &rfds);
	
	int ret = native_select(selfpipe[0] + 1, &rfds, 0, 0, &tv);
	
	if(ret == 0) {
		timeout = true;
		return 0;
	}
	
	char c;	
	if(native_read(selfpipe[0], &c, 1) == -1) {
		return -1;
	}
	
	return native_waitpid(pid, status, options | WNOHANG);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DebuggerCoreUNIX::DebuggerCoreUNIX() {

	// create a pipe and make it non-blocking
	int r = ::pipe(selfpipe);
	Q_UNUSED(r);
	
	::fcntl(selfpipe[0], F_SETFL, ::fcntl(selfpipe[0], F_GETFL) | O_NONBLOCK);
	::fcntl(selfpipe[1], F_SETFL, ::fcntl(selfpipe[1], F_GETFL) | O_NONBLOCK);

	// setup a signal handler
	struct sigaction new_action;
	struct sigaction old_action;
	
	std::memset(&new_action, 0, sizeof(new_action));
	std::memset(&old_action, 0, sizeof(old_action));
	
	new_action.sa_sigaction = sigchld_handler;
	new_action.sa_flags = 0;
	new_action.sa_flags |= SA_RESTART;

	sigaction(SIGCHLD, &new_action, &old_action);
	
	old_sigchld_handler = old_action.sa_sigaction;
}


//------------------------------------------------------------------------------
// Name: writeByte(edb::address_t address, quint8 value, bool &ok)
// Desc: writes a single byte at a given address
// Note: assumes the this will not trample any breakpoints, must be handled
//       in calling code!
//------------------------------------------------------------------------------
void DebuggerCoreUNIX::writeByte(edb::address_t address, quint8 value, bool &ok) {
	writeByteBase(address, value, ok);
}

//------------------------------------------------------------------------------
// Name: readByte(edb::address_t address, bool &ok)
// Desc: reads a single bytes at a given address
//------------------------------------------------------------------------------
quint8 DebuggerCoreUNIX::readByte(edb::address_t address, bool &ok) {

	// TODO: handle if breakponts have a size more than 1!
	quint8 ret = readByteBase(address, ok);
	
	if(ok) {
		QSharedPointer<Breakpoint> bp = findBreakpoint(address);
		if(bp != 0) {
			ret = bp->originalBytes()[0];
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: writeByteBase(edb::address_t address, quint8 value, bool &ok)
// Desc: the base implementation of writing a byte
//------------------------------------------------------------------------------
void DebuggerCoreUNIX::writeByteBase(edb::address_t address, quint8 value, bool &ok) {
	// TOOD: assert that we are paused
	
	ok = false;
	if(attached()) {
		long v;
		long mask;
		// pageSize() - 1 will always be 0xf* because pagesizes
		// are always 0x10*, so the masking works
		// range of a is [1..n] where n=pagesize, and we have to adjust
		// if a < wordsize
		const edb::address_t page_size = pageSize();
		const edb::address_t a = page_size - (address & (page_size - 1));
		
		v = value;
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
		if (a < EDB_WORDSIZE) {
			address -= (EDB_WORDSIZE - a);							// LE + BE
			mask = ~(0xffUL << (CHAR_BIT * (EDB_WORDSIZE - a)));	// LE
			v <<= CHAR_BIT * (EDB_WORDSIZE - a);					// LE
		} else {
			mask = ~0xffUL; // LE
		}
#else /* BIG ENDIAN */
		if (a < EDB_WORDSIZE) {
			address -= (EDB_WORDSIZE - a);				// LE + BE
			mask = ~(0xffUL << (CHAR_BIT * (a - 1)));	// BE
			v <<= CHAR_BIT * (a - 1);					// BE
		} else {
			mask = ~(0xffUL << (CHAR_BIT * (EDB_WORDSIZE - 1)));	// BE
			v <<= CHAR_BIT * (EDB_WORDSIZE - 1);					// BE
		}
#endif

		v |= (readData(address, ok) & mask);
		if(ok) {
			ok = writeData(address, v);
		}
	}
}

//------------------------------------------------------------------------------
// Name: readByteBase(edb::address_t address, bool &ok)
// Desc: the base implementation of reading a byte
//------------------------------------------------------------------------------
quint8 DebuggerCoreUNIX::readByteBase(edb::address_t address, bool &ok) {
	// TOOD: assert that we are paused

	quint8 ret = 0xff;
	ok = false;
	errno = -1;
	if(attached()) {
		// if this spot is unreadable, then just return 0xff, otherwise
		// continue as normal and hide any breakpoints we may have
		
		// pageSize() - 1 will always be 0xf* because pagesizes
		// are always 0x10*, so the masking works
		// range of a is [1..n] where n=pagesize, and we have to adjust
		// if a < wordsize
		const edb::address_t page_size = pageSize();
		const edb::address_t a = page_size - (address & (page_size - 1));
				
		if (a < EDB_WORDSIZE) {
			address -= (EDB_WORDSIZE - a);		// LE + BE
		}
		
		long value = readData(address, ok);
		
		if(ok) {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
			if (a < EDB_WORDSIZE) {
				value >>= CHAR_BIT * (EDB_WORDSIZE - a);	// LE
			}
#else
			if (a < EDB_WORDSIZE) {
				value >>= CHAR_BIT * (a - 1);				// BE
			} else {
				value >>= CHAR_BIT * (EDB_WORDSIZE - 1);	// BE
			}
#endif
			ret = value & 0xff;
		}
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: readPages(edb::address_t address, void *buf, std::size_t count)
// Desc: reads <count> pages from the process starting at <address>
// Note: buf's size must be >= count * pageSize()
// Note: address should be page aligned.
//------------------------------------------------------------------------------
bool DebuggerCoreUNIX::readPages(edb::address_t address, void *buf, std::size_t count) {

	Q_ASSERT(buf != 0);
	
	if(!attached()) {
		return false;
	}

	if((address & (pageSize() - 1)) == 0) {
		const edb::address_t orig_address	= address;
		long *ptr							= reinterpret_cast<long *>(buf);
		quint8 *const orig_ptr				= reinterpret_cast<quint8 *>(buf);

		const edb::address_t page_size		= pageSize();
		const edb::address_t end_address	= orig_address + page_size * count;

		for(std::size_t c = 0; c < count; ++c) {
			for(edb::address_t i = 0; i < page_size; i += EDB_WORDSIZE) {
				bool ok;
				const long v = readData(address, ok);
				if(!ok) {
					return false;
				}

				*ptr++ = v;
				address += EDB_WORDSIZE;
			}
		}

		// TODO: handle if breakponts have a size more than 1!
		Q_FOREACH(const QSharedPointer<Breakpoint> &bp, m_Breakpoints) {
			if(bp->address() >= orig_address && bp->address() < end_address) {
				// show the original bytes in the buffer..
				orig_ptr[bp->address() - orig_address] = bp->originalBytes()[0];
			}
		}
	}
	
	return true;
}

//------------------------------------------------------------------------------
// Name: readBytes(edb::address_t address, void *buf, std::size_t len)
// Desc: reads <len> bytes into <buf> starting at <address>
// Note: if the read failed, the part of the buffer that could not be read will 
//       be filled with 0xff bytes
//------------------------------------------------------------------------------
bool DebuggerCoreUNIX::readBytes(edb::address_t address, void *buf, std::size_t len) {

	Q_ASSERT(buf != 0);
	
	if(!attached()) {
		return false;
	}

	if(len != 0) {
		bool ok;
		quint8 *p = reinterpret_cast<quint8 *>(buf);
		quint8 ch = readByte(address, ok);

		while(ok && len) {
			*p++ = ch;
			if(--len) {
				++address;
				ch = readByte(address, ok);
			}
		}

		if(!ok) {
			while(len--) {
				*p++ = 0xff;
			}
		}
	}

	return true;
}

//------------------------------------------------------------------------------
// Name: writeBytes(edb::address_t address, const void *buf, std::size_t len)
// Desc: writes <len> bytes from <buf> starting at <address>
//------------------------------------------------------------------------------
bool DebuggerCoreUNIX::writeBytes(edb::address_t address, const void *buf, std::size_t len) {
	
	Q_ASSERT(buf != 0);
	
	bool ok = false;
	if(attached()) {
		const quint8 *p = reinterpret_cast<const quint8 *>(buf);

		while(len--) {
			writeByte(address++, *p++, ok);
			if(!ok) {
				break;
			}
		}
	}
	return ok;
}

//------------------------------------------------------------------------------
// Name: executeProcess(const QString &path, const QString &cwd, const QStringList &args)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCoreUNIX::executeProcess(const QString &path, const QString &cwd, const QStringList &args) {
	// change to the desired working directory
	if(::chdir(qPrintable(cwd)) == 0) {

		// allocate space for all the arguments
		char **const argv_pointers = new char *[args.count() + 2];

		char **p = argv_pointers;

		*p = new char[path.length() + 1];
		std::strcpy(*p, qPrintable(path));
		++p;

		for(int i = 0; i < args.count(); ++i) {
			const QString s(args[i]);
			*p = new char[s.length() + 1];
			std::strcpy(*p, qPrintable(s));
			++p;
		}

		*p = 0;

		const int ret = native_execvp(argv_pointers[0], argv_pointers);

		// should be no need to cleanup, the process which allocated all that 
		// space no longer exists!
		// if we get here...execvp failed!
		if(ret == -1) {
			p = argv_pointers;
			while(*p != 0) {
				delete [] *p++;
			}
			delete [] argv_pointers;
		}
	}
}
