/*-*-c++-*-
 * $Id: qlcrashdoc.cpp,v 1.5 2002/04/19 15:24:17 felfert Exp $
 *
 * This file is part of qlcrash, a GUI for the dump-analysis tool lcrash.
 *
 * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *
 * Authors:
 * Michael Geselbracht (let@users.sourceforge.net)
 * Fritz Elfert (elfert@de.ibm.com)
 * Michael Holzheu (holzheu@de.ibm.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */
#include "qlcrashdoc.h"
#include "parser.h"
#include "cchildmanager.h"
#include "cconfigmanager.h"
#include "structviewitem.h"
#include "crashtypes.h"
#include "clistview.h"

#include <qapplication.h>
#include <qmessagebox.h>
#include <qstringlist.h>
#include <qtimer.h>
#include <qcache.h>
#include <qstring.h>
#include <qregexp.h>

#ifndef WIN32
# ifndef _GNU_SOURCE
#  define _GNU_SOURCE
# endif
#endif

#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#ifndef WIN32
#include <stdint.h>
#ifdef USE_REGEX
#include <regex.h>
#endif
#else
#ifdef USE_REGEX
#include <hsregex.h>
#endif
#endif
#include <signal.h>

#include <iostream>
#include "avl.h"

// max. number of walk iterations
#define WALKMAX 500

using namespace std;

int hexCharToInt(unsigned char ch)
{
	int retval = ch - '0';
	if (retval > 9) {
		retval = ch - 'A' + 10;
	}
	if (retval > 15) {
		retval = ch - 'a' + 10;
	}

	return retval;
}

QLcrashDoc::QLcrashDoc(CConfigManager* cm, QObject* parent, const char* name)
	: QObject(parent, name)
	, oConfigManager(cm)
	, oGraph(false)
	, oWaitForInit(true)
	, oQuitAfterChildExit(false)
	, lcrashIsReady(false)
	, oS390dbf(-1)
{
	oParser = new CParser(this);
	oChildManager = new CChildManager(this);
	oDumpCache = new QCache<unsigned char>(100, 131);

	connect(oChildManager, SIGNAL(sigChildDied()), SLOT(slotChildDied()));
	connect(oChildManager, SIGNAL(sigStdout(QString)), SLOT(slotStdoutFromChild(QString)));
	connect(oChildManager, SIGNAL(sigStderr(QString)), SLOT(slotStderrFromChild(QString)));
	connect(oChildManager, SIGNAL(sigBusy(bool)), SIGNAL(sigBusy(bool)));
	connect(this, SIGNAL(sigSetup()), oChildManager, SLOT(slotSetup()));

	oInitialTimer = new QTimer(this);
	connect(oInitialTimer, SIGNAL(timeout()), SLOT(slotInitialTimeout()));
	oInitialTimer->start(1000, true);
}

QLcrashDoc::~QLcrashDoc()
{
}

void
QLcrashDoc::slotSetup() {
	emit sigSetup();
}

void
QLcrashDoc::slotFromConsole(CNConsole* con, QString str)
{
	// don't accept user input until initialization of lcrash is complete
	if (oWaitForInit) {
		return;
	}

	str = str.stripWhiteSpace();

	if (str.isEmpty()) {
		emit sigToConsole(con, /*QString("\n") +*/ CMD_PROMPT);
		return;
	}

	if (str == "quit" || str == "q!" || str == "q") {
		emit sigExit(true);
		return;
	}

	if (str.left(2) == "vi") {
		printErr(tr("Editing of sial functions not supported from within qlcrash"));
		return;
	}

	if (str.left(5) == "graph") {
		oGraph = true;
		str = str.mid(5).stripWhiteSpace();
	}

	if (str.left(5) == "print") {
	    QString tmp = str.mid(5).stripWhiteSpace();
	    if ((tmp.left(2) != "-x") &&
		(tmp.left(2) != "-d") &&
		(tmp.left(2) != "-o") &&
		(tmp.left(2) != "-b")   ) {
		str = QString("print") + printFormat() + tmp;
	    }
	}
	QString result;
	bool w = oChildManager->command(str, result);
	if (w) {
		if (result.length() > 0) {
			if (oGraph == true) {
				if (result.left(6) == "struct" || result.left(6) == "union") {
					oParser->setString(result);
					oParser->parseStruct();
					if (oParser->root() != 0) {
						// search address in command string
						int pos = str.find("0x", 0, false);
						if (pos != -1) {
							QString addr = getAddressFromExpression(str);

							// Address found. Store in root node.
							if (addr.length() > 2) {
								TreeNode* root = oParser->root();
								root->value = addr;
							}
						}
						QString type = getTypeFromExpression(str);
						if (!type.isEmpty()) {
							createTree(con, type);
						}
						else {
							emit sigErrToConsole(con, tr("Invalid type") + "\n");
						}
					} else {
						emit sigErrToConsole(con, tr("Parse Error") + "\n");
					}

				}
				else if (str.left(4) == "task") {
					PRINT(cerr << "graph task requested" << endl);
					oParser->setString(result);
					oParser->parseTaskList();

					if (!oParser->error()) {
						emit sigTaskList(&oParser->taskList());
					}
					else {
						PRINT(cerr << "Parse error in task list" << endl);
					}
				}
				else if (str.find(" print")) { // assuming pointer to scalar
					CListView* lv = new CListView(0);
					lv->addColumn(tr("Name"));
					lv->addColumn(tr("Value"));
					lv->addColumn(tr("Type"));
					lv->setSorting(-1);
					lv->setFindColumn(0);
					lv->setMinimumSize(200, 100);

					QString type = getTypeFromExpression(str);
					QString addr = getAddressFromExpression(str);

					if (result.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
						result.truncate(result.length() - CMD_PROMPT_LEN);
					}
					result = result.simplifyWhiteSpace();

					StructViewItem* lvi;
					lvi = new StructViewItem(lv);
					lvi->setType(type);
					lvi->setPixmapType(Scalar);
					lvi->setText(0, "*(" + addr + ")");
					lvi->setText(1, result);
					lvi->setText(2, type);

					emit sigNewGraph(lv);
					if (lv->parentWidget() == 0) { // object is unused
						delete lv;
					}
				}

				oGraph = false;
			}
			else {
				emit sigToConsole(con, result /*+ CMD_PROMPT*/);
			}
		}
		else {
			emit sigToConsole(con, CMD_PROMPT);
		}
	}
	else {
		emit sigErrToConsole(con, result + CMD_PROMPT);
		oGraph = false;
	}
}

void
QLcrashDoc::slotRestart()
{
	oQuitAfterChildExit = false;
	oChildManager->killChild();
	sigToConsole(0, "\n");

	// prevent qlcrash from exiting if call of lcrash fails
	oInitialTimer->start(1000, true);

	setLCrashPath(oConfigManager->item(CFG_GRP_FILES, CFG_ATT_FILES_LCRASH, LCRASH_DEFAULT_PATH));
	runCrash();
}

void
QLcrashDoc::slotChildDied()
{
	if (oChildManager->retval() != 0) {
		cerr << tr("lcrash exited with an error.") << endl;
	}
	if (oQuitAfterChildExit) {
		emit sigExit(false);
	}
	else {
		emit sigToConsole(0, tr("lcrash exited.") + "\n");
	}
}

void
QLcrashDoc::slotInitialTimeout()
{
//	oQuitAfterChildExit = true;
	if (lcrashIsReady)
		emit sigLCrashReady();
}

void
QLcrashDoc::runCrash()
{
	if (!oChildManager->valid()) {
		QMessageBox::critical(
			0,
			stdCaption(tr("Error")),
			tr("Cannot execute lcrash. Please check the program settings\nand restart lcrash with File->Restart.")
		);
	}
	else {
		oWaitForInit = true;
		lcrashIsReady = false;
		emit sigBusy(true);
		oChildManager->forkChild();
		oChildManager->setSignalOnTimeout(SIGINT);
	}
}

void
QLcrashDoc::setLCrashPath(const QString path)
{
#if 0
	oChildManager->setChildPath(path);

	// do some sanity checks
	if (!oChildManager->exists()) {
		QMessageBox::critical(
			0,
			stdCaption(tr("Error")),
			tr("Could not find lcrash at specified position.")
		);
	}
	else if (!oChildManager->executable()) {
		QMessageBox::critical(
			0,
			stdCaption(tr("Error")),
			tr("You do not have the permission to execute the\nspecified file.")
		);
	}
#endif
}

void
QLcrashDoc::loadConfig()
{
	setLCrashPath(oConfigManager->item(CFG_GRP_FILES, CFG_ATT_FILES_LCRASH, LCRASH_DEFAULT_PATH));

	// set opts
	int n = qApp->argc();
	QStringList list;
	char** argv = qApp->argv();
	for (int i = 1; i < n; i++) {
		list.append(argv[i]);
	}
	oChildManager->setArgs(list);

	// save directory defaults to the user's home dir
	oLastSaveDir = oConfigManager->homeDir();
}

void
QLcrashDoc::createTree(CNConsole* con, QString typeHint)
{
	TreeNode* root = oParser->root();
	if (root == 0) {
		emit sigErrToConsole(con, tr("Cannot create struct view") + "\n");
		return;
	}

	CListView* lv = new CListView(0);
	lv->addColumn(tr("Name"));
	lv->addColumn(tr("Value"));
	lv->addColumn(tr("Type"));
	lv->setSorting(-1);
	lv->setFindColumn(0);
	lv->setMinimumSize(200, 100);

	// traverse the tree created by the parser and build a QListView
	createTreeTraverse(root, lv, typeHint);

	emit sigNewGraph(lv);

	if (lv->parentWidget() == 0) {
		delete lv;
	}
}

void
QLcrashDoc::createTreeTraverse(TreeNode* root, CListView* rootItem, QString typeHint)
{
	// prepare root node...
	StructViewItem* item = new StructViewItem(rootItem);
	item->setType(root->name);
	item->setPixmapType(root->type);
	item->setText(0, root->name);
	item->setText(1, root->value);
	item->setType((root->name.length() > 0) ? root->name : typeHint);
	TreeNode* walker = root->childList.first();

	// get struct info. use type hint from caller if lcrash does not return the type of the structure
	QDict<QString>* map = structInfo(item->type());

	// also use type hint for the tree itself, if we can't obtain a type info from the print output
	if (item->text(0).length() == 0) {
		item->setText(0, typeHint);
	}

	// ...and continue with its children
	StructViewItem* after = 0;
	while (walker != 0) {
		createTreeTraverse(walker, item, map, &after, root->name);
		walker = root->childList.next();
	}

	// expand tree (first level)
	item->setOpen(true);
}

void
QLcrashDoc::createTreeTraverse(TreeNode* parentNode, StructViewItem* parentItem, QDict<QString>* theMap, StructViewItem** after, const QString& pName)
{
	if (parentNode == 0) {
		return;
	}

	StructViewItem* item = 0; // "0" to avoid a compiler warning
	QString type;

	// the tree might contain null entries which contains the children of the parent node
	if (parentNode->type != Null) {
#ifdef WIN32
#pragma warning(push)
#pragma warning(disable:4390)
#endif
		if (parentNode->name == "0") { // to set a `conditional' breakpoint in gdb
			if (parentNode);
		}
#ifdef WIN32
#pragma warning(pop)
#endif

		item = (*after == 0) ? new StructViewItem(parentItem) : new StructViewItem(parentItem, *after);
		// item->setPixmapType(parentNode->type);
		item->setText(0, parentNode->name);
		*after = item;

		if (parentNode->type == Array) {
			type = parentNode->arrayType;
		}
		else if (parentNode->type == ArrayStruct) {
			type = parentNode->value;
		}
		else { // Struct and Scalar
			type = parentNode->name;

			if (type != QString::null && theMap != 0) {
				QString* tname = theMap->find(type);
				if (tname != 0) {
					PRINT(cerr << theMap << " " << parentNode->name << endl);
					type = *tname;
				}
				else {
					type = "?";
				}
			}
			else {
				type = "?";
			}
		}

		item->setPixmapType((type.findRev("*") == -1) ? parentNode->type : Pointer);
		item->setType(type);
		if (parentNode->type == Scalar || parentNode->type == Array) {
			item->setText(1, parentNode->value);
		}
		item->setText(2, type);
	}
	else {
		item = parentItem;
		*after = 0;
	}

	// get type information for child tree
	if ((parentNode->type == Struct || parentNode->type == ArrayStruct)) {
		// in case of an array struct, the type is stored as 'value' attribute :(
		QString tmp = (parentNode->type == Struct) ? parentNode->name : parentNode->value;
		QString* tname = 0;
		if (!tmp.isEmpty()) {
			tname = (theMap != 0) ? theMap->find(tmp) : 0;
		/*
			if (tname != 0) {
				theMap = structInfo(*tname);
			}
		*/
		/*
			else if (parentNode->type == ArrayStruct && type[0] != '(') {
				theMap = structInfo(tmp);
			}
		*/

			theMap = structInfo(pName);
		}
	} // array elments are of the same type as their parent node, so get type of such a parent
	else if (parentNode->type == Array && parentNode->childList.count() > 0 && theMap != 0) {
		QString* tmp = theMap->find(parentNode->name);

		if (tmp != 0) {
			parentNode->arrayType = *tmp;

			// this is a hack to set the array type correctly for display
			item->setText(2, *tmp);
			item->setType(*tmp);
		}
		else {
			parentNode->arrayType = "?";
		}
	}

	TreeNode* walker = parentNode->childList.first();
	StructViewItem* tmp = 0;
	type = parentNode->arrayType;
	if (parentNode->type == Array) { // strip array size from type name for array elements
		if (type.right(1) == "]") {
			type.truncate(type.findRev("["));
		}
	}
	while (walker != 0) {
		// walker is an array -> assign its type. Be aware of the null-nodes (assign type if parent is array)!
		if (walker->type == Array || parentNode->type == Array) {
			walker->arrayType = type;
		}

		// build `path' from this attribute to the root data type
		QString cName = pName;
		if (walker->type != Null) { // this is a node type, not a data type :-O
			if (walker->type != Array) { // ArrayStruct || Struct
				if (walker->type == Struct) {
					cName = cName + "." + walker->name;
				}
				else { // ArrayStruct
				//	if (parentNode->type == Null) {
						cName = type;
				//	}
				}
			}
			else {
				/* keep cName */
			}
		}
		else { // save name
		//	walker->name = parentNode->name;
		}

		createTreeTraverse(walker, item, theMap, &tmp, cName);
		walker = parentNode->childList.next();
	}
}

QDict<QString>*
QLcrashDoc::structInfo(QString str)
{
	if (str == QString::null || str[0] == '(') {
		return 0;
	}

	/** `whatis' returns weird results if there are no parenthesis around the expression.
		But this doesn't work with subtypes.
	*/
	QString search = str;
	if (str.find('.') == -1) { // simple expression
		search.prepend("(");
		search.append(")");
	}

	QDict<QString>* list = oTypeMap[str];

	if (list == 0) {
		/* O.k. retrieve information from lcrash */
		QString q;
		PRINT(cerr << "whatis " + search + " ...");
		bool w = oChildManager->command(QString("whatis ") + search + "\n", q);
		PRINT(cerr << "done" << endl);

		if (w && !q.isEmpty()) {
			oParser->setString(q);
			oParser->parseWhatIs();
			list = oParser->typeMap();
			if (list == 0) {
				PRINT(cerr << "Could not get type info (" << search << ")" << endl);
			}
			else {
				oTypeMap.insert(str, list);
			}
		}
	}

	return list;
}

CListView*
QLcrashDoc::getTree(const QString& name, const QString& addr, const QString& type)
{
	QString result;
	bool w = oChildManager->command(
		QString("print ") + printFormat() + "*(" + type + ") " + addr, result);
	if (w) {
		if (result.left(6) == "struct" || result.left(6) == "union") {
			oGraph = true;
			oParser->setString(result);
			oParser->parseStruct();
			TreeNode* root = oParser->root();

			result = result.stripWhiteSpace();
			if (root != 0) {
				CListView* lv = new CListView(0);
				lv->addColumn(tr("Name"));
				lv->addColumn(tr("Value"));
				lv->addColumn(tr("Type"));
				lv->setSorting(-1);
				lv->setFindColumn(0);
				lv->setMinimumSize(200, 100);

				root->value = addr;
				QString root_t = type;
				if (root_t.right(1) == "*") {
					root_t.truncate(root_t.length() - 1);
				}
				createTreeTraverse(root, lv, root_t);
				oGraph = false;

				return lv;
			}
		}
		else { // no struct
			QString ntype = type;
			CListView* lv = new CListView(0);
			if (result.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
				result.truncate(result.length() - CMD_PROMPT_LEN);
			}
			result = result.stripWhiteSpace();
			lv->addColumn(tr("Name"));
			lv->addColumn(tr("Value"));
			lv->addColumn(tr("Type"));
			// lv->header()->hide();
			lv->setMinimumSize(200, 40);
			if (ntype.right(1) == "*") {
				ntype.truncate(type.length() - 1);
			}
			StructViewItem* svi = new StructViewItem(lv);
			svi->setText(0, name);
			svi->setText(1, result);
			svi->setText(2, ntype);
			svi->setType(ntype);
			svi->setPixmapType((ntype.findRev("*") == -1) ? Scalar : Pointer);

			return lv;
		}
	}
	else {
		emit sigErrToConsole(0, result + "\n" + CMD_PROMPT);
	}

	return 0;
}

CListView*
QLcrashDoc::getArray(const QString& addr, const QString& type, int number)
{
	CListView* retList = 0;


	// get size info
	int size = sizeinfo(type, "");
	if (size > 0) {
		// get address
		bool ret;
		unsigned int val = addr.mid(2).toUInt(&ret, 16);

		if (ret) {
			// O.k., create list
			CListView* lv = new CListView(0, "arrayList");
			lv->addColumn(tr("Name"));
			lv->addColumn(tr("Value"));
			lv->addColumn(tr("Type"));
			lv->setSorting(-1);
			lv->setMinimumSize(200, 100);

			StructViewItem* svi = new StructViewItem(lv);
			svi->setText(0, type);
			svi->setType(type);
			svi->setPixmapType(Array);

			// arrays of pointers are handled different from `normal' arrays
			bool pointer = (type.findRev('*') != -1) ? true : false;

			// ensure that item are appended to the tree at the end
			StructViewItem* after = 0;

			if (pointer) {
				/*  pointers are a bit more difficult than other types since
					we have to request the contents of each element from lcrash
				*/

				//! assuming that `sizeof(long) == sizeof(void*)' on dump source platform
				size = targetWordSize();

				for (int i = 0; i < number; i++) {
					QString result;

					bool ret = oChildManager->command("print *(void**) 0x" + QString::number(val, 16), result);
					if (result.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
                        			result.truncate(result.length() - CMD_PROMPT_LEN);
                			}
					val += size;
					if (ret) {
						result = result.stripWhiteSpace();
						StructViewItem* svch = after ? new StructViewItem(svi, after) : new StructViewItem(svi);
						after = svch;

						svch->setText(0, QString::number(i));
						svch->setText(1, result);
						svch->setText(2, type);
						svch->setType(type);
						svch->setPixmapType(Pointer);
					}
					else { // failed
						delete lv;
					}
				}
			}
			else {
				for (int i = 0; i < number; i++) {
					StructViewItem* svch = after ? new StructViewItem(svi, after) : new StructViewItem(svi);
					after = svch;

					svch->setText(0, QString::number(i));
					svch->setText(1, QString("0x") + QString::number(val, 16));
					svch->setText(2, type + "*");
					svch->setType(type + "*"); // we display an array of pointers
					svch->setPixmapType(Pointer);

					val += size;
				}
			}

			svi->setOpen(true);
			lv->setCaption(type + "[" + QString::number(number) + "]" + "@" + addr);

			retList = lv;
		}
	}

	return retList;
}

CListView*
QLcrashDoc::getWalk(const QString& name, const QString& addr, const QString& type)
{
	bool ret;
	QString result;

	// determine if `name' is a list_head
	ret = oChildManager->command("whatis " + type + "." + name, result);
	if (ret) {
		if (result.left(16) == "struct list_head") {
			return getListHead(name, addr, type);
		}
	}
	else {
		printErr(result);
		return 0;
	}

	ret = oChildManager->command("print ((" + type + "*)" + addr + ")->" + name, result);

	if (ret) {
		// remove trailing prompt
		if (result.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
			result.truncate(result.length() - CMD_PROMPT_LEN);
			result = result.stripWhiteSpace();
		}
		else {
			result = result.stripWhiteSpace();
		}
		CListView* lv = new CListView(0);
		lv->addColumn(tr("Name"));
		lv->addColumn(tr("Value"));
		lv->addColumn(tr("Type"));
		lv->setMinimumSize(200, 100);
		lv->setSorting(-1);

		StructViewItem* svi = new StructViewItem(lv);
		StructViewItem* after = 0;
		svi->setText(0, type + "." + name);
		svi->setText(1, addr);
		svi->setType(type);
		svi->setPixmapType(Array);

		int i = 0;
		Avl<QString> aset;
		do {
			aset.insert(result);
			StructViewItem* svch = after ? new StructViewItem(svi, after) : new StructViewItem(svi);
			after = svch;
			svch->setText(0, QString::number(i++));
			svch->setText(1, result);
			svch->setText(2, type + "*");
			svch->setType(type + "*");
			svch->setPixmapType(Pointer);

			// avoid infinite recursion
			if (i > WALKMAX) {
				sigErrToConsole(0, tr("Walk has reached WALKMAX (%1)").arg(QString::number(WALKMAX)));
				break;
			}

			ret = oChildManager->command("print ((" + type + "*)" + result + ")->" + name, result);
			// remove trailing prompt
			if (ret && result.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
				result.truncate(result.length() - CMD_PROMPT_LEN);
				result = result.stripWhiteSpace();
			}
			else {
				result = result.stripWhiteSpace();
			}
		} while (ret && result != "0x0" && result[0].latin1() != '(' && !aset.search(result));

		svi->setOpen(true);
		lv->setCaption("walk:" + type + "." + name + "@" + addr);

		return lv;
	}

	return 0;
}

CListView*
QLcrashDoc::getListHead(const QString& name, const QString& addr, const QString& type)
{
	QString result;
	CListView* tree = 0;

	bool ret = oChildManager->command("walk -h n -t " + type + " " + name + " " + addr, result);

	if (ret) {
		QList<QString>* list = new QList<QString>;
		list->setAutoDelete(true);

		// create a list of addresses using the first column
		const int len = result.length();
		for (int i = 0; i < len; i++) {
			// ignore white space
			while (i < len && (result[i].latin1() == ' ' || result[i].latin1() == '\t'))
				i++;

			if (isdigit(result[i].latin1())) {
				int s = i++;
				while (i < len && isalnum(result[i].latin1()))
					i++;
				list->append(new QString(result.mid(s, i - s)));
			}

			// ignore the rest of the line
			while (i < len && result[i].latin1() != '\n')
				i++;
		}

		// lreate a list view
		CListView* lv = new CListView(0);
		lv->addColumn(tr("Name"));
		lv->addColumn(tr("Value"));
		lv->addColumn(tr("Type"));
		lv->setMinimumSize(200, 100);
		lv->setSorting(-1);

		StructViewItem* svi = new StructViewItem(lv);
		StructViewItem* after = 0;
		svi->setText(0, type + "." + name);
		svi->setText(1, addr);
		svi->setType(type);
		svi->setPixmapType(Array);

		QString* walker = list->first();
		int n = 0;
		while (walker != 0) {
			if (walker->length() > 1) {
				StructViewItem* svch = after ? new StructViewItem(svi, after) : new StructViewItem(svi);
				after = svch;
				svch->setText(0, QString::number(n++));
				svch->setText(1, *walker);
				svch->setText(2, type + "*");
				svch->setType(type + "*");
				svch->setPixmapType(Pointer);
			}
			walker = list->next();
		}

		delete list;

		svi->setOpen(true);

		lv->setCaption("walk:" + type + "." + name + "@" + addr);
		tree =  lv;
	}

	return tree;
}

void
QLcrashDoc::slotStderrFromChild(QString str)
{
	emit sigErrToConsole(0, str);
}

void
QLcrashDoc::slotStdoutFromChild(QString str)
{
	if (oWaitForInit) {
		if (str.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
			oWaitForInit = false;

			lcrashIsReady = true;
			oInitialTimer->start(1000, true);
			// emit sigLCrashReady();

			// Initial newline
			emit sigToConsole(0, "\n");
		}
	}

	if (oGraph == false) {
		emit sigToConsole(0, str);
	}
}

void
QLcrashDoc::printTypeInfo(const QString& type, const QString& name)
{
	if (type == "" || type == "?") {
		sigErrToConsole(0, tr("Could not get type info") + "\n");
	}
	else {
		int size = sizeinfo(type, name);
		if (size > 0) {
			emit sigToConsole(0, QString("\n") + tr("Type") + ": " + type + "\n");
			emit sigToConsole(0, tr("sizeof") + ": " + QString::number(size) + " Bytes\n");
		}
		else {
			sigErrToConsole(0, tr("Could not get type info") + "\n");
		}
	}

	emit sigToConsole(0, CMD_PROMPT);
}

void
QLcrashDoc::printWhatis(const QString& type, const QString& name)
{
	if (type.length() == 0 || type == "?") {
		sigErrToConsole(0, tr("'whatis' failed") + "\n");
	}
	else {
		QString result;
		bool w = oChildManager->command("whatis " + type, result);

		if (w) {
			emit sigToConsole(0, type + " = " + result + "\n");
		}
		else {
			w = oChildManager->command("whatis " + name, result);

			if (w) {
				emit sigToConsole(0, name + " = " + result + "\n");
			}
			else {
				emit sigErrToConsole(0, tr("'whatis' failed") + ".\n");
			}
		}
	}

	emit sigToConsole(0, CMD_PROMPT);
}

void
QLcrashDoc::printTrace(const QString& addr)
{
	QString result;
	bool w = oChildManager->command(QString("trace") + " " + addr, result);

	if (w) {
		sigToConsole(0, (QString) "\n" + result + "\n");
	}

	emit sigToConsole(0, CMD_PROMPT);
}

int
QLcrashDoc::sizeinfo(const QString& type, const QString& name)
{
	// a pointer has always the same size

	if (type.right(1) == "*") {
		return targetWordSize();
	}


	// compare types with builtin types
	Avl<QString> set;
	QStringList list = QStringList::split(' ', type);
	for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
		set.insert(*it);
	}


	// check for 'long long'
	Avl<QString>::iterator ait = set.find("long");
	if (ait != set.end()) {
		// is there a second 'long' ?
		if (++ait != set.end() && *ait == "long") {
			return 8;
		}
		return targetWordSize();
	}
	if (set.search("int")) {
		return 4;
	}
	if (set.search("short")) {
		return 2;
	}
	if (set.search("char")) {
		return 1;
	}
#ifndef WIN32
#warning "This could become a problem..."
#endif
	if (set.search("double")) {
		return sizeof(double);
	}
	if (set.search("float")) {
		return sizeof(float);
	}


	// not a builtin type
	QString result;


	bool ret = oChildManager->command("sizeof " + type, result);
	if (!ret) { // try 'whatis' followed by 'sizeof'
		ret = oChildManager->command("whatis (" + type + ")", result);
		if (ret) {
			ret = oChildManager->command("sizeof " + result, result);
		}
		if (!ret && name.length() > 0) { // try attribute name
			ret = oChildManager->command("sizeof " + name, result);
		}
	}

	if (ret) {
		QString num = "";

		int i, len = result.length(),match_len;
		// skip to colon
		QRegExp r(": *[0-9]");
		i=r.match(result,0,&match_len);
		i+=match_len -1;


		// copy number digit by digit
		for (; i < len; i++) {
			char ch = result.at(i).latin1();
			if (!isdigit(ch)) {
				break;
			}
			num.append(ch);
		}
		int val = num.toInt(&ret);
		return ret ? val : 0;
	}
	else {
		sigErrToConsole(0, QString("\n") + tr("Size of `%1' unknown").arg(type) + "\n");
	}

	return 0;
}

void
QLcrashDoc::print(const QString& str)
{
	sigToConsole(0, str + "\n" + CMD_PROMPT);
}

void
QLcrashDoc::printErr(const QString& str)
{
	sigErrToConsole(0, str + "\n" + CMD_PROMPT);
}

QString
QLcrashDoc::printFormat() const
{
	int idx = oConfigManager->item(CFG_GRP_GRAPH, CFG_ATT_GRAPH_PRINTFORMAT, 0);

	if (idx > 0) {
		switch (idx) {
			case 1:
				return " -x ";
			case 2:
				return " -d ";
			case 3:
				return " -o ";
			case 4:
				return " -b ";
			default:
				return " ";
		}
	}

	return " ";
}

const unsigned char*
QLcrashDoc::getDump(const QString& addr)
{
	if (oWaitForInit) {
		return 0;
	}

	unsigned char* cptr = oDumpCache->find(addr);

	if (cptr == 0) {
		QString sAddr = QString("0x") + addr;
		QString result;

		bool ret = oChildManager->command("dump -B " + sAddr + " 16", result);
		if (ret) {
			cptr = new unsigned char[16];
			oDumpCache->insert(addr, cptr);
			int len = result.length();
			int i;
			// skip to the first colon
			for (i = 0; i < len && result.at(i).latin1() != ':'; i++) { /* empty */ }
			i += 2;
			unsigned char ch;
			int idx = 0, s = -1;
			while (i < len && (ch = result.at(i).latin1()) != ':') {
				i++;
				if (ch == ' ') {
					assert(idx < 16);
					cptr[idx++] = s;
					s = -1;
					continue;
				}

				s = (s == -1) ? hexCharToInt(ch) * 16 : s + hexCharToInt(ch);
			}
		}
	}

	return cptr;
}

bool
QLcrashDoc::targetLittleEndian() const
{
	volatile short t = 1;

	return *((char*)&t) == 1;
}

QStringList*
QLcrashDoc::s390AvailableLogs()
{
	QStringList* list = new QStringList;

	QString result;
	bool ret = oChildManager->command("s390dbf", result);

	if (ret) {
		*list = QStringList::split('\n', result);
		int count = list->count();
		if (count > 2) {
			QStringList::Iterator it = list->begin();
			it = list->remove(it);
			it = list->remove(it);
			for (int i = 2; i < count - 1; i++) {
				(*it) = (*it).simplifyWhiteSpace();
				(*it) = (*it).mid(2);
				++it;
			}
			list->remove(it);
		}
	}

	return list;
}

QStringList*
QLcrashDoc::s390AvailableViews(const QString& log)
{
	QStringList* list = new QStringList;

	QString result;
	bool ret = oChildManager->command("s390dbf " + log, result);

	if (ret) {
		*list = QStringList::split('\n', result);
		int count = list->count();
		if (count > 3) {
			QStringList::Iterator it = list->begin();
			it = list->remove(it);
			it = list->remove(it);
			for (int i = 2; i < count - 2; i++) {
				(*it) = (*it).simplifyWhiteSpace();
				// (*it) = QStringList::split(' ', (*it).mid(2)).first();
				(*it) = (*it).mid(2);
				++it;
			}
			it = list->remove(it);
			list->remove(it);
		}
	}

	return list;
}

QString
QLcrashDoc::s390dbfText(const QString& log, const QString& view)
{
	QString result;
	oChildManager->command("s390dbf " + log + " " + view, result);

	// remove trailing newline and prompt
	if (result.right(CMD_PROMPT_LEN) == CMD_PROMPT) {
		result.truncate(result.length() - CMD_PROMPT_LEN);
	}
	while (!result.isEmpty() && result.right(1) == "\n") {
		result.truncate(result.length() - 1);
	}

	return result;
}

void
QLcrashDoc::s390dbfSaveToFile(const QString& log, const QString& view, const QString& file)
{
	QString result;
	bool ret = oChildManager->command("s390dbf -w " + file + " " + log + " " + view, result);

	if (!ret) {
		emit printErr(result);
	}
}

bool
QLcrashDoc::haveDebugFeature()
{
	if (oWaitForInit) {
		cerr << "QLcrashDoc::haveDebugFeature() called during initialization phase." << endl;
		return false;
	}
	if (oS390dbf == -1) {
		QString result;
		bool ret = oChildManager->command("s390dbf", result);
		oS390dbf = ret ? 1 : 0;
	}

	return oS390dbf;
}

QString
QLcrashDoc::helpHomeURL()
{
#ifdef WIN32
#ifdef _DEBUG
	return QString("file:/") + execdir() + "/../../Doc/Manual/index.html";
#else
	return QString("file:/") + execdir() + "/Manual/index.html";
#endif
#else
	return "file:/" DOCDIR "/index.html";
#endif
}

QString
QLcrashDoc::lastSaveDir() const
{
	return oLastSaveDir;
}

void
QLcrashDoc::setLastSaveDir(const QString& lsd)
{
	oLastSaveDir = lsd;
}

QString
QLcrashDoc::getTypeFromExpression(const QString& expr) const
{
	int len = expr.length();
	int i = expr.find("print");
	int count = 0;
	QString type;

	if (i != -1) {
		i += 5;
	}
	else {
		i = 0;
	}

	for (; i < len; i++) {
		if (expr[i].latin1() != ' ') {
			break;
		}
	}
	for (; i < len; i++) {
		if (expr[i].latin1() == '*') {
			count--;
		}
		else {
			break;
		}
	}
	for (; i < len; i++) {
		if (expr[i].latin1() == '(') {
			break;
		}
	}

	while (i < len && !isalpha(expr[i].latin1())) {
		i++;
	}

	char ch;
	int start = i;
	while (i < len && (isalpha((ch = expr[i].latin1()))||ch=='_'||ch==' '|| (isdigit(ch) && (start < i)) )) {
		type += ch;
		i++;
	}
	for (; i < len; i++) {
		ch = expr[i].latin1();
		switch (ch) {
			case '*':
				count++;
				break;
			case ')':
				i = len;
				break;
			default:
				continue;
		}
	}

	for (i = 0; i < count; i++) {
		type += '*';
	}

	return (count >= 0) ? type : QString::null;
}

QString
QLcrashDoc::getAddressFromExpression(const QString& expr) const
{
	QString ret;
	int pos = expr.find("0x");

	if (pos >= 0) {
		const int len = expr.length();
		pos += 2;
		ret = "0x";

		while (pos < len && isxdigit(expr[pos].latin1())) {
			ret += expr[pos];
			pos++;
		}
	}

	return ret;
}

QValueList<CDisassemble>
QLcrashDoc::getDisassemble(const QString& fkt)
{
	QValueList<CDisassemble> list;

	QString result;
	bool ret = oChildManager->command("dis -F " + fkt, result);

	if (ret) {
#ifdef USE_REGEX
		regex_t reg;
		regmatch_t sub[4];
		ret = regcomp(
			&reg,
			"(0x[0-9a-f]+)[ \t]*(<[^>]+>):[ \t]*(.+)\n",
			REG_EXTENDED | REG_NEWLINE
		);
		assert(!ret);

		int offset = 0, len = result.length();
		while (offset < len && regexec(&reg, result.latin1() + offset, 4, sub, 0) == 0) {
			QString part;
			CDisassemble cd;

			part = result.mid(offset + sub[1].rm_so, sub[1].rm_eo - sub[1].rm_so);
			cd.setAddr(part.simplifyWhiteSpace());
			part = result.mid(offset + sub[2].rm_so, sub[2].rm_eo - sub[2].rm_so);
			cd.setOffset(part.simplifyWhiteSpace());
			part = result.mid(offset + sub[3].rm_so, sub[3].rm_eo - sub[3].rm_so);
			cd.setMnemonic(part.simplifyWhiteSpace());

			list.append(cd);

			offset = offset + sub[0].rm_eo;
		}

		regfree(&reg);
#else
#if QT_VERSION >= 300

		// New QT 3.0
		QRegExp r("(0x[0-9a-f]+)[ \t]*(<[^>]+>):[ \t]*([^\\n]+)\\n");

		int offset = 0, len = result.length();
		while (offset < len && (r.search(result, offset) >= 0)) {
			CDisassemble cd;
			cd.setAddr(r.cap(1).simplifyWhiteSpace());
			cd.setOffset(r.cap(2).simplifyWhiteSpace());
			cd.setMnemonic(r.cap(3).simplifyWhiteSpace());
			list.append(cd);
			offset += r.matchedLength();
		}
#else

		// Old QT < 3.0
		QRegExp r("0x[0-9a-f]+[ \t]*<[^>]+>:[ \t]*[^\\n]+\\n");
		QRegExp sub1("0x[0-9a-f]+");
		QRegExp sub2("[ \t]*");
		QRegExp sub3("<[^>]+>");
		QRegExp sub4(":[ \t]*");
		QRegExp sub5("[^\\n]+");

		int offset = 0, len = result.length();
		int rlen;
		int rpos;
		while (offset < len && ((rpos = r.match(result, offset, &rlen)) >= 0)) {
			int slen;
			CDisassemble cd;

			sub1.match(result, rpos, &slen);
			cd.setAddr(result.mid(rpos, slen).simplifyWhiteSpace());
			rpos += slen;

			sub2.match(result, rpos, &slen);
			rpos += slen;

			sub3.match(result, rpos, &slen);
			cd.setOffset(result.mid(rpos, slen).simplifyWhiteSpace());
			rpos += slen;

			sub4.match(result, rpos, &slen);
			rpos += slen;

			sub5.match(result, rpos, &slen);
			cd.setMnemonic(result.mid(rpos, slen).simplifyWhiteSpace());

			list.append(cd);

			offset += rlen;
		}
#endif // !QT 3.0
#endif // !USE_REGEX
	}

	return list;
}

QValueList<CTraceItem>
QLcrashDoc::getTrace(const QString& addr)
{
	QValueList<CTraceItem> list;

	QString result;
	bool ret = oChildManager->command("trace " + addr, result);

	if (ret) {
		int offset = 0, len = result.length();

		// skip header and retrieve task name
		while (offset < len && result[offset].latin1() != '(') {
			offset++;
		}
		QString name;
		offset++;
		while (offset < len && result[offset].latin1() != ')') {
			name.append(result[offset]);
			offset++;
		}
		CTraceItem item;
		item.setFunction(name);
		list.append(item);
		while (offset < len && !isdigit(result[offset].latin1())) {
			offset++;
		}

#ifdef USE_REGEX
		regex_t reg;
		regmatch_t sub[5];
		ret = regcomp(
			&reg,
			"[ \t]*([0-9]+)[ \t]*([a-zA-Z0-9_]+)(\\+[0-9]+)?[ \t]*\\[(0x[0-9a-zA-F]+)\\][ \t]*\n",
			REG_EXTENDED | REG_NEWLINE
		);
		assert(!ret);

		while (offset < len && regexec(&reg, result.latin1() + offset, 5, sub, 0) == 0) {
			QString part;
			CTraceItem ci;

			part = result.mid(offset + sub[1].rm_so, sub[1].rm_eo - sub[1].rm_so);
			ci.setLevel(part);
			part = result.mid(offset + sub[2].rm_so, sub[2].rm_eo - sub[2].rm_so);
			ci.setFunction(part);
			part = result.mid(offset + sub[3].rm_so, sub[3].rm_eo - sub[3].rm_so);
			ci.setOffset(part);
			part = result.mid(offset + sub[4].rm_so, sub[4].rm_eo - sub[4].rm_so);
			ci.setAddress(part);

			offset = offset + sub[0].rm_eo;
			list.append(ci);
		}

		regfree(&reg);
#else
#if QT_VERSION >= 300
		QRegExp r("[ \t]*([0-9]+)[ \t]*([a-zA-Z0-9_]+)(\\+[0-9]+)?[ \t]*\\[(0x[0-9a-zA-F]+)\\][ \t]*\\n");

		while (offset < len && (r.search(result, offset) >= 0)) {
			CTraceItem ci;
			ci.setLevel(r.cap(1));
			ci.setFunction(r.cap(2));
			ci.setOffset(r.cap(3));
			ci.setAddress(r.cap(4));
			list.append(ci);
			offset += r.matchedLength();
		}
#else
		QRegExp  r("[ \t]*[0-9]+[ \t]*[a-zA-Z0-9_]+\\+[0-9]+[ \t]*\\[0x[0-9a-zA-F]+\\][ \t]*\\n");
		QRegExp r2("[ \t]*[0-9]+[ \t]*[a-zA-Z0-9_]+[ \t]*\\[0x[0-9a-zA-F]+\\][ \t]*\\n");

		QRegExp sub1("[ \t]*");
		QRegExp sub2("[0-9]+");				// cap
		//QRegExp sub3 = sub1;
		QRegExp sub4("[a-zA-Z0-9_]+");		// cap
		QRegExp sub5("\\+[0-9]+");			// cap
		QRegExp sub6("[ \t]*\\[");
		QRegExp sub7("0x[0-9a-zA-F]+");		// cap
		//QRegExp sub8("\\][ \t]*\\n");

		int rlen;
		int rlen2;
		int rpos;
		int rpos2;

		while (offset < len &&
				(((rpos = r.match(result, offset, &rlen)) >= 0)   ||
				 ((rpos2 = r2.match(result, offset, &rlen2)) >= 0)  )) {
			int slen;
			CTraceItem ci;

			int rofs = (rpos >= 0) ? rpos : rpos2;
			int alen = (rpos >= 0) ? rlen : rlen2;
			offset = rofs + alen;

			sub1.match(result, rofs, &slen);
			rofs += slen;

			sub2.match(result, rofs, &slen);
			ci.setLevel(result.mid(rofs, slen));
			rofs += slen;

			sub1.match(result, rofs, &slen);
			rofs += slen;

			sub4.match(result, rofs, &slen);
			ci.setFunction(result.mid(rofs, slen));
			rofs += slen;

			if (rpos >= 0) {
				sub5.match(result, rofs, &slen);
				ci.setOffset(result.mid(rofs, slen));
				rofs += slen;
			} else
				ci.setOffset("");

			sub6.match(result, rofs, &slen);
			rofs += slen;

			sub7.match(result, rofs, &slen);
			ci.setAddress(result.mid(rofs, slen));
			rofs += slen;

			list.append(ci);
		}
#endif // !QT >= 3.0
#endif // !USE_REGEX
	}

	return list;
}

CLowcoreList
QLcrashDoc::getTraceLowcore(const QString& addr)
{
	CLowcoreList list;
	QString result;
	bool ret = oChildManager->command("trace " + addr, result);

	if (ret) {
		int offset;
		const int len = result.length();

		// check for lowcore info
		if ((offset = result.find("LOWCORE INFO")) != -1) {
			while (offset < len && result[offset].latin1() != '-') {
				offset++;
			}

			QString token;
			pair<QString, QStringList> p;
			while (offset < len) {
				offset = offset + getTraceLowcoreToken(result.latin1() + offset, token);
				if (token[0].latin1() == '-') { // new section
					if (!p.first.isEmpty()) {
						list.append(p);
					}
					p.first = token;
					p.second.clear();
				}
				else { // normal token
					if (token.left(5) == "STACK" || token.at(1).latin1() == '=') {
					    break;
					}
					p.second.append(token);
				}
			}

			if (!p.first.isEmpty()) {
				list.append(p);
			}
		}
	}

	return list;
}

int
QLcrashDoc::getTraceLowcoreToken(const char* str, QString& ret)
{
	bool label = false;
	const char* cptr = str;

	while (*cptr != 0 && (*cptr == ' ' || *cptr == '\t' || *cptr == '\n')) {
		cptr++;
	}

	if (*cptr == '-') {
		label = true;
		ret = "-";
		cptr++;
	}
	else {
	    ret = "";
	}
	while (*cptr != 0 && *cptr != (label ? ':' : ' ') && *cptr != '\n') {
		while (*cptr != 0 && *cptr == ' ' && *(cptr-1) == ' ') {
			cptr++;
		}
		while (*cptr != 0 && *cptr == '\n') {
		    cptr++;
		}
		if (label && *cptr == ':') {
		    cptr++;
		    break;
		}
		ret += *cptr++;
	}

	if (label && *cptr == ':') {
	    cptr++;
	}
	if (ret.at(ret.length() - 1).latin1() == ':') {
	    ret.truncate(ret.length() - 1);
	}
	if (ret.at(ret.length() - 1).latin1() == ' ') {
		ret.truncate(ret.length() - 1);
	}

	return cptr - str;
}

QValueList<CTaskItem>
QLcrashDoc::getTaskList()
{
	QValueList<CTaskItem> list;

	QString result;
	bool ret = oChildManager->command("task", result);

	if (ret) {
		// skip header
		int offset = 0, len = result.length();
		while (offset < len && !isdigit(result[offset].latin1())) {
			offset++;
		}

#ifdef USE_REGEX
		regex_t reg;
		regmatch_t sub[6];

		ret = regcomp(
			&reg,
			"(0x[a-z0-9]+)[ \t]+"		// ADDR
			"[0-9]+[ \t]+"				// UID
			"([0-9]+)[ \t]+"			// PID
			"[0-9]+[ \t]+"				// PPID
			"([0-9]+)[ \t]+"			// STATE
			"0[x0-9]*[ \t]+"			// FLAGS
			"(-|[0-9]+)[ \t]+"			// CPU
			"(.+)\n",					// NAME
			REG_EXTENDED | REG_NEWLINE
		);
		assert(!ret);

		// match line by line
		while (offset < len && regexec(&reg, result.latin1() + offset, 6, sub, 0) == 0) {
			QString part;
			CTaskItem ti;

			part = result.mid(offset + sub[1].rm_so, sub[1].rm_eo - sub[1].rm_so);
			ti.setAddress(part);
			part = result.mid(offset + sub[2].rm_so, sub[2].rm_eo - sub[2].rm_so);
			ti.setPid(part);
			part = result.mid(offset + sub[3].rm_so, sub[3].rm_eo - sub[3].rm_so);
			ti.setStatus(part);
			part = result.mid(offset + sub[4].rm_so, sub[4].rm_eo - sub[4].rm_so);
			ti.setCpu(part);
			part = result.mid(offset + sub[5].rm_so, sub[5].rm_eo - sub[5].rm_so);
			ti.setName(part);

			offset = offset + sub[0].rm_eo;

			list.append(ti);
		}

		regfree(&reg);
#else
#if QT_VERSION >= 300
		QRegExp r(
			"(0x[a-z0-9]+)[ \t]+"		// ADDR
			"[0-9]+[ \t]+"				// UID
			"([0-9]+)[ \t]+"			// PID
			"[0-9]+[ \t]+"				// PPID
			"([0-9]+)[ \t]+"			// STATE
			"0[x0-9]*[ \t]+"			// FLAGS
			"(-|[0-9]+)[ \t]+"			// CPU
			"([^\\n]+)\\n"				// NAME
			);
		while (offset < len && (r.search(result, offset) >= 0)) {
			CTaskItem ti;
			ti.setAddress(r.cap(1));
			ti.setPid(r.cap(2));
			ti.setStatus(r.cap(3));
			ti.setCpu(r.cap(4));
			ti.setName(r.cap(5));
			offset += r.matchedLength();
			list.append(ti);
		}
#else

		QRegExp r(
			"0x[a-z0-9]+[ \t]+"		// ADDR
			"[0-9]+[ \t]+"			// UID
			"[0-9]+[ \t]+"			// PID
			"[0-9]+[ \t]+"			// PPID
			"[0-9]+[ \t]+"			// STATE
			"0[x0-9]*[ \t]+"		// FLAGS
			"[0-9-]+[ \t]+"			// CPU
			"[^\\n]+\\n"			// NAME
			);
		QRegExp sub1("0x[a-z0-9]+");			// cap
		QRegExp sub2("[ \t]+[0-9]+[ \t]+");
		QRegExp sub3("[0-9]+");					// cap
		// QRegExp sub4 == sub2;
		// QRegExp sub5 == sub3;				// cap
		QRegExp sub6("[ \t]+0[x0-9]*[ \t]+");
		QRegExp sub7("[0-9-]+");				// cap
		QRegExp sub8("[ \t]+");
		QRegExp sub9("[^\\n]+");				// cap

		int rlen;
		int rpos;
		while (offset < len && ((rpos = r.match(result, offset, &rlen)) >= 0)) {
			int slen;
			CTaskItem ti;

			sub1.match(result, rpos, &slen);
			ti.setAddress(result.mid(rpos, slen));
			rpos += slen;

			sub2.match(result, rpos, &slen);
			rpos += slen;

			sub3.match(result, rpos, &slen);
			ti.setPid(result.mid(rpos, slen));
			rpos += slen;

			sub2.match(result, rpos, &slen);
			rpos += slen;

			sub3.match(result, rpos, &slen);
			ti.setStatus(result.mid(rpos, slen));
			rpos += slen;

			sub6.match(result, rpos, &slen);
			rpos += slen;

			sub7.match(result, rpos, &slen);
			ti.setCpu(result.mid(rpos, slen));
			rpos += slen;

			sub8.match(result, rpos, &slen);
			rpos += slen;

			sub9.match(result, rpos, &slen);
			ti.setName(result.mid(rpos, slen));
			rpos += slen;

			offset += rlen;
			list.append(ti);
		}

#endif // !QT >= 3.0
#endif // !USE_REGEX
	}

	return list;
}

QValueList<CSymtabItem>
QLcrashDoc::getGlobalSymbols()
{
	QValueList<CSymtabItem> list;

	QString result;
	bool ret = oChildManager->command("symtab -f -l", result);

	if (ret) {
		int offset = 0;
		const int len = result.length();
		const char* cptr = result.latin1();

		while (offset < len) {
			CSymtabItem item;
			QString part;

			// A new line...

			// ignore leading spaces
			offset += strspn(cptr + offset, " \t");

			// Address
			size_t pos = strcspn(cptr + offset, " \n");
			part = result.mid(offset, pos);
			// validate line
			if (part.left(2) != "0x") {
				offset += strcspn(cptr + offset, "\n") + 1;
				continue;
			}
			offset += pos;
			item.setAddress(part);

			offset += strspn(cptr + offset, " \t");

			// A digit
			pos = strcspn(cptr + offset, " \n");
			offset += pos;

			offset += strspn(cptr + offset, " \t");

			// Type
			pos = strcspn(cptr + offset, " \n");
			part = result.mid(offset, pos);
			offset += pos;
			item.setType(part);

			offset += strspn(cptr + offset, " \t");

			// Name
			pos =  strcspn(cptr + offset, " \n");
			part = result.mid(offset, pos);
			offset += pos;
			item.setName(part);

			list.append(item);

			offset += strspn(cptr + offset, " \t\n");
		}
	}

	return list;
}

QString
QLcrashDoc::getLcrashHelp()
{
	QString str;
	bool ret = oChildManager->command("help", str);

	return ret ? str : QString::null;
}
