/*
Copyright (C) 2006 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)

#define SET_OK(ok, v)	do { (ok) = ((v) != -1) || (errno == 0); } while(0)
#define EDB_WORDSIZE	sizeof(long)

//------------------------------------------------------------------------------
// 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 DebuggerCore::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 DebuggerCore::readByte(edb::address_t address, bool &ok) {

	// TODO: handle if breakponts have a size more than 1!
	quint8 ret = readByteBase(address, ok);
	
	if(ok) {
		Breakpoint *const 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 DebuggerCore::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) | v;
		if(ok) {
			writeData(address, v, ok);
		}
	}
}

//------------------------------------------------------------------------------
// Name: readByteBase(edb::address_t address, bool &ok)
// Desc: the base implementation of reading a byte
//------------------------------------------------------------------------------
quint8 DebuggerCore::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 DebuggerCore::readPages(edb::address_t address, void *buf, std::size_t count) {

	Q_ASSERT(buf != 0);
	
	bool ok = false;

	if(attached()) {
		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) {
					const long v = readData(address, ok);
					if(!ok) {
						return ok;
					}

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

			// TODO: handle if breakponts have a size more than 1!
			foreach(const 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 ok;
}

//------------------------------------------------------------------------------
// 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 DebuggerCore::readBytes(edb::address_t address, void *buf, std::size_t len) {

	Q_ASSERT(buf != 0);

	bool ok = false;
	
	if(attached()) {
		if(len != 0) {
			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 ok;
}

//------------------------------------------------------------------------------
// Name: writeBytes(edb::address_t address, const void *buf, std::size_t len)
// Desc: writes <len> bytes from <buf> starting at <address>
//------------------------------------------------------------------------------
bool DebuggerCore::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: doExecProcess(const QString &path, const QString &cwd, const QStringList &args)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::doExecProcess(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;

		execvp(argv_pointers[0], &argv_pointers[0]);

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