///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoClient.cc
// -------------
// Cego client program
//
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: main
//
// Description: The cego SQL console program
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

// LFC INCLUDES
#include <lfcbase/Chain.h>
#include <lfcbase/Exception.h>
#include <lfcbase/GetLongOpt.h>
#include <lfcbase/File.h>
#include <lfcbase/Timer.h>
#include <lfcbase/Tokenizer.h>
#include <lfcbase/Net.h>
#include <lfcbase/SigHandler.h>
#include <lfcbase/Tokenizer.h>
#include <lfcbase/Pager.h>
#include <lfcxml/Element.h>
#include <lfcxml/Document.h>
#include <lfcxml/XMLSuite.h>

// CEGO INCLUDES
#include "CegoOutput.h"
#include "CegoModule.h"
#include "CegoNet.h"
#include "CegoXMLdef.h"
#include "CegoQueryHelper.h"

// #include "CegoDefs.h"

#define DEFAULTPORT 2200
#define DEFAULTSERVER "localhost"
#define DEFAULTPROTOCOL "serial"
#define DEFAULTMAXLINE 10000
#define DEFAULTMAXRESULT 100000
#define DEFAULTPACKSIZE 500

#define USAGE "Usage: cgclt --user=<user>/<password>\n\
          [ --server=<host> ]\n\
          [ --port=<port> ]\n\
          [ --protocol={xml|serial|fastserial} ]\n\
	  [ --tableset=<tableset> ]\n\
          [ --batchfile=<batchfile> ]\n\
          [ --dumpfile=<dumpfile> ]\n\
          [ --structure ]\n\
          [ --logfile=<logfile> ]\n\
          [ --raw=<sep> ]\n\
          [ --nohead ]\n\
          [ --pager ]\n\
          [ --maxline=<maxline> ]\n\
          [ --maxresult=<maxresult> ]\n\
          [ --pack=<packsize> ]\n\
          [ --ignore ]\n\
          [ --profile=<profile id> ]\n\
          [ --debug ] [ --version  ] [ --help ]"

#define DEFAULTRAWSEP ","
#define HISTFILE ".cgclt_history"
#define PGHISTFILE ".cgclt_pghist"
#define PROMPT "CGCLT > "
#define PROMPT2 "> "
#define CGPROFILE ".cgprofile"
#define LANGUAGE_ENV "LANG"
#define LOCALE_CATEGORY LC_ALL
#define PIPE_TIMEOUT 30 /* checking input pipe data timeout in ms */
#define HOMEVAR "HOME"

enum RunMode { INTERACTIVE, PAGER, BATCH, DUMP };

extern char __lfcVersionString[];
extern char __lfcxmlVersionString[];
extern Chain __dateTimeFormat;
extern unsigned __dateLength;
extern char __currencySymbol;
extern char __decimalPoint;

class AbortHandler : public SigHandler
{
  
public:

    AbortHandler(const Chain& serverName, 
		 unsigned portNo,
		 const Chain& tableSet,
		 const Chain& user, 
		 const Chain& password,		 
		 unsigned long long tid, 
		 CegoModule *pModule,
		 bool isQuiet);
    ~AbortHandler();
    void sigCatch(int sig);
    void setup();
    void setQueryAbort(bool doAbort);
    
private:

    unsigned long long _tid;
    bool _doAbort;
    CegoModule *_pModule;
    unsigned _portNo;
    Chain _serverName;
    Chain _user;
    Chain _password;
    Chain _tableSet;
    bool _isQuiet;
};

class ProfileEntry
{
  
public:

    ProfileEntry();
    ProfileEntry(const Chain& pid);
    ProfileEntry(const Chain& pid,
		 const Chain& serverName,
		 unsigned portNo,
		 const Chain& protocol,
		 const Chain& tableSet,
		 const Chain& auth, 
		 const Chain& prompt);
    ~ProfileEntry();

    const Chain& getId() const;
    const Chain& getServerName() const;
    unsigned getPortNo() const;
    const Chain& getProtocol() const;
    const Chain& getTableSet() const;
    const Chain& getAuth() const;
    const Chain& getPrompt() const;
        
    ProfileEntry& operator = ( const ProfileEntry& ad);
    bool operator == ( const ProfileEntry& ad);
    
private:

    Chain _pid;
    unsigned _portNo;
    Chain _serverName;
    Chain _protocol;
    Chain _tableSet;
    Chain _auth;
    Chain _prompt;
};


class CegoPager : public Pager
{
    
public:
    
    CegoPager(const Chain& title, CegoNet* pNet, unsigned long long maxResult) : Pager(title) {
	_pNet = pNet;
	_maxResult = maxResult;
    }
    
    ~CegoPager() {
    }

    void setAbortHandler(AbortHandler *pAH) {
	_pAH = pAH;
    }
    
    bool execute(const Chain& cmd, Chain& msg) {
	
	_pagerSchema.Empty();
	_outData.Empty();
	_hasData=false;
	
	if ( _pAH )
	    _pAH->setQueryAbort(true);

	try
	{
	    // cout << "Query= <<<" << cmd << ">>>" << endl;
	    // doQuery return number of affected, which we do not use here
	    _pNet->doQuery(cmd);
	}
	catch ( Exception e )
	{
	    e.pop(msg);
	    
	    if ( _pAH )
		_pAH->setQueryAbort(false);
	    
	    return false;
	}

	if ( _pNet->isFetchable() )
	{
	    try
	    {					
		ListT<CegoField> schema;
		_pNet->getSchema(schema);
		
		ListT<CegoFieldValue> fvl;
		
		unsigned long long count=0;
						
		while ( _pNet->fetchData(schema, fvl)  )
		{
		    count++;
		    
		    if ( count > _maxResult )
		    {
			_pNet->abortQuery();
			msg=Chain("Result is too large");
			return false;
		    }
		    
		    
		    CegoFieldValue *pFV = fvl.First();
		    ListT<Chain> outRow;
		    while ( pFV )
		    {
			outRow.Insert(pFV->valAsChain());
			pFV = fvl.Next();		    
		    }
		    _outData.Insert(outRow);
		}
		fvl.Empty();	    
				
		CegoField *pF = schema.First();
		while ( pF )
		{
		    Chain attrDesc = pF->getAttrName() + Chain(":auto"); //  + Chain(CegoQueryHelper::maxFieldSize(pF));
		    _pagerSchema.Insert(attrDesc);		
		    pF = schema.Next();		
		}

		_hasData=true;
		msg=Chain(count) + Chain(" tuples");
	    }
	    catch ( Exception e )
	    {
		_pNet->abortQuery();
		e.pop(msg);
	    }
	}
	else
	{	      
	    msg = _pNet->getMsg();
	}
	return true;
    }

    bool hasData() {
	return _hasData;
    }
    
    ListT<Chain> getSchema() {

	return _pagerSchema;
    }
    
    ListT< ListT<Chain> > getData() {

	return _outData;
	
    }

private:

    CegoNet *_pNet;
    AbortHandler *_pAH;

    unsigned long long _maxResult;

    bool _hasData;
    ListT<Chain> _pagerSchema;
    ListT< ListT<Chain> > _outData;
            
};


int execute();
void treatEscape(Chain& cmd);
bool dispatchCmd(CegoNet* pCegoNet, AbortHandler* pAH, Chain cmd, bool rawMode, bool noHead, int& exitCode, Chain& msg);
void loadProfile(const Chain& rcFileName, ListT<ProfileEntry>& pfeList);
void dumpProlog(File *pDF);
void dumpTableSet(CegoNet *pCegoNet, const Chain& dumpFileName, bool isStructure);
void dumpCounter(CegoNet *pCegoNet, File *pDF);
void dumpTable(CegoNet *pCegoNet, File *pDF, bool isStructure);
void dumpAVLtree(CegoNet *pCegoNet, File *pDF);
void dumpBtree(CegoNet *pCegoNet, File *pDF);
void dumpKey(CegoNet *pCegoNet, File *pDF);
void dumpCheck(CegoNet *pCegoNet, File *pDF);
void dumpView(CegoNet *pCegoNet, File *pDF);
void dumpProcedure(CegoNet *pCegoNet, File *pDF);
void dumpAlias(CegoNet *pCegoNet, File *pDF);

Chain user;
Chain password;
Chain serverName;
Chain tableSet;
unsigned portNo;
Chain authString;
Chain promptString;
Chain protocol;

unsigned long long maxLine;
unsigned long long maxResult;
unsigned long long packSize;
bool rawMode = false;
bool noHead = false;
Chain rawSep;
bool ignoreError = false;
bool isStructure = false;
Chain batchFileName;
Chain dumpFileName;
Chain command;
Chain logFile;    
RunMode runMode = INTERACTIVE;
bool debug=false;
CegoModule *pModule = 0;
unsigned long modId;
CegoDbHandler::ProtocolType protType;
Chain histPath;
Chain pgHistPath;
File *pBatchInput = 0;

int main(int argc, char **argv)
{    
    GetLongOpt longOpt(argc, argv);
    
    longOpt.addOpt("version");
    longOpt.addOpt("help");
    longOpt.addOpt("logfile");
    longOpt.addOpt("tableset");
    longOpt.addOpt("user");
    longOpt.addOpt("batchfile");
    longOpt.addOpt("dumpfile");
    longOpt.addOpt("structure");
    longOpt.addOpt("maxline", Chain(DEFAULTMAXLINE));
    longOpt.addOpt("maxresult", Chain(DEFAULTMAXRESULT));
    longOpt.addOpt("pack", Chain(DEFAULTPACKSIZE));
    longOpt.addOpt("debug");
    longOpt.addOpt("raw", Chain(DEFAULTRAWSEP));
    longOpt.addOpt("nohead");
    longOpt.addOpt("pager");
    longOpt.addOpt("ignore");
    longOpt.addOpt("server", DEFAULTSERVER);
    longOpt.addOpt("protocol", DEFAULTPROTOCOL);
    longOpt.addOpt("port", Chain(DEFAULTPORT));
    longOpt.addOpt("profile");
    
    try
    {
	longOpt.parseOpt(); 
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	cerr << USAGE << endl;
	exit(1);	
    }

    Chain homeDir(getenv(HOMEVAR));
    
    histPath = homeDir + "/" + Chain(HISTFILE);
    pgHistPath = homeDir + "/" + Chain(PGHISTFILE);

    if ( longOpt.isSet( Chain("version") ) )
    {
	cout << CEGO_PRODUCT << " SQL Client (" << sizeof(void*) * 8 << " bit), Version " << CEGO_VERSION 
	     << " [ lfc: " << __lfcVersionString  
	     << ", lfcxml: " <<  __lfcxmlVersionString << " ]" << endl;
	cout << CEGO_COPYRIGHT << endl;
	exit(0);
    }
    
    if ( longOpt.isSet("debug") )
	debug=true;
    
    if ( longOpt.isSet("raw") )
    {
	rawMode=true;
	rawSep = longOpt.getOptValue("raw");
    }

    if ( longOpt.isSet("nohead") )
	noHead=true;
    
    if ( longOpt.isSet("ignore") )
	ignoreError=true;

    if ( longOpt.isSet("structure") )
	isStructure=true;

    if ( longOpt.isSet( Chain("help") ) )
    {	
	cerr << USAGE << endl;
	exit(0);
    }


    ListT<ProfileEntry> pfeList;
    
    try
    {
	loadProfile(homeDir + "/" + Chain(CGPROFILE), pfeList);
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exit(1);
    }

    logFile = longOpt.getOptValue("logfile");
    
    if ( longOpt.isSet("batchfile") )
    {
	runMode = BATCH;
	batchFileName = longOpt.getOptValue("batchfile");

	pBatchInput = new File(batchFileName);
	if ( pBatchInput->exists() == false )
	{
	    delete pBatchInput;
	    pBatchInput=0;
	    Chain msg = Chain("File ") + batchFileName + Chain(" does not exist");		    
	    throw Exception(EXLOC, msg);
	}
	pBatchInput->open(File::READ);

    }
    else if ( longOpt.isSet("dumpfile") )
    {
	runMode = DUMP;
	dumpFileName = longOpt.getOptValue("dumpfile");
    }
    else if ( longOpt.isSet("pager") )
    {
	runMode = PAGER;
    }	
    else
    {
	// check for data from stdin, if available, set batch mode
	
	pBatchInput = new File(File::IN);
	if ( pBatchInput->hasData(PIPE_TIMEOUT) )
	{
	    runMode = BATCH;	
	}
	else
	{
	    delete pBatchInput;
	    pBatchInput = 0;
	}
    }


    if ( longOpt.isSet("profile") )
    {
	Chain profile;
        profile = longOpt.getOptValue("profile");
	
	ProfileEntry *pE;
	if ( ( pE = pfeList.Find( ProfileEntry(profile))) != 0 )
	{
	    serverName = pE->getServerName();
	    tableSet = pE->getTableSet();
	    portNo = pE->getPortNo();
	    authString = pE->getAuth();
	    promptString = pE->getPrompt();	 
	    protocol = pE->getProtocol();
	}
	else
	{
	    cerr << "Profile entry " << profile << " not found" << endl;
	    exit(1);
	}
    }
    else
    {	
	serverName = longOpt.getOptValue("server");
	tableSet = longOpt.getOptValue("tableset");
	portNo = longOpt.getOptValue("port").asUnsigned();    
	authString = longOpt.getOptValue("user");
	promptString = Chain(PROMPT);	
	protocol = longOpt.getOptValue("protocol");
    }

    Tokenizer authTok(authString, Chain("/")); 
    authTok.nextToken(user);
    authTok.nextToken(password);

    if ( user.length() == 0 )
    {
	cerr << "User not set" << endl;
	exit (1);
    }
    if ( password.length() == 0 )
    {
	cerr << "Password not set" << endl;
	exit (1);
    }
    if ( tableSet.length() == 0 )
    {
	cerr << "Tableset not set" << endl;
	exit (1);
    }

    if ( protocol == Chain("serial") )
    {
	protType = CegoDbHandler::SERIAL;
    }
    else if ( protocol == Chain("fastserial") )
    {
	protType = CegoDbHandler::FASTSERIAL;
    }
    else if ( protocol == Chain("xml") )
    {
	protType = CegoDbHandler::XML;
    }
    else
    {
	cerr << "Invalid protocol " << protocol << endl;
	exit (1);
    }

    // for localization
    char* lang = 0;
    if ( ( lang = getenv(LANGUAGE_ENV) ) != 0)
    {
	if ( setlocale(LOCALE_CATEGORY, lang) == 0)
	{
	    Chain msg = Chain("Cannot set locale ") + Chain(lang);
	    cerr << msg << endl;
	    exit(1);
	}
    }

    const struct lconv * const currentlocale = localeconv();

    __currencySymbol = *currentlocale->currency_symbol;
    __decimalPoint = *currentlocale->decimal_point;        
    
    maxLine = longOpt.getOptValue("maxline").asUnsignedLongLong();
    maxResult = longOpt.getOptValue("maxresult").asUnsignedLongLong();

    if ( rawMode )
	packSize=0;
    else
	packSize = longOpt.getOptValue("pack").asUnsignedLongLong();
    
    if ( logFile.length() > 0 )
	pModule = new CegoModule(logFile, Chain());
    else
	pModule = new CegoModule;
    
    modId = pModule->getModId("CegoClient");
    
    if ( debug )
    {
	pModule->logModule(modId, "CegoClient", Logger::DEBUG);
	unsigned long id = pModule->getModId("CegoDbHandler");
	pModule->logModule(id, "CegoDbHandler", Logger::DEBUG);
    }
    else
    {
	pModule->logModule(modId, "CegoClient", Logger::NOTICE);
	unsigned long id = pModule->getModId("CegoDbHandler");
	pModule->logModule(id, "CegoDbHandler", Logger::NOTICE);
    }

    int exitCode = 0;
    try
    {
	exitCode = execute();
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    
    delete pModule;
    exit(exitCode);
}

int execute()
{
    int exitCode = 0;

    Chain logMode("notice");
    if ( debug )
	logMode=Chain("debug");
    
    switch ( runMode )
    {	
    case BATCH:
    {
	unsigned lineNo=0;

	CegoNet *pCegoNet = new CegoNet( protType, logFile, Chain(), logMode );	
	
	try
	{	    
	    if ( pBatchInput == 0 )
	    {
		Chain msg = Chain("No data available in batch mode ");
		throw Exception(EXLOC, msg);
	    }

	    pCegoNet->connect(serverName, portNo, tableSet, user, password);

	    Datetime d(PROOFDATE_VALUE, PROOFDATE_FORMAT);
	    __dateLength = d.asChain(__dateTimeFormat).length();

	    
	    Chain cmd;
	    Chain line;
	    bool disableDelimiter=false;
	    bool goOn = true;
	    
	    while (goOn && pBatchInput->readLine(line, maxLine))
	    {
		lineNo++;
		if ( line.truncRight(" \t") == Chain("@") )
		{
		    if ( disableDelimiter == false )
			disableDelimiter=true;
		    else
			disableDelimiter=false;
		}
		else
		{
		    cmd = cmd + Chain(" ") + line;			
		}

		if ( cmd.length() > 0 )
		{
		    if ( CegoQueryHelper::hasOpenString(cmd) == false )
		    {
			cmd = CegoQueryHelper::skipComment(cmd);

			if ( cmd != Chain() )
			{
			    if (cmd.subChain(cmd.length()-1, cmd.length()) == Chain(";") 
				&& disableDelimiter==false)
			    {		
				// send command to server here				
				// replace escape characters
				
				treatEscape(cmd);
				
				Timer t(6,3);
				t.start();
				
				Chain msg;
				goOn = dispatchCmd(pCegoNet, 0, cmd, rawMode, noHead, exitCode, msg);
				
				t.stop();
				
				if ( exitCode != 0 )
				{
				    cerr << "Line Number " + Chain(lineNo) + " : " << msg << endl;
				}
				else
				{
				    if ( rawMode == false )
				    {
					cout << msg << endl;				
					cout << "ok ( " << t << " s )" << endl;
				    }
				}
			
				if ( exitCode != 0 && ignoreError && pCegoNet->isValid() )
				    goOn = true;
			
				cmd = Chain();
			    }
			}
		    }
		    else
		    {
			// collect further data
		    }
		}
	    }
	
	    cmd = cmd.cutTrailing(" \t");
	    if ( cmd.length() > 1 )
	    {
		cout << "Incomplete command ..<" << cmd << ">" << endl;
	    }
	    
	    pBatchInput->close();
	    delete pBatchInput;
	    pBatchInput=0;
	    
	    pCegoNet->disconnect();

	    delete pCegoNet;
	}
	catch ( Exception e )
	{
	    Chain msg;
	    e.pop(msg);

	    if ( pBatchInput )
		delete pBatchInput;
	    
	    cerr << msg << endl;
	    delete pCegoNet;
	    return 1;
	}	    	
	break;
    }
    case DUMP:
    {
	CegoNet *pCegoNet = new CegoNet( protType, logFile, Chain(), logMode );	
	
	try
	{
	    pCegoNet->connect(serverName, portNo, tableSet, user, password);
	}
	catch ( Exception e )
	{
	    cerr << "Cannot connect to database" << endl;
	    delete pCegoNet;
	    return 1;
	}

	dumpTableSet(pCegoNet, dumpFileName, isStructure);

	pCegoNet->disconnect();
	
	delete pCegoNet;
	
	exitCode = 0;
	break;
    }
    case INTERACTIVE:
    {
	CegoNet *pCegoNet = new CegoNet( protType, logFile, Chain(), logMode );	

	try
	{	    
	    pCegoNet->connect(serverName, portNo, tableSet, user, password);

	    
	    unsigned long long tid = pCegoNet->getTid();
	    
	    AbortHandler *pAH = new AbortHandler(serverName, portNo, tableSet, user, password, tid, pModule, false);

	    // explicit init, since there is no other use of sighandler
	    pAH->init();
	    pAH->setup(); 
	    	 
#ifdef DEBUG	
	    pModule->log(modId, Logger::DEBUG, Chain("Got session ..."));
#endif

	    Chain dbName = pCegoNet->getDbName();
	    Chain dbVersion = pCegoNet->getDbVersion();

	    Datetime d(PROOFDATE_VALUE, PROOFDATE_FORMAT);
	    __dateLength = d.asChain(__dateTimeFormat).length();

	    cout << "Connected to " << dbName << " ( Version " << dbVersion << " )" << endl;

	    read_history((char*)histPath);
 
	    bool goOn = true;
	    while ( goOn )
	    {	
		char *pC = 0;
		
		Chain cs;

		pC = readline(promptString);
		
		if (pC)
		{
		    cs = Chain(pC);

		    bool stmtComplete = false;
		    while (! stmtComplete )
		    {		
			add_history(pC);	
			
			if (pC)
			    free(pC);	    
			
			cs = cs.cutTrailing(Chain(" \t\r\n"));
			
			if ( cs.length() > 1)
			{		
			    if ( cs[cs.length()-2] == ';')
			    {
				// cut multiple semicolon
				cs = cs.cutTrailing(Chain(";"));
				cs = cs + Chain(";");				
				stmtComplete = true;
			    }
			}
			
			if ( ! stmtComplete )
			{
			    if ( cs.length() > 1 )
			    {
				pC = readline(PROMPT2);
			    }
			    else
			    {
				pC = readline(promptString);
			    }
			    if (pC)
				cs = cs + Chain(" ") + Chain(pC);
			    else
			    {
				stmtComplete=true;
				goOn=false;
				cout << "Bye" << endl;				
			    }
			}
		    }

		    if ( goOn )
		    {
			treatEscape(cs);
		    
			Timer t(6,3);
			t.start();
			
			Chain msg;
			goOn = dispatchCmd(pCegoNet, pAH, cs, rawMode, noHead, exitCode, msg);
			
			t.stop();
			
			if ( exitCode != 0 )
			{
			    cerr << "Error : " <<  msg << endl;
			    cerr << "Query failed" << endl;

			    if ( pCegoNet->isValid() )
				goOn = true;
			    else
				goOn = false;
			}
			else
			{
			    if ( rawMode == false )
			    {			   
				cout << msg << endl;
				cout << "ok ( " << t << " s )" << endl;
			    }
			}
			write_history((char*)histPath);
		    }
		}
		else
		{
		    cout << "Bye" << endl;
		    goOn=false;
		}
	    }
	    
	    pCegoNet->disconnect();
	    delete pCegoNet;
	}
	catch ( Exception e)
	{
	    delete pCegoNet;
	    Chain msg;
	    e.pop(msg);
	    cerr << msg << endl;
	    return 1;
	}
	break;
    }
    case PAGER:
    {
	CegoNet *pCegoNet = new CegoNet( protType, logFile, Chain(), logMode );	
	
	try
	{	    
	    pCegoNet->connect(serverName, portNo, tableSet, user, password);
	    
	    unsigned long long tid = pCegoNet->getTid();


	    CegoPager pager(Chain("Pager"), pCegoNet, maxResult);

	    pager.readHistory(pgHistPath);
	    
	    // The pager class disables Cntrl-C in constructor,
	    // so we have to instantiate and set abort handler with explicit method
	    AbortHandler *pAH = new AbortHandler(serverName, portNo, tableSet, user, password, tid, pModule, true);

	    pAH->setup();
	   
	    // no signal init of abort hander, since this is alread done by pager
	    // another SigHandler::init would reset this signal configuration

	    pager.setAbortHandler(pAH);
	    
#ifdef DEBUG	
	    pModule->log(modId, Logger::DEBUG, Chain("Got session ..."));
#endif

	    Chain dbName = pCegoNet->getDbName();
	    Chain dbVersion = pCegoNet->getDbVersion();

	    // cout << "Connected to " << dbName << " ( Version " << dbVersion << " )" << endl;
	    
	    pager.manage();

	    pager.writeHistory(pgHistPath);	    

	    delete pCegoNet;
	    delete pAH;
	}
	catch ( Exception e)
	{
	    delete pCegoNet;
	    Chain msg;
	    e.pop(msg);
	    cerr << msg << endl;
	    return 1;
	}    
    }
    }
    return exitCode;
}

void treatEscape(Chain& cmd)
{
    Chain bval;
    if ( cmd.replaceAll(Chain("\\n"), Chain("\n"), bval) > 0 )
	cmd = bval;
    
    if ( cmd.replaceAll(Chain("\\r"), Chain("\r"), bval) > 0 )
	cmd = bval;
    
    if ( cmd.replaceAll(Chain("\\t"), Chain("\t"), bval) > 0 )
	cmd = bval;
}

bool dispatchCmd(CegoNet *pCegoNet, AbortHandler *pAH, Chain cmd, bool rawMode, bool noHead, int& exitCode, Chain& msg)
{
    exitCode = 0;

    if ( cmd.cutTrailing(" ;") == Chain("quit") )
    {
	msg = Chain("Goodbye");
	return false;
    }
        
    if ( pAH )
	pAH->setQueryAbort(true);

    try
    {
	// cout << "Query= <<<" << cmd << ">>>" << endl;
	// doQuery return number of affected, which we do not use here
	pCegoNet->doQuery(cmd);
    }
    catch ( Exception e )
    {
	e.pop(msg);
	
	if ( pAH )
	    pAH->setQueryAbort(false);
	
	exitCode = 1;
	return false;
    }
    
    if ( pCegoNet->isFetchable() )
    {		
	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);

	Chain format;
	pCegoNet->getFormat(format);

	if ( packSize == 0 )
	{
	    CegoOutput output(schema, format);
	    
	    output.setRawMode(rawMode);
	    if ( rawMode )
		output.setRawSeparator(rawSep);
	    
	    if ( noHead == true )
	    {
		if ( rawMode == false )
		{
		    output.tailOut();
		}
	    }
	    else
	    {
		output.headOut();
	    }
	    
	    ListT<CegoFieldValue> fvl;
	    
	    unsigned long long count=0;
	    
	    try
	    {
		while ( pCegoNet->fetchData(schema, fvl) )
		{
		    count++;
		    
		    if ( count > maxResult )
		    {
			pCegoNet->abortQuery();
			exitCode = 1;
			msg=Chain("Result is too large");
			return false;
		    }
		    
		    output.rowOut(fvl);
		    		
		    fvl.Empty();	    
		}
		
		output.tailOut();
		
		msg=Chain(count) + Chain(" tuples");
	    }
	    catch ( Exception e )
	    {
		pCegoNet->abortQuery();
		e.pop(msg);
		exitCode=1;
	    }
	}
	else
	{
	   	    
	    try
	    {

		unsigned long long count=0;
		
		bool hasData=true; 
		while ( hasData )
		{
		    // collect data
		    unsigned long long i=0;
		    ListT<CegoFieldValue > pfvl;		    
		    ListT<CegoFieldValue> fvl;

		    while ( hasData && i < packSize )
		    {
			hasData = pCegoNet->fetchData(schema, fvl);
			if ( hasData )
			{
			    count++;
			    CegoFieldValue *pFV = fvl.First();
			    while ( pFV )
			    {
				pfvl.Insert(pFV->getLocalCopy());
				pFV = fvl.Next();
			    }
			    i++;
			}
		    }

		    if ( format != Chain("m") )
		    {
			// pack schema
			bool firstRow=true;
			CegoFieldValue *pFV = pfvl.First();
			while ( pFV )
			{
			    CegoField *pF = schema.First();
			    while ( pF && pFV )
			    {			       
				if ( firstRow || pF->getLength() < pFV->getLength() )
				{
				    pF->setLength(pFV->getLength());
				}
				pF = schema.Next();
				pFV = pfvl.Next();
			    }
			    firstRow=false;
			}
		    }
		    
		    CegoOutput output(schema, format);

		    bool headOut = true;
		    CegoFieldValue *pFV = pfvl.First();
		    while ( pFV )
		    {
			ListT<CegoFieldValue> fvl;			
			CegoField *pF = schema.First();
			while ( pF && pFV )
			{
			    fvl.Insert(*pFV);
			    pF = schema.Next();
			    pFV = pfvl.Next();
			}
			    
			if ( headOut )
			{
			    output.headOut();
			    headOut = false;
			}
			output.rowOut(fvl);
		    }		    

		    if ( hasData == false )
			output.tailOut();
		}			       
		msg=Chain(count) + Chain(" tuples");
	    }
	    catch ( Exception e )
	    {
		pCegoNet->abortQuery();
		e.pop(msg);
		exitCode=1;
	    }	    	  
	}
    }
    else
    {	      
	msg = pCegoNet->getMsg();
    }
    return true;
}
    
void dumpTableSet(CegoNet *pCegoNet, const Chain& dumpFileName, bool isStructure)
{
    File* pDF = new File(dumpFileName);
    pDF->open(File::WRITE);

    try
    {
	cout << "Dumping prolog ..." << endl;
	dumpProlog(pDF);

	cout << "Dumping counters ..." << endl;
	dumpCounter(pCegoNet, pDF);

	cout << "Dumping tables ..." << endl;
	dumpTable(pCegoNet, pDF, isStructure);
	
	cout << "Dumping avl trees ..." << endl;
	dumpAVLtree(pCegoNet, pDF);

	cout << "Dumping btrees ..." << endl;
	dumpBtree(pCegoNet, pDF);
       
	cout << "Dumping aliases ..." << endl;
	dumpAlias(pCegoNet, pDF);

	cout << "Dumping foreign keys ..." << endl;
	dumpKey(pCegoNet, pDF);

	cout << "Dumping checks ..." << endl;
	dumpCheck(pCegoNet, pDF);

	cout << "Dumping procedures ..." << endl;
	dumpProcedure(pCegoNet, pDF);

	cout << "Dumping views ..." << endl;
	dumpView(pCegoNet, pDF);
    }
    catch ( Exception e)
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
    }

    pDF->close();
    delete pDF;
}

void dumpProlog(File *pDF)
{
    pDF->writeChain(Chain("set append on;\n"));
    pDF->writeChain(Chain("set grace on;\n"));
}

void dumpCounter(CegoNet *pCegoNet, File *pDF)
{
    pCegoNet->doQuery(Chain("list counter;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain counterName = pFV->valAsChain();
	pFV = fvl.Next();
	Chain counterValue = pFV->valAsChain();

	Chain cmd;

	cmd=Chain("drop if exists counter ") + counterName + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("create counter ") + counterName + Chain(";\n");
	pDF->writeChain(cmd);
	cmd=Chain("set counter ") + counterName + Chain(" to ") + counterValue + Chain(";\n");
	pDF->writeChain(cmd);
	fvl.Empty();	
    }
}

void dumpTable(CegoNet *pCegoNet, File *pDF, bool isStructure)
{	

    pCegoNet->doQuery(Chain("list table;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> tableList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain tableName = pFV->valAsChain();
	tableList.Insert(tableName);
	fvl.Empty();	
    }
    
    Chain *pTable = tableList.First();
    while ( pTable ) 
    {
	pCegoNet->doQuery(Chain("desc table ") + *pTable + Chain(";"));

	Chain cmd;

	cmd=Chain("drop if exists table ") + *pTable + Chain(";\n");
	pDF->writeChain(cmd);
	
	cmd=Chain("create table ") + *pTable + Chain("(");

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	bool isFirst=true;
	Chain selectAttr;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    if ( isFirst == false )
	    {
		cmd += Chain(", ");
		selectAttr += Chain(", ");
	    }
	    else
	    {
		isFirst=false;
	    }
	    CegoFieldValue *pFV = fvl.First();
	    Chain attrName = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrType = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrSize = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrDim = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrDefault = pFV->valAsChain();	   
	    pFV = fvl.Next();
	    Chain attrNull = pFV->valAsChain();

	    if ( attrType == Chain(XML_CLOB_VALUE) )
		selectAttr += Chain("clob2str(") + attrName + Chain(")");
	    else
		selectAttr += attrName;
	    
	    cmd += attrName + Chain(" ") + attrType;
	    if ( attrType == Chain(XML_STRING_VALUE)
		 || attrType == Chain(XML_BIGINT_VALUE) )
		cmd +=  Chain("(") + attrSize + Chain(")");
	    else if ( attrType == Chain(XML_DECIMAL_VALUE) )
		cmd +=  Chain("(") + attrSize + Chain(",") + attrDim + Chain(")");
		 
	    if ( attrDefault != Chain(XML_NULL_VALUE))
	    {
		if ( attrType == Chain(XML_STRING_VALUE)
		     || ( attrType == Chain(XML_DATETIME_VALUE) && attrDefault != Chain("sysdate")) )
		    cmd += Chain(" default '") + attrDefault + Chain("'");
		else
		    cmd += Chain(" default ") + attrDefault;
	    }
	    if ( attrNull == Chain("no"))
		cmd += Chain(" not null");
	    fvl.Empty();	
	}

	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	if ( isStructure == false )
	{
	    // dump table content
	    pCegoNet->doQuery(Chain("select ") + selectAttr + Chain(" from ") + *pTable + Chain(";"));
	    
	    schema.Empty();
	    fvl.Empty();
	    
	    pCegoNet->getSchema(schema);
	    
	    while ( pCegoNet->fetchData(schema, fvl)  )
	    {		
		Chain cmd=Chain("insert into ") + *pTable + Chain(" values (");
		
		CegoFieldValue *pFV = fvl.First();
		while ( pFV )
		{
		    Chain val = pFV->toChain();
		    cmd += val;
		    pFV = fvl.Next();
		    
		    if ( pFV )
			cmd += Chain(", ");
		}
		cmd += Chain(");\n");
		pDF->writeChain(cmd);
		fvl.Empty();
	    }
	}

	pTable = tableList.Next();
    }
}

void dumpAVLtree(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list avltree;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> avlList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain avlName = pFV->valAsChain();
	avlList.Insert(avlName);
	fvl.Empty();	
    }
    
    Chain *pAVL = avlList.First();
    while ( pAVL ) 
    {
	pCegoNet->doQuery(Chain("desc avltree ") + *pAVL + Chain(";"));

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	Chain tableName;
	Chain indexType;
	ListT<Chain> attrList;
	
	ListT<CegoFieldValue> fvl;	
	while ( pCegoNet->fetchData(schema, fvl)  )
	{	    
	    CegoFieldValue *pFV = fvl.First();
	    Chain attrName = pFV->valAsChain();
	    attrList.Insert(attrName);
	    pFV = fvl.Next();
	    tableName = pFV->valAsChain();
	    pFV = fvl.Next();
	    indexType = pFV->valAsChain();

	    fvl.Empty();
	}

	Chain cmd;

	cmd=Chain("drop if exists avltree ") + *pAVL + Chain(";\n");
	pDF->writeChain(cmd);

	if ( indexType == Chain(XML_PINDEX_VALUE) )
	    cmd=Chain("create primary avltree ");
	else if ( indexType == Chain(XML_UINDEX_VALUE) )
	    cmd=Chain("create unique avltree ") + *pAVL; 
	else
	    cmd=Chain("create avltree ") + *pAVL;

	cmd += Chain(" on ") + tableName + Chain("(");
	
	Chain *pAttr = attrList.First();
	bool isFirst=true;
	while ( pAttr )
	{
	    if ( isFirst == false )
		cmd += Chain(",");
	    isFirst=false;

	    cmd += *pAttr;
	    pAttr = attrList.Next();
	}
	
	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	pAVL = avlList.Next();
    }
}

void dumpBtree(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list btree;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> indexList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain indexName = pFV->valAsChain();
	indexList.Insert(indexName);
	fvl.Empty();	
    }
    
    Chain *pIndex = indexList.First();
    while ( pIndex ) 
    {
	pCegoNet->doQuery(Chain("desc btree ") + *pIndex + Chain(";"));

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	Chain tableName;
	Chain indexType;
	ListT<Chain> attrList;
	
	ListT<CegoFieldValue> fvl;	
	while ( pCegoNet->fetchData(schema, fvl)  )
	{	    
	    CegoFieldValue *pFV = fvl.First();
	    Chain attrName = pFV->valAsChain();
	    attrList.Insert(attrName);
	    pFV = fvl.Next();
	    tableName = pFV->valAsChain();
	    pFV = fvl.Next();
	    indexType = pFV->valAsChain();

	    fvl.Empty();
	}

	Chain cmd;

	cmd=Chain("drop if exists btree ") + *pIndex + Chain(";\n");
	pDF->writeChain(cmd);

	if ( indexType == Chain(XML_PBTREE_VALUE) )
	    cmd=Chain("create primary btree ");
	else if ( indexType == Chain(XML_UBTREE_VALUE) )
	    cmd=Chain("create unique btree ") + *pIndex; 
	else
	    cmd=Chain("create btree ") + *pIndex;

	cmd += Chain(" on ") + tableName + Chain("(");
	
	Chain *pAttr = attrList.First();
	bool isFirst=true;
	while ( pAttr )
	{
	    if ( isFirst == false )
		cmd += Chain(",");
	    isFirst=false;

	    cmd += *pAttr;
	    pAttr = attrList.Next();
	}
	
	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	pIndex = indexList.Next();
    }
}

void dumpKey(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list key;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> keyList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain keyName = pFV->valAsChain();
	keyList.Insert(keyName);
	fvl.Empty();	
    }
    
    Chain *pKey = keyList.First();
    while ( pKey ) 
    {
	pCegoNet->doQuery(Chain("desc key ") + *pKey + Chain(";"));
	
	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);

	Chain keyTable;
	Chain refTable;
	ListT<Chain> keyAttrList;
	ListT<Chain> refAttrList;

	ListT<CegoFieldValue> fvl;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    CegoFieldValue *pFV = fvl.First();

	    Chain tableName = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrName = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain attrType = pFV->valAsChain();
	    
	    if ( attrType == Chain("key") )
	    {
		keyTable=tableName;
		keyAttrList.Insert(attrName);
	    }
	    else if ( attrType == Chain("reference") )
	    {
		refTable=tableName;
		refAttrList.Insert(attrName);		
	    }
	    fvl.Empty();	
	}

	Chain cmd;	

	cmd=Chain("drop if exists foreign key ") + *pKey + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("alter table ") + keyTable + Chain(" add foreign key ") + *pKey + Chain("(");
	Chain *pKeyAttr = keyAttrList.First();
	while ( pKeyAttr )
	{
	    cmd += *pKeyAttr;
	    pKeyAttr = keyAttrList.Next();
	    if ( pKeyAttr )
		cmd += Chain(",");
	}

	cmd+=Chain(") references ") + refTable + Chain("(");
	Chain *pRefAttr = refAttrList.First();
	while ( pRefAttr )
	{
	    cmd += *pRefAttr;
	    pRefAttr = refAttrList.Next();
	    if ( pRefAttr )
		cmd += Chain(",");
	}

	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	pKey = keyList.Next();
    }
}

void dumpCheck(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list check;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> checkList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain checkName = pFV->valAsChain();
	checkList.Insert(checkName);
	fvl.Empty();	
    }
    
    Chain *pCheck = checkList.First();
    while ( pCheck ) 
    {
	pCegoNet->doQuery(Chain("desc check ") + *pCheck + Chain(";"));

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	while ( pCegoNet->fetchData(schema, fvl) );

	CegoFieldValue *pFV = fvl.First();
	Chain checkTable = pFV->valAsChain();
	pFV = fvl.Next();
	Chain checkCondition = pFV->valAsChain();
	
	fvl.Empty();	
       
	Chain cmd;	

	cmd=Chain("drop if exists check ") + *pCheck + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("alter table ") +  checkTable + Chain(" add check ") + *pCheck + Chain(" on ") + checkCondition + Chain(";\n");
	pDF->writeChain(cmd);

	pCheck = checkList.Next();
    }
}

void dumpView(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list view;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> viewList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain viewName = pFV->valAsChain();
	viewList.Insert(viewName);
	fvl.Empty();	
    }
    
    Chain *pView = viewList.First();
    while ( pView ) 
    {
	pCegoNet->doQuery(Chain("show view ") + *pView + Chain(";"));

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	Chain viewText;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    CegoFieldValue *pFV = fvl.First();
	    viewText = pFV->valAsChain();
	    fvl.Empty();	
	}

	Chain cmd;	

	cmd=Chain("drop if exists view ") + *pView + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("create ") + viewText;
	cmd += Chain("\n");
	pDF->writeChain(cmd);

	pView = viewList.Next();
    }
}

void dumpProcedure(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list procedure;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> procList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain procName = pFV->valAsChain();
	procList.Insert(procName);
	fvl.Empty();	
    }
    
    Chain *pProc = procList.First();
    while ( pProc ) 
    {
	pCegoNet->doQuery(Chain("show procedure ") + *pProc + Chain(";"));

	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);
    
	ListT<CegoFieldValue> fvl;
	Chain procText;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    CegoFieldValue *pFV = fvl.First();
	    procText = pFV->valAsChain();
	    fvl.Empty();	
	}

	Chain cmd;

	cmd=Chain("drop if exists procedure ") + *pProc + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("@\ncreate ") + procText;
	cmd += Chain("\n@\n");
	pDF->writeChain(cmd);

	pProc = procList.Next();
    }
}

void dumpAlias(CegoNet *pCegoNet, File *pDF)
{	
    pCegoNet->doQuery(Chain("list alias;"));
        
    ListT<CegoField> schema;
    pCegoNet->getSchema(schema);
    
    ListT<Chain> aliasList;
    ListT<CegoFieldValue> fvl;
    while ( pCegoNet->fetchData(schema, fvl)  )
    {
	CegoFieldValue *pFV = fvl.First();
	Chain aliasName = pFV->valAsChain();
	aliasList.Insert(aliasName);
	fvl.Empty();	
    }
    
    Chain *pAlias = aliasList.First();
    while ( pAlias ) 
    {
	pCegoNet->doQuery(Chain("desc alias ") + *pAlias + Chain(";"));
	
	ListT<CegoField> schema;
	pCegoNet->getSchema(schema);

	Chain refTable;
	ListT<Chain> refAttrList;
	ListT<Chain> aliasAttrList;

	ListT<CegoFieldValue> fvl;
	while ( pCegoNet->fetchData(schema, fvl)  )
	{
	    CegoFieldValue *pFV = fvl.First();

	    refTable = pFV->valAsChain();

	    pFV = fvl.Next();
	    Chain refAttr = pFV->valAsChain();
	    pFV = fvl.Next();
	    Chain aliasAttr = pFV->valAsChain();

	    refAttrList.Insert(refAttr);
	    aliasAttrList.Insert(aliasAttr);
	    
	    fvl.Empty();	
	}

	Chain cmd;	

	cmd=Chain("drop if exists alias ") + *pAlias + Chain(";\n");
	pDF->writeChain(cmd);

	cmd=Chain("create alias ") + *pAlias + Chain(" on ") + refTable + Chain("(");
	Chain *pRefAttr = refAttrList.First();
	Chain *pAliasAttr = aliasAttrList.First();
	
	while ( pRefAttr && pAliasAttr )
	{
	    cmd += *pRefAttr + Chain(" as ") + *pAliasAttr;	    
	    pRefAttr = refAttrList.Next();
	    pAliasAttr = aliasAttrList.Next();
	    if ( pRefAttr && pAliasAttr )
		cmd += Chain(",");
	}
	           
	cmd += Chain(");\n");
	pDF->writeChain(cmd);

	pAlias = aliasList.Next();
    }
}

void loadProfile(const Chain& rcFileName, ListT<ProfileEntry>& pfeList)
{
    File rcFile(rcFileName);
    if ( rcFile.exists() )
    {	
	rcFile.open(File::READ);

	Chain rcLine;
	while ( rcFile.readLine(rcLine) )
	{
	    Tokenizer tok(rcLine, Chain(":"));

	    Chain pid;
	    Chain serverName;
	    Chain portNo;
	    Chain protocol;
	    Chain tableSet;
	    Chain prompt;
	    Chain user;
	    Chain pwd;
	    
	    if ( tok.nextToken(pid) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(serverName) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(portNo) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(protocol) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(tableSet) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(prompt) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(user) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }
	    if ( tok.nextToken(pwd) == false)
	    {
		throw Exception(EXLOC, Chain("Incomplete profile entry <") + rcLine + Chain(">"));
	    }

	    Chain auth = user + Chain("/") + pwd;
	    ProfileEntry pfe(pid, serverName, portNo.asUnsigned(), protocol, tableSet, auth, prompt);
	    pfeList.Insert(pfe);	    
	}		
    }
}

AbortHandler::AbortHandler(const Chain& serverName, 
			   unsigned portNo, 
			   const Chain& tableSet,
			   const Chain& user, 
			   const Chain& password,
			   unsigned long long tid, 
			   CegoModule *pModule,
			   bool isQuiet) :  SigHandler() 
{
    _tid = tid;
    _pModule = pModule;
    _portNo = portNo;
    _tableSet = tableSet;
    _user = user;
    _password = password;
    _serverName = serverName;
    _isQuiet = isQuiet;
}

AbortHandler::~AbortHandler()
{
} 

void AbortHandler::setup()
{
    install(SIGINT);
    install(SIGPIPE);
}

void AbortHandler::sigCatch(int sig) 
{    
    try
    {	
	setup();
		
	if ( _doAbort )
	{
	    if ( _isQuiet == false )
		cout << endl << "Aborting query .." << endl;

	    Chain logMode("notice");
	    if ( debug )
		logMode=Chain("debug");
	    
	    CegoNet *pCegoNet = new CegoNet( protType, logFile, Chain(), logMode );	
	    
	    try
	    {
		pCegoNet->connect(_serverName, _portNo, _tableSet, _user, _password);
	    }
	    catch ( Exception e )
	    {
		delete pCegoNet;
		throw Exception(EXLOC, "Cannot connect to database");
	    }
	    	        
	    pCegoNet->reqAbortQuery(_tid);	    	    	    
	    pCegoNet->disconnect();

	    delete pCegoNet;
	}
	else
	{
	    if ( _isQuiet == false )
		cout << "Use quit to quit" << endl;
	}
    }
    catch ( Exception e)
    {
	Chain msg;
	e.pop(msg);
	cout << "Error : " << msg << endl;
    }
}

void AbortHandler::setQueryAbort(bool doAbort)
{
    _doAbort = doAbort;
}

ProfileEntry::ProfileEntry()
{
}

ProfileEntry::ProfileEntry(const Chain& pid)
{
    _pid = pid;
}

ProfileEntry::ProfileEntry(const Chain& pid, 
			   const Chain& serverName, 
			   unsigned portNo, 
			   const Chain& protocol, 
			   const Chain& tableSet,
			   const Chain& auth, 
			   const Chain& prompt)
{
    _pid = pid;
    _serverName = serverName;
    _portNo = portNo;
    _protocol = protocol;
    _tableSet = tableSet;
    _auth = auth;
    _prompt = prompt;
}

ProfileEntry::~ProfileEntry()
{
} 

const Chain& ProfileEntry::getId() const
{
    return _pid;
}

const Chain& ProfileEntry::getServerName() const
{
    return _serverName;
}

unsigned ProfileEntry::getPortNo() const
{
    return _portNo;
}

const Chain& ProfileEntry::getProtocol() const
{
    return _protocol;
}

const Chain& ProfileEntry::getTableSet() const
{
    return _tableSet;
}

const Chain& ProfileEntry::getAuth() const
{
    return _auth;
}

const Chain& ProfileEntry::getPrompt() const
{
    return _prompt;
}

ProfileEntry& ProfileEntry::operator = ( const ProfileEntry& pe)
{
    _pid = pe._pid;
    _serverName = pe._serverName;
    _portNo = pe._portNo;
    _protocol = pe._protocol;
    _tableSet = pe._tableSet;
    _auth = pe._auth;
    _prompt = pe._prompt;
    return (*this);
}
    
bool ProfileEntry::operator == ( const ProfileEntry& pe)
{
    if ( _pid == pe._pid ) 
	return true;
    return false;
}
