//####COPYRIGHTBEGIN####
//                                                                          
// ----------------------------------------------------------------------------
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
// Copyright (C) 2003 John Dallaway
// Copyright (C) 2008 eCosCentric Limited
//
// This program is part of the eCos host tools.
//
// 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., 
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// ----------------------------------------------------------------------------
//                                                                          
//####COPYRIGHTEND####
//
//===========================================================================
//===========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): 	sdf, jld
// Contact(s):	sdf
// Date:		1998/08/11
// Version:		0.01
// Purpose:	
// Description:	This is a collection of utility functions.
//              Modified by julians for wxWindows (originally CTUtils.h)
// Requires:	
// Provides:	
// See also:    
// Known bugs:	
// Usage:	
//
//####DESCRIPTIONEND####
//
//===========================================================================

#ifdef __GNUG__
#pragma implementation "ecutils.h"
#endif

// Includes other headers for precompiled compilation
#include "ecpch.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#include "ecutils.h"
#include "wx/listctrl.h"
#include "wx/stream.h"

#include <float.h> // for DBL_DIG macro
#include <sys/types.h>
#include <sys/stat.h>

#ifndef __WXMSW__
#include <errno.h>
#endif

#ifdef __WXMSW__
#include <tlhelp32.h>
#endif

#ifdef __CYGWIN__
#include <sys/cygwin.h> /* for cygwin_conv_to_*_path() */
#endif

#if 0

#define INCLUDEFILE <string>
#include "IncludeSTL.h"

#endif

// Chop str into pieces, using cSep as separator.
// " and \ have usual semantics
// Return value is array of pieces found.

int ecUtils::Chop(const wxString& psz, wxArrayString &ar, wxChar cSep,
                  bool bObserveStrings/*=false*/,bool bBackslashQuotes/*=false*/)

{
    if(wxT(' ') == cSep)
    {
        return Chop(psz, ar, wxT("\t\n\v\f\r "), bObserveStrings, bBackslashQuotes);
    } else {
        wxASSERT(wxT('\0')!=cSep);
        wxChar c[2]={cSep,wxT('\0')};
        return Chop(psz,ar,wxString(c),bObserveStrings,bBackslashQuotes);
    }
}

int ecUtils::Chop(const wxString& str, wxArrayString &ar, const wxString& sep,
                  bool bObserveStrings/*=false*/,bool bBackslashQuotes/*=false*/)
{
    ar.Clear();
    
    const wxChar* pszSep = (const wxChar*) sep;
    const wxChar* psz = (const wxChar*) str;
    
    int i=0;
    for(;;){
        // Skip multiple separators
        while(*psz && wxStrchr(pszSep,*psz)){
            psz++;
        }
        if(!*psz){
            return i;
        }
        wxString strTok;
        if(bObserveStrings){
            bool bInString=FALSE;
            do{
                if(*psz == wxT('\\') && bBackslashQuotes && psz[1]){
                    strTok += psz[1];
                    psz++;
                } else if(*psz == wxT('"')){
                    bInString ^= 1;
                } else if (!bInString && *psz && NULL != wxStrchr(pszSep,*psz)) {
                    break;
                } else {
                    strTok+=*psz;
                }
            } while (*++psz);
        } else {
            const wxChar* pszStart=psz;
            do {
                psz++;
            } while (*psz && ! wxStrchr(pszSep,*psz));
            strTok=wxString(pszStart,psz-pszStart);
        }
        ar.Add(strTok);
        i++;
    }
    return ar.GetCount();
}

#if 0

// vararg-style message box formatter
int ecUtils::MessageBoxF (LPCTSTR pszFormat, ...)
{
  int rc;
  va_list args;
  va_start(args, pszFormat);
  rc=ecUtils::vMessageBox(MB_OK, pszFormat,args);
  va_end(args);
  return rc;
}

// As above, but with type as first parameter.
int ecUtils::MessageBoxFT (UINT nType, LPCTSTR pszFormat, ...)
{
  int rc;
  va_list args;
  va_start(args, pszFormat);
  rc=ecUtils::vMessageBox(nType, pszFormat,args);
  va_end(args);
  return rc;
}

int ecUtils::vMessageBox(UINT nType, LPCTSTR  pszFormat, va_list marker)
{
  int rc=0;
  for(int nLength=100;nLength;) {
    TCHAR *buf=new TCHAR[1+nLength];
    int n=_vsntprintf(buf, nLength, pszFormat, marker ); 
    if(-1==n){
      nLength*=2;  // NT behavior
    } else if (n<nLength){
      rc=AfxMessageBox(buf,nType);
      nLength=0;   // trigger exit from loop
    } else {
      nLength=n+1; // UNIX behavior generally, or NT behavior when buffer size exactly matches required length
    }
    delete [] buf;
  }
  return rc;
}

#endif

bool ecUtils::StrToItemIntegerType(const wxString & str, long &d)
{
	wxChar* pEnd;
	bool rc;
	errno=0;
	bool bHex=(str.Len() > 2 && str[0]==wxT('0') && (str[1]==wxT('x')||str[1]==wxT('X')));
	//d=_tcstol(str,&pEnd,bHex?16:10);
	d=wxStrtol(str,&pEnd,bHex?16:10);
	rc=(0==errno && (*pEnd==wxT('\0')));
	return rc;
}

const wxString ecUtils::IntToStr(long d,bool bHex)
{
  wxString s;
  s.Printf(bHex?wxT("0x%08x"):wxT("%d"),d);
  return s;
}

const wxString ecUtils::DoubleToStr (double dValue)
{
  wxString s;
  s.Printf(wxT("%.*e"), DBL_DIG, dValue);
  return s;
}

bool ecUtils::StrToDouble (const wxString & strValue, double &dValue)
{
	wxChar* pEnd;
	errno = 0;
	//dValue = _tcstod (strValue, &pEnd);
	dValue = wxStrtod(strValue, &pEnd);
	return (0 == errno) && (*pEnd == wxT('\0'));
}

#if 0
const wxString ecUtils::Explanation(CFileException & exc)
{
	wxString strMsg;
	switch(exc.m_cause){
		case CFileException::none: strMsg=wxT("No error occurred.");break;
		case CFileException::generic: strMsg=wxT("   An unspecified error occurred.");break;
		case CFileException::fileNotFound: strMsg=wxT("   The file could not be located.");break;
		case CFileException::badPath: strMsg=wxT("   All or part of the path is invalid.");break;
		case CFileException::tooManyOpenFiles: strMsg=wxT("   The permitted number of open files was exceeded.");break;
		case CFileException::accessDenied: strMsg=wxT("   The file could not be accessed.");break;
		case CFileException::invalidFile: strMsg=wxT("   There was an attempt to use an invalid file handle.");break;
		case CFileException::removeCurrentDir: strMsg=wxT("   The current working directory cannot be removed.");break;
		case CFileException::directoryFull: strMsg=wxT("   There are no more directory entries.");break;
		case CFileException::badSeek: strMsg=wxT("   There was an error trying to set the file pointer.");break;
		case CFileException::hardIO: strMsg=wxT("   There was a hardware error.");break;
		case CFileException::sharingViolation: strMsg=wxT("   SHARE.EXE was not loaded, or a shared region was locked.");break;
		case CFileException::lockViolation: strMsg=wxT("   There was an attempt to lock a region that was already locked.");break;
		case CFileException::diskFull: strMsg=wxT("   The disk is full.");break;
		case CFileException::endOfFile: strMsg=wxT("   The end of file was reached. ");break;
		default:
			strMsg=wxT(" Unknown cause");
			break;
	}
	return strMsg;
}


const wxString ecUtils::LoadString(UINT id)
{
	wxString str;
	str.LoadString(id);
	return str;
}
#endif

const wxString ecUtils::NativeToPosixPath(const wxString & native)
{
#ifdef __CYGWIN__
    if (native.IsEmpty())
        return native;
    else
    {
        wxString posix;
        cygwin_conv_to_posix_path(native.c_str(), posix.GetWriteBuf(PATH_MAX + 1));
        posix.UngetWriteBuf();
        return posix;
    }
#else
    return native;
#endif
}

const wxString ecUtils::PosixToNativePath(const wxString & posix)
{
#ifdef __CYGWIN__
    if (posix.IsEmpty())
        return posix;
    else
    {
        wxString native;
        cygwin_conv_to_win32_path(posix.c_str(), native.GetWriteBuf(PATH_MAX + 1));
        native.UngetWriteBuf();
        return native;
    }
#else
    return posix;
#endif
}

bool ecUtils::AddToPath(const ecFileName & strFolder, bool bAtFront)
{
    wxString strPath,strOldPath;
    
    if (wxGetEnv(wxT("PATH"), & strOldPath))
    {
        // Place the user tools folders at the head or tail of the new path
        if(bAtFront)
        {
            strPath.Printf(wxT("%s;%s"), (const wxChar*) strFolder.ShortName(), (const wxChar*) strOldPath);
        } else
        {
            strPath.Printf(wxT("%s;%s"), (const wxChar*) strOldPath, (const wxChar*) strFolder.ShortName());
        }
    } else
    {
        // unlikely, but ...
        strPath = strFolder;
    }
    return (TRUE == wxSetEnv(wxT("PATH"),strPath));
}

#if 0		
wxString ecUtils::GetLastErrorMessageString()
{
	wxString str;
	PTCHAR pszMsg;
	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR)&pszMsg,
		0,
		NULL 
	);

	// Display the string.
	str=pszMsg;
  str.TrimRight();
	// Free the buffer.
	LocalFree(pszMsg);
	return str;

}

bool ecUtils::Launch(const ecFileName &strFileName, const ecFileName &strViewer)
{
	bool rc=false;

	if(!strViewer.IsEmpty())//use custom editor
	{
		wxString strCmdline(strViewer);
		
		PTCHAR pszCmdLine=strCmdline.GetBuffer(strCmdline.GetLength());
		GetShortPathName(pszCmdLine,pszCmdLine,strCmdline.GetLength());
		strCmdline.ReleaseBuffer();

		strCmdline+=_TCHAR(' ');
		strCmdline+=strFileName;
		PROCESS_INFORMATION pi;
		STARTUPINFO si;

		si.cb = sizeof(STARTUPINFO); 
		si.lpReserved = NULL; 
		si.lpReserved2 = NULL; 
		si.cbReserved2 = 0; 
		si.lpDesktop = NULL; 
		si.dwFlags = 0; 
		si.lpTitle=NULL;

		if(CreateProcess(
			NULL, // app name
			//strCmdline.GetBuffer(strCmdline.GetLength()),    // command line
			strCmdline.GetBuffer(strCmdline.GetLength()),    // command line
			NULL, // process security
			NULL, // thread security
			TRUE, // inherit handles
			0,
			NULL, // environment
			NULL, // current dir
			&si, // startup info
			&pi)){
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
			rc=true;
		} else {
			MessageBoxF(wxT("Failed to invoke %s.\n"),strCmdline);
		}
		strCmdline.ReleaseBuffer();
	} else {// Use association
		TCHAR szExe[MAX_PATH];
		HINSTANCE h=FindExecutable(strFileName,wxT("."),szExe);
		if(int(h)<=32){
			wxString str;
			switch(int(h)){
				case 0:  str=wxT("The system is out of memory or resources.");break;
				case 31: str=wxT("There is no association for the specified file type.");break;
				case ERROR_FILE_NOT_FOUND: str=wxT("The specified file was not found.");break;
				case ERROR_PATH_NOT_FOUND: str=wxT("The specified path was not found.");break;
				case ERROR_BAD_FORMAT:     str=wxT("The .EXE file is invalid (non-Win32 .EXE or error in .EXE image).");break;
				default: break;
			}
			MessageBoxF(wxT("Failed to open document %s.\r\n%s"),strFileName,str);
		} else {

			SHELLEXECUTEINFO sei = {sizeof(sei), 0, AfxGetMainWnd()->GetSafeHwnd(), wxT("open"),
					strFileName, NULL, NULL, SW_SHOWNORMAL, AfxGetInstanceHandle( )};

			sei.hInstApp=0;
			HINSTANCE hInst=ShellExecute(AfxGetMainWnd()->GetSafeHwnd(),wxT("open"), strFileName, NULL, wxT("."), 0)/*ShellExecuteEx(&sei)*/;
			if(int(hInst)<=32/*sei.hInstApp==0*/)
			{
				wxString str;
				switch(int(hInst))
				{
					case 0 : str=wxT("The operating system is out of memory or resources. ");break;
					case ERROR_FILE_NOT_FOUND : str=wxT("The specified file was not found. ");break;
					case ERROR_PATH_NOT_FOUND : str=wxT("The specified path was not found. ");break;
					case ERROR_BAD_FORMAT : str=wxT("The .EXE file is invalid (non-Win32 .EXE or error in .EXE image). ");break;
					case SE_ERR_ACCESSDENIED : str=wxT("The operating system denied access to the specified file. ");break;
					case SE_ERR_ASSOCINCOMPLETE : str=wxT("The filename association is incomplete or invalid. ");break;
					case SE_ERR_DDEBUSY : str=wxT("The DDE transaction could not be completed because other DDE transactions were being processed. ");break;
					case SE_ERR_DDEFAIL : str=wxT("The DDE transaction failed. ");break;
					case SE_ERR_DDETIMEOUT : str=wxT("The DDE transaction could not be completed because the request timed out. ");break;
					case SE_ERR_DLLNOTFOUND : str=wxT("The specified dynamic-link library was not found. ");break;
					//case SE_ERR_FNF : str=wxT("The specified file was not found. ");break;
					case SE_ERR_NOASSOC : str=wxT("There is no application associated with the given filename extension. ");break;
					case SE_ERR_OOM : str=wxT("There was not enough memory to complete the operation. ");break;
					//case SE_ERR_PNF : str=wxT("The specified path was not found. ");break;
					case SE_ERR_SHARE : str=wxT("A sharing violation occurred. ");break;
					default: str=wxT("An unexpected error occurred");break;
				}
				MessageBoxF(wxT("Failed to open document %s using %s.\r\n%s"),strFileName,szExe,str);
			} else {
				rc=true;
			}
		}
	}
	return rc;
}

#endif

void ecUtils::UnicodeToCStr(const wxChar* str,char *&psz)
{
    int nLength=1 + wxStrlen(str);
    psz=new char[nLength];
#ifdef _UNICODE
    WideCharToMultiByte(CP_ACP, 0, str, -1, psz, nLength, NULL, NULL);
#else
    strcpy(psz,str);
#endif
}

std::string ecUtils::UnicodeToStdStr(const wxChar* str)
{
    std::string stdstr;
    char *psz;
    UnicodeToCStr(str,psz);
    stdstr=std::string(psz);
    delete psz;
    return stdstr;
}

// ecUtils::StripExtraWhitespace() returns a modified version of
// a string in which each sequence of whitespace characters is
// replaced by a single space

static bool ecIsSpace(wxChar ch)
{
	return (ch == wxT(' ') || ch == wxT('\r') || ch == wxT('\n') || ch == wxT('\t'));
}

wxString ecUtils::StripExtraWhitespace (const wxString & strInput)
{
    wxString strOutput;
    wxChar* o=strOutput.GetWriteBuf(1+strInput.Len());
    for(const wxChar* c=strInput.GetData();*c;c++){
        if(ecIsSpace(*c)){
            *o++=wxT(' ');
            if (ecIsSpace(c[1])){
                for(c=c+2; ecIsSpace(*c);c++);
                c--;
            }
        } else {
            *o++=*c;
        }
    }
    *o=0;
    strOutput.UngetWriteBuf();
    strOutput.Trim(TRUE);
    strOutput.Trim(FALSE);
    return strOutput;
#if 0    
    wxString strOutput;
    LPTSTR o=strOutput.GetBuffer(1+strInput.GetLength());
    for(LPCTSTR c=strInput;*c;c++){
        if(_istspace(*c)){
            *o++=_TCHAR(' ');
            if (_istspace(c[1])){
                for(c=c+2;_istspace(*c);c++);
                c--;
            }
        } else {
            *o++=*c;
        }
    }
    *o=0;
    strOutput.ReleaseBuffer();
    strOutput.TrimLeft();
    strOutput.TrimRight();
    return strOutput;
#endif
}

#if 0
ecFileName ecUtils::WPath(const std::string &str)
{
  // Convert a path as read from cdl into host format
  // Change / to \ throughout
  ecFileName
    strPath(str.c_str());
  strPath.Replace (_TCHAR('/'), _TCHAR('\\'));
  return strPath;
}

// Copy file helper function.
// This makes sure the destination file is only touched as necessary.
// It is written using Posix calls lest it should be more broadly applicable.

bool ecUtils::CopyFile(LPCTSTR pszSource,LPCTSTR pszDest)
{
  // Compare the files.  First set rc to the result of the comparison (true if the same)
  bool rc=false;

  struct _stat s1,s2;
  if(-1!=_tstat(pszSource,&s1) && -1!=_tstat(pszDest,&s2) && s1.st_size==s2.st_size){
    // Files both exist and are of equal size
    FILE *f1=_tfopen(pszSource,wxT("rb"));
    if(f1){
      FILE *f2=_tfopen(pszDest,wxT("rb"));
      if(f2){
        int nSize1,nSize2;
        rc=true;
        do{
          char buf1[4096],buf2[4096];
          nSize1=fread(buf1,1,sizeof buf1,f1);
          nSize2=fread(buf2,1,sizeof buf2,f2);
          if(nSize1!=nSize2 || 0!=memcmp(buf1,buf2,nSize1)){
            rc=false;
            break;
          }
        } while (nSize1>0);
        fclose(f2);
      }
      fclose(f1);
    }
  }

  if(rc){
    // Files are identical
  } else {
    rc=TRUE==::CopyFile(pszSource,pszDest,FALSE);
    if(rc){
    } else {
      MessageBoxF(wxT("Failed to copy '%s' to '%s' - %s"),pszSource,pszDest,GetLastErrorMessageString());
    }
  }

  return rc;
}

#endif

/*
 * wxStringToStringMap
 *
 * Stores string values keyed by strings
 */

void wxStringToStringMap::Set(const wxString& key, const wxString& value)
{
    wxString oldValue;
    if (Find(key, oldValue))
        Remove(key);
    m_hashTable.Put(key, (wxObject*) new wxString(value));
}

bool wxStringToStringMap::Remove(const wxString& key)
{
    wxString* str = (wxString*) m_hashTable.Delete(key);
    if (str)
    {
        delete str;
        return TRUE;
    }
    else
        return FALSE;
}

bool wxStringToStringMap::Find(const wxString& key, wxString& value)
{
    wxString* str = (wxString*) m_hashTable.Get(key);
    if (str)
    {
        value = * str;
        return TRUE;
    }
    else
        return FALSE;
}

void wxStringToStringMap::Clear()
{
    m_hashTable.BeginFind();
#if wxCHECK_VERSION(2, 6, 0)
    wxHashTable::Node* node;
    while ((node = m_hashTable.Next()))
    {
        wxString* str = (wxString*) node->GetData();
#else
    wxNode* node;
    while ((node = m_hashTable.Next()))
    {
        wxString* str = (wxString*) node->Data();
#endif
        delete str;
    }
}

void wxStringToStringMap::BeginFind()
{
    m_hashTable.BeginFind();
}

bool wxStringToStringMap::Next(wxString& key, wxString& value)
{
#if wxCHECK_VERSION(2, 6, 0)
    wxHashTable::Node* node = m_hashTable.Next();
    if (node)
    {
        value = * (wxString*) node->GetData();
#else
    wxNode* node = m_hashTable.Next();
    if (node)
    {
        value = * (wxString*) node->Data();
#endif
        return TRUE;
    }
    else
        return FALSE;
}

// Is str a member of arr?
bool wxArrayStringIsMember(const wxArrayString& arr, const wxString& str)
{
    size_t i;
    for (i = (size_t) 0; i < arr.GetCount(); i++)
        if (arr[i] == str)
            return TRUE;

    return FALSE;
}

// Eliminate .. and .
wxString wxGetRealPath(const wxString& path)
{
    wxChar* p = new wxChar[path.Len() + 1];
    wxStrcpy(p, (const wxChar*) path);
    wxRealPath(p);

    wxString str(p);
    delete[] p;
    return str;
}

// A version of the above but prepending 'cwd' (current path) first
// if 'path' is relative
wxString wxGetRealPath(const wxString& cwd, const wxString& path)
{
    wxString path1(path);

    if (!wxIsAbsolutePath(path))
    {
        path1 = cwd;
        if (path1.Last() != wxFILE_SEP_PATH)
            path1 += wxFILE_SEP_PATH;
        path1 += path;
    }

    return wxGetRealPath(path1);
}

// Find the absolute path where this application has been run from.
// argv0 is wxTheApp->argv[0]
// cwd is the current working directory (at startup)
// appVariableName is the name of a variable containing the directory for this app, e.g.
// MYAPPDIR. This is checked first.

wxString wxFindAppPath(const wxString& argv0, const wxString& cwd, const wxString& appVariableName)
{
    wxString str;

    // Try appVariableName
    if (!appVariableName.IsEmpty())
    {
        str = wxGetenv(appVariableName);
        if (!str.IsEmpty())
            return str;
    }

    if (wxIsAbsolutePath(argv0))
        return wxPathOnly(argv0);
    else
    {
        // Is it a relative path?
        wxString currentDir(cwd);
        if (currentDir.Last() != wxFILE_SEP_PATH)
            currentDir += wxFILE_SEP_PATH;

        str = currentDir + argv0;
        if (wxFileExists(str))
            return wxPathOnly(str);
    }

    // OK, it's neither an absolute path nor a relative path.
    // Search PATH.

    wxPathList pathList;
    pathList.AddEnvList(wxT("PATH"));
    str = pathList.FindAbsoluteValidPath(argv0);
    if (!str.IsEmpty())
        return wxPathOnly(str);

    // Failed
    return wxEmptyString;
}

// Make a path name with no separators, out of a full pathname,
// e.g. opt_ecos_ecos-1.4.5 out of /opt/ecos/ecos-1.4.5
wxString ecMakeNameFromPath(const wxString& path)
{
    wxString p(path);
    p.Replace(wxT("/"), wxT("_"));
    p.Replace(wxT("\\"), wxT("_"));
    p.Replace(wxT(":"), wxT("_"));
    return p;
}


// Find the text of the list control item at the given column
wxString wxListCtrlGetItemTextColumn(wxListCtrl& listCtrl, long item, int col)
{
    wxListItem listItem;
    listItem.m_mask = wxLIST_MASK_TEXT;
    listItem.m_itemId = item;
    listItem.m_col = col;

    if (listCtrl.GetItem(listItem))
        return listItem.m_text;
    else
        return wxEmptyString;
}

// Select the given item
void wxListCtrlSelectItem(wxListCtrl& listCtrl, long sel, bool deselectOthers)
{
    long n = listCtrl.GetItemCount();
    long i;
    if (deselectOthers)
    {
        for (i = 0; i < n; i++)
        {
            if (listCtrl.GetItemState(i, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
            {
                listCtrl.SetItemState(i, wxLIST_STATE_SELECTED, 0);
            }
        }
    }
    listCtrl.SetItemState(sel, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}

// Find the selection
long wxListCtrlGetSelection(wxListCtrl& listCtrl)
{
    long n = listCtrl.GetItemCount();
    long i;
    for (i = 0; i < n; i++)
    {
        if (listCtrl.GetItemState(i, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
        {
            return i;
        }
    }
    return -1;
}

// Find which column the cursor is on
int wxListCtrlFindColumn(wxListCtrl& listCtrl, int noCols, int x)
{
    int col = 0;
    
    // Find which column we're on
    int width = 0;
    int i;
    for (i = 0; i < noCols; i++)
    {
        width += listCtrl.GetColumnWidth(i);
        if (x <= width)
        {
            col = i;
            break;
        }
    }
    return col;
}

// Utility function
void wxRefreshControls(wxWindow* win)
{
#if wxCHECK_VERSION(2, 6, 0)
    wxNode *node = (wxNode *) win->GetChildren().GetFirst();
    while (node)
    {
        wxWindow* win = (wxWindow*) node->GetData();
        win->Refresh();
        node = node->GetNext();
    }
#else
    wxNode *node = win->GetChildren().First();
    while (node)
    {
        wxWindow* win = (wxWindow*) node->Data();
        win->Refresh();
        node = node->Next();
    }
#endif
}

wxOutputStream& operator <<(wxOutputStream& stream, const wxString& s)
{
    stream.Write(s, s.Length());
    return stream;
}

wxOutputStream& operator <<(wxOutputStream& stream, long l)
{
    wxString str;
    str.Printf("%ld", l);
    return stream << str;
}

wxOutputStream& operator <<(wxOutputStream& stream, const char c)
{
    wxString str;
    str.Printf("%c", c);
    return stream << str;
}

/*
 * ecDialog
 * Supports features we want to have for all dialogs in the application.
 * So far, this just allows dialogs to be resizeable under MSW by
 * refreshing the controls in OnSize (otherwise there's a mess)
 */

IMPLEMENT_CLASS(ecDialog, wxDialog)

BEGIN_EVENT_TABLE(ecDialog, wxDialog)
#ifdef __WXMSW__
    EVT_SIZE(ecDialog::OnSize)
#endif
END_EVENT_TABLE()

void ecDialog::OnSize(wxSizeEvent& event)
{
    wxDialog::OnSize(event);

    wxRefreshControls(this);
}

/*
 * Implements saving/loading of window settings - fonts only for now
 */

wxWindowSettingsObject* wxWindowSettings::FindSettings(const wxString& windowName) const
{
#if wxCHECK_VERSION(2, 6, 0)
    wxNode* node = m_settings.GetFirst();
    while (node)
    {
        wxWindowSettingsObject* obj = (wxWindowSettingsObject*) node->GetData();
        if (obj->m_windowName.CmpNoCase(windowName) == 0)
            return obj;
        node = node->GetNext();
    }
#else
    wxNode* node = m_settings.First();
    while (node)
    {
        wxWindowSettingsObject* obj = (wxWindowSettingsObject*) node->Data();
        if (obj->m_windowName.CmpNoCase(windowName) == 0)
            return obj;
        node = node->Next();
    }
#endif

    return NULL;
}

bool wxWindowSettings::LoadConfig(wxConfigBase& config)
{
    unsigned int i = 0;

    for (i = 0; i < GetCount(); i++)
    {
        wxWindowSettingsObject* obj = GetNth(i);

        wxString name(obj->m_windowName);
        name.Replace(wxT(" "), wxT(""));

        LoadFont(config, name, obj->m_font);
    }

    return TRUE;
}

bool wxWindowSettings::SaveConfig(wxConfigBase& config)
{
    unsigned int i = 0;
    for (i = 0; i < GetCount(); i++)
    {
        wxWindowSettingsObject* obj = GetNth(i);

        wxString name(obj->m_windowName);
        name.Replace(wxT(" "), wxT(""));

        SaveFont(config, name, obj->m_font);
    }

    return TRUE;
}

// Load and save font descriptions
bool wxWindowSettings::LoadFont(wxConfigBase& config, const wxString& windowName, wxFont& font)
{
    wxString pathBase(wxT("/Fonts/"));
    pathBase += windowName;
    pathBase += wxT("/");

    int pointSize, family, style, weight;
    bool underlined = FALSE;
    wxString faceName;

    if (!config.Read(pathBase + wxT("PointSize"), & pointSize))
        return FALSE;

    if (!config.Read(pathBase + wxT("Family"), & family))
        return FALSE;

    if (!config.Read(pathBase + wxT("Style"), & style))
        return FALSE;

    if (!config.Read(pathBase + wxT("Weight"), & weight))
        return FALSE;

    config.Read(pathBase + wxT("Underlined"), (bool*) & underlined);
    config.Read(pathBase + wxT("FaceName"), & faceName);

    wxFont font1(pointSize, family, style, weight, underlined, faceName);
    font = font1;

    return TRUE;    
}

bool wxWindowSettings::SaveFont(wxConfigBase& config, const wxString& windowName, const wxFont& font)
{
    if (!font.Ok())
        return FALSE;

    wxString pathBase(wxT("/Fonts/"));
    pathBase += windowName;
    pathBase += wxT("/");

    config.Write(pathBase + wxT("PointSize"), (long) font.GetPointSize());
    config.Write(pathBase + wxT("Family"), (long) font.GetFamily());
    config.Write(pathBase + wxT("Style"), (long) font.GetStyle());
    config.Write(pathBase + wxT("Weight"), (long) font.GetWeight());
    config.Write(pathBase + wxT("Underlined"), (long) font.GetUnderlined());
    config.Write(pathBase + wxT("FaceName"), font.GetFaceName());

    return TRUE;    
}

wxFont wxWindowSettings::GetFont(const wxString& name) const
{
    wxWindowSettingsObject* obj = FindSettings(name);
    if (!obj)
        return wxFont();
    else
        return obj->m_font;
}

void wxWindowSettings::SetFont(const wxString& name, const wxFont& font)
{
    wxWindowSettingsObject* obj = FindSettings(name);
    if (!obj)
    {
        obj = new wxWindowSettingsObject(name, NULL) ;
        obj->m_font = font;
        m_settings.Append(obj);
    }
    obj->m_font = font;
}

wxWindow* wxWindowSettings::GetWindow(const wxString& name) const
{
    wxWindowSettingsObject* obj = FindSettings(name);
    if (!obj)
        return NULL;
    if (obj->m_arrWindow.GetCount() > 0)
        return (wxWindow*) obj->m_arrWindow[0];
    else
        return NULL;
}

void wxWindowSettings::SetWindow(const wxString& name, wxWindow* win)
{
    wxWindowSettingsObject* obj = FindSettings(name);
    if (!obj)
    {
        obj = new wxWindowSettingsObject(name, win) ;
        m_settings.Append(obj);
    }
    obj->m_arrWindow.Clear();

    if (win)
        obj->m_arrWindow.Add(win);
}

wxArrayPtrVoid* wxWindowSettings::GetWindows(const wxString& name) const
{
    wxWindowSettingsObject* obj = FindSettings(name);
    if (!obj)
        return NULL;
    return & obj->m_arrWindow ;
}

void wxWindowSettings::SetWindows(const wxString& name, wxArrayPtrVoid& arr)
{
    wxWindowSettingsObject* obj = FindSettings(name);
    if (!obj)
    {
        obj = new wxWindowSettingsObject(name, NULL) ;
        m_settings.Append(obj);
    }
    obj->m_arrWindow.Clear() ;
    obj->m_arrWindow = arr;
}

bool wxWindowSettings::ApplyFontsToWindows()
{
    if (m_useDefaults)
        return FALSE;

    unsigned int i = 0;
    for (i = 0; i < GetCount(); i++)
    {
        wxWindowSettingsObject* obj = GetNth(i);

        unsigned int j = 0;
        for (j = 0; j < obj->m_arrWindow.GetCount(); j++)
        {
            wxWindow* win = (wxWindow*) obj->m_arrWindow[j];
            win->SetFont(obj->m_font);
            win->Refresh();
        }
    }
    return TRUE;
}

#ifdef __WIN32__
// This will be obsolete when we switch to using the version included
// in wxWindows (from wxWin 2.3.1 onwards)
enum ecKillError
{
    ecKILL_OK,              // no error
    ecKILL_BAD_SIGNAL,      // no such signal
    ecKILL_ACCESS_DENIED,   // permission denied
    ecKILL_NO_PROCESS,      // no such process
    ecKILL_ERROR            // another, unspecified error
};
#endif

// ----------------------------------------------------------------------------
// process management
// ----------------------------------------------------------------------------

#ifdef __WIN32__

// structure used to pass parameters from wxKill() to wxEnumFindByPidProc()
struct wxNewFindByPidParams
{
    wxNewFindByPidParams() { hwnd = 0; pid = 0; }

    // the HWND used to return the result
    HWND hwnd;

    // the PID we're looking from
    DWORD pid;
};

// wxKill helper: EnumWindows() callback which is used to find the first (top
// level) window belonging to the given process
static BOOL CALLBACK wxEnumFindByPidProc(HWND hwnd, LPARAM lParam)
{
    DWORD pid;
    (void)::GetWindowThreadProcessId(hwnd, &pid);

    wxNewFindByPidParams *params = (wxNewFindByPidParams *)lParam;
    if ( pid == params->pid )
    {
        // remember the window we found
        params->hwnd = hwnd;

        // return FALSE to stop the enumeration
        return FALSE;
    }

    // continue enumeration
    return TRUE;
}

// This will be obsolete when we switch to using the version included
// in wxWindows (from wxWin 2.3.1 onwards)
int wxNewKill(long pid, wxSignal sig, ecKillError *krc = NULL)
{
#ifdef __WIN32__
    // get the process handle to operate on
    HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
                                    PROCESS_TERMINATE |
                                    PROCESS_QUERY_INFORMATION,
                                    FALSE, // not inheritable
                                    (DWORD)pid);
    if ( hProcess == NULL )
    {
        if ( krc )
        {
            if ( ::GetLastError() == ERROR_ACCESS_DENIED )
            {
                *krc = ecKILL_ACCESS_DENIED;
            }
            else
            {
                *krc = ecKILL_NO_PROCESS;
            }
        }

        return -1;
    }

    bool ok = TRUE;
    switch ( sig )
    {
        case wxSIGKILL:
            // kill the process forcefully returning -1 as error code
            if ( !::TerminateProcess(hProcess, (UINT)-1) )
            {
                wxLogSysError(_("Failed to kill process %d"), pid);

                if ( krc )
                {
                    // this is not supposed to happen if we could open the
                    // process
                    *krc = ecKILL_ERROR;
                }

                ok = FALSE;
            }
            break;

        case wxSIGNONE:
            // do nothing, we just want to test for process existence
            break;

        default:
            // any other signal means "terminate"
            {
                wxNewFindByPidParams params;
                params.pid = (DWORD)pid;

                // EnumWindows() has nice semantics: it returns 0 if it found
                // something or if an error occured and non zero if it
                // enumerated all the window
                if ( !::EnumWindows(wxEnumFindByPidProc, (LPARAM)&params) )
                {
                    // did we find any window?
                    if ( params.hwnd )
                    {
                        // tell the app to close
                        //
                        // NB: this is the harshest way, the app won't have
                        //     opportunity to save any files, for example, but
                        //     this is probably what we want here. If not we
                        //     can also use SendMesageTimeout(WM_CLOSE)
                        if ( !::PostMessage(params.hwnd, WM_QUIT, 0, 0) )
                        {
                            wxLogLastError(_T("PostMessage(WM_QUIT)"));
                        }
                    }
                    else // it was an error then
                    {
                        wxLogLastError(_T("EnumWindows"));

                        ok = FALSE;
                    }
                }
                else // no windows for this PID
                {
                    if ( krc )
                    {
                        *krc = ecKILL_ERROR;
                    }

                    ok = FALSE;
                }
            }
    }

    // the return code
    DWORD rc;

    if ( ok )
    {
        // as we wait for a short time, we can use just WaitForSingleObject()
        // and not MsgWaitForMultipleObjects()
        switch ( ::WaitForSingleObject(hProcess, 500 /* msec */) )
        {
            case WAIT_OBJECT_0:
                // process terminated
                if ( !::GetExitCodeProcess(hProcess, &rc) )
                {
                    wxLogLastError(_T("GetExitCodeProcess"));
                }
                break;

            default:
                wxFAIL_MSG( _T("unexpected WaitForSingleObject() return") );
                // fall through

            case WAIT_FAILED:
                wxLogLastError(_T("WaitForSingleObject"));
                // fall through

            case WAIT_TIMEOUT:
                if ( krc )
                {
                    *krc = ecKILL_ERROR;
                }

                rc = STILL_ACTIVE;
                break;
        }
    }
    else // !ok
    {
        // just to suppress the warnings about uninitialized variable
        rc = 0;
    }

    ::CloseHandle(hProcess);

    // the return code is the same as from Unix kill(): 0 if killed
    // successfully or -1 on error
    if ( sig == wxSIGNONE )
    {
        if ( ok && rc == STILL_ACTIVE )
        {
            // there is such process => success
            return 0;
        }
    }
    else // not SIGNONE
    {
        if ( ok && rc != STILL_ACTIVE )
        {
            // killed => success
            return 0;
        }
    }
#else // Win15
    wxFAIL_MSG( _T("not implemented") );
#endif // Win32/Win16

    // error
    return -1;
}
#endif

int ecKill(long pid, wxSignal sig)
{
#if defined(__UNIX__) && !defined(__CYGWIN__)
    return wxKill(pid, sig);
#elif defined(__WXMSW__)
    return wxNewKill(pid, sig);
#else
    return -1;
#endif
}

#ifdef _WIN32
  #include <tlhelp32.h>

  WXHINSTANCE wxProcessKiller::hInstLib1 = VER_PLATFORM_WIN32_NT==wxProcessKiller::GetPlatform()? (WXHINSTANCE) LoadLibrary(_T("PSAPI.DLL")) : (WXHINSTANCE) LoadLibrary(_T("Kernel32.DLL")) ;
  WXHINSTANCE wxProcessKiller::hInstLib2 = VER_PLATFORM_WIN32_NT==wxProcessKiller::GetPlatform()? (WXHINSTANCE) LoadLibrary(_T("NTDLL.DLL")): (WXHINSTANCE) NULL;

#endif

const unsigned int wxProcessKiller::PROCESS_KILL_EXIT_CODE=0xCCFFCCFF;

wxProcessKiller::wxProcessKiller(int pid):
  m_bVerbose(false),
  m_nExitCode(-1),
  m_idProcess(pid)
{
#ifdef _WIN32
      m_hProcess = (WXHANDLE) ::OpenProcess(SYNCHRONIZE |
          PROCESS_TERMINATE |
          PROCESS_QUERY_INFORMATION,
          FALSE, // not inheritable
          (DWORD) m_idProcess);
#endif
}

wxProcessKiller::~wxProcessKiller()
{
#ifdef _WIN32
    if (m_hProcess)
    {
        ::CloseHandle((HANDLE) m_hProcess);
    }
#endif
}

bool wxProcessKiller::Kill(bool bRecurse)
{
    wxPInfoArray arPinfo;
    bool rc=false;
    if(m_idProcess && -1!=m_idProcess){
        // Start of with the easy one:
        if(bRecurse) {
            // Need to gather this information before we orphan our grandchildren:
            PSExtract(arPinfo);
        }
        
#ifdef _WIN32
        
        if(m_hProcess){
            rc=(TRUE==::TerminateProcess((HANDLE) m_hProcess,PROCESS_KILL_EXIT_CODE));
            // dtor's (or subsequent Run's) responsibility to close the handle
        }
        
#else
        rc=(0==kill(m_idProcess,SIGTERM));
        int status;
        waitpid(m_idProcess,&status,WNOHANG);
#endif
        
        if(bRecurse) {
            // kill process *and* its children
            // FIXME: needs to be top-down
            for(int i=0;i<(signed)arPinfo.size();i++){
                if(arPinfo[i].IsChildOf(m_idProcess)){
                    
#ifdef _WIN32
                    // begin hack
                    // WHY NECESSARY??
                    const wxString strName(Name(arPinfo[i].PID));
                    if(_tcsstr(strName,_T("eCosTest")) || _tcsstr(strName,_T("cmd.EXE")) || _tcsstr(strName,_T("CMD.EXE")) || arPinfo[i].PID==(signed)GetCurrentProcessId()){
                        continue;
                    }
                    // end hack
                    HANDLE hProcess=::OpenProcess(PROCESS_TERMINATE,false,arPinfo[i].PID);
                    if(hProcess){
                        rc&=(TRUE==::TerminateProcess(hProcess,PROCESS_KILL_EXIT_CODE));
                        CloseHandle(hProcess);
                    } else {
                        rc=false;
                    }
#else
                    rc&=(0==kill(arPinfo[i].PID,SIGTERM));
                    int status;
                    waitpid(arPinfo[i].PID,&status,WNOHANG);
#endif
                }
            }
        }
    }
    return rc;
}

#ifdef _WIN32
bool wxProcessKiller::PSExtract(wxProcessKiller::wxPInfoArray &arPinfo)
{
    bool rc=false;
    arPinfo.clear();
    // If Windows NT:
    switch(GetPlatform()) {
    case VER_PLATFORM_WIN32_NT:
        if(hInstLib1) {
            
            // Get procedure addresses.
            static BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * ) = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*))GetProcAddress( (HINSTANCE) hInstLib1, "EnumProcesses" ) ;
            if( lpfEnumProcesses) {
                
                if(hInstLib2) {
                    
                    static DWORD (WINAPI *lpfNtQueryInformationProcess)( HANDLE, int, void *, DWORD, LPDWORD ) =
                        (DWORD(WINAPI *)(HANDLE, int, void *, DWORD, LPDWORD)) GetProcAddress( (HINSTANCE) hInstLib2,"NtQueryInformationProcess" ) ;
                    
                    if(lpfNtQueryInformationProcess){
                        DWORD dwMaxPids=256;
                        DWORD dwPidSize;
                        DWORD *arPids = NULL ;
                        do {
                            delete [] arPids;
                            arPids=new DWORD[dwMaxPids];
                        } while(lpfEnumProcesses(arPids, dwMaxPids, &dwPidSize) && dwPidSize/sizeof(DWORD)==dwMaxPids) ;
                        
                        if(dwPidSize/sizeof(DWORD)<dwMaxPids){
                            rc=true;
                            for( DWORD dwIndex = 0 ; (signed)dwIndex < dwPidSize/sizeof(DWORD); dwIndex++ ) {
                                // Regardless of OpenProcess success or failure, we
                                // still call the enum func with the ProcID.
                                DWORD pid=arPids[dwIndex];
                                HANDLE hProcess=::OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid ); 
                                if (hProcess ) {
                                    struct {
                                        DWORD ExitStatus; // receives process termination status
                                        DWORD PebBaseAddress; // receives process environment block address
                                        DWORD AffinityMask; // receives process affinity mask
                                        DWORD BasePriority; // receives process priority class
                                        ULONG UniqueProcessId; // receives process identifier
                                        ULONG InheritedFromUniqueProcessId; // receives parent process identifier
                                    } pbi;
                                    memset( &pbi, 0, sizeof(pbi)); 
                                    DWORD retLen; 
                                    __int64 ftCreation,ftExit,ftKernel,ftUser;
                                    if(lpfNtQueryInformationProcess(hProcess, 0 /*ProcessBasicInformation*/, &pbi, sizeof(pbi), &retLen)>=0 &&
                                        TRUE==::GetProcessTimes (hProcess,(FILETIME *)&ftCreation,(FILETIME *)&ftExit,(FILETIME *)&ftKernel,(FILETIME *)&ftUser)){
                                        // The second test is important.  It excludes orphaned processes who appear to have been adopted by virtue of a new
                                        // process having been created with the same ID as their original parent.
                                        wxPInfo p;
                                        p.PID=pid;
                                        p.PPID=pbi.InheritedFromUniqueProcessId;
                                        p.tCreation=ftCreation;
                                        p.tCpu=Time((ftKernel+ftUser)/10000);
                                        arPinfo.push_back(p);
                                    }
                                    
                                    CloseHandle(hProcess); 
                                    
                                }
                            }
                        }
                        delete [] arPids;
                    }          
                }
            }      
        }
        break;
    case VER_PLATFORM_WIN32_WINDOWS:
        
        if( hInstLib1) {
            
            static HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD)=
                (HANDLE(WINAPI *)(DWORD,DWORD))GetProcAddress( (HINSTANCE) hInstLib1,"CreateToolhelp32Snapshot" ) ;
            static BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32)=
                (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))GetProcAddress( (HINSTANCE) hInstLib1, "Process32First" ) ;
            static BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32)=
                (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))GetProcAddress( (HINSTANCE) hInstLib1, "Process32Next" ) ;
            if( lpfProcess32Next && lpfProcess32First && lpfCreateToolhelp32Snapshot) {
                
                // Get a handle to a Toolhelp snapshot of the systems
                // processes.
                HANDLE hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;
                if(INVALID_HANDLE_VALUE != hSnapShot) {
                    // Get the first process' information.
                    PROCESSENTRY32 procentry;
                    procentry.dwSize = sizeof(PROCESSENTRY32) ;
                    if(lpfProcess32First( hSnapShot, &procentry )){
                        rc=true;
                        do {
                            wxPInfo p;
                            p.PID=procentry.th32ProcessID;
                            p.PPID=procentry.th32ParentProcessID;
                            arPinfo.push_back(p);
                        } while(lpfProcess32Next( hSnapShot, &procentry ));
                    }
                    CloseHandle(hSnapShot);
                }
            }
        }
        break;
    default:
        break;
    }    
    
    SetParents(arPinfo);
    
    if(!rc){
        wxLogError(_T("Couldn't get process information!\n"));
    }
    return rc;
}

#else // UNIX

bool wxProcessKiller::PSExtract(wxProcessKiller::wxPInfoArray &arPinfo)
{
    arPinfo.clear();
    int i;
    FILE *f=popen("ps -l",_T("r") MODE_TEXT);
    if(f){
        char buf[100];
        while(fgets(buf,sizeof(buf)-1,f)){
            TCHAR discard[100];
            wxPInfo p;
            // Output is in the form
            //  F S   UID   PID  PPID  C PRI  NI ADDR    SZ WCHAN  TTY          TIME CMD
            //100 S   490   877   876  0  70   0    -   368 wait4  pts/0    00:00:00 bash
            int F,UID,C,PRI,NI,SZ,HH,MM,SS; 
            bool rc=(15==_stscanf(buf,_T("%d %s %d %d %d %d %d %d %s %d %s %s %d:%d:%d"),&F,discard,&UID,&p.PID,&p.PPID,&C,&PRI,&NI,discard,&SZ,discard,discard,&HH,&MM,&SS));
            if(rc){
                p.tCpu=1000*(SS+60*(60*HH+MM));
                arPinfo.push_back(p);
            }
        }
        pclose(f);
        for(i=0;i<(signed)arPinfo.size();i++){
            int pid=arPinfo[i].PPID;
            arPinfo[i].pParent=0;
            for(int j=0;j<(signed)arPinfo.size();j++){
                if(i!=j && arPinfo[j].PID==pid){
                    arPinfo[i].pParent=&arPinfo[j];
                    break;
                }
            }
        }
    } else {
        wxLogError(_T("Failed to run ps -l\n"));
    }
    return true; //FIXME
}

#endif

void wxProcessKiller::SetParents(wxProcessKiller::wxPInfoArray &arPinfo)
{
    int i;
    for(i=0;i<(signed)arPinfo.size();i++){
        wxPInfo &p=arPinfo[i];
        p.pParent=0;
        for(int j=0;j<(signed)arPinfo.size();j++){
            if(arPinfo[j].PID==p.PPID 
#ifdef _WIN32
                && arPinfo[j].tCreation<p.tCreation
#endif
                )
            {
                arPinfo[i].pParent=&arPinfo[j];
                break;
            }
        }
    }
    
    // Check for circularity
    bool bCircularity=false;
    for(i=0;i<(signed)arPinfo.size();i++){
        wxPInfo *p=&arPinfo[i];
        for(int j=0;j<(signed)arPinfo.size() && p;j++){
            p=p->pParent;
        }
        // If all is well, p should be NULL here.  Otherwise we have a loop.
        if(p){
            // Make sure it can't foul things up:
            arPinfo[i].pParent=0;
            bCircularity=true;
        }
    }
    
    if(bCircularity){
        wxLogError(_T("!!! Circularly linked process list at index %d\n"),i);
        for(int k=0;k<(signed)arPinfo.size();k++){
            const wxPInfo &p=arPinfo[k];
            wxLogError(_T("%d: %s ppid=%4d\n"),k,(LPCTSTR)Name(p.PID),p.PPID);
        }
    }
}

bool wxProcessKiller::wxPInfo::IsChildOf(int pid) const
{
    for(wxPInfo *p=pParent;p && p!=this;p=p->pParent) { // guard against circular linkage
        if(p->PID==pid){
            return true;
        }
    }
    return false;
}

const wxString wxProcessKiller::Name(int pid)
{
    wxString str;
    str.Printf(_T("id=%d"),pid);
#ifdef _DEBUG
#ifdef _WIN32
    if(VER_PLATFORM_WIN32_NT==GetPlatform() && hInstLib1){
        static BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *, DWORD, LPDWORD ) =
            (BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( (HINSTANCE) hInstLib1,"EnumProcessModules" ) ;
        static DWORD (WINAPI *lpfGetModuleFileNameEx)( HANDLE, HMODULE, LPTSTR, DWORD )=
            (DWORD (WINAPI *)(HANDLE, HMODULE,LPTSTR, DWORD )) GetProcAddress( (HINSTANCE) hInstLib1,"GetModuleFileNameExA" ) ;
        if( lpfEnumProcessModules &&  lpfGetModuleFileNameEx ) {
            HANDLE hProcess=::OpenProcess(PROCESS_ALL_ACCESS,false,pid);
            if(hProcess) {
                HMODULE hMod;
                DWORD dwSize;
                if(lpfEnumProcessModules( hProcess, &hMod, sizeof(HMODULE), &dwSize ) ){
                    // Get Full pathname:
                    TCHAR buf[1+MAX_PATH];
                    lpfGetModuleFileNameEx( hProcess, hMod, buf, MAX_PATH);
                    str+=_TCHAR(' ');
                    str+=buf;
                }
                CloseHandle(hProcess);
            }
        }
    }
#endif
#endif
    return str;
}

#ifdef _WIN32
long wxProcessKiller::GetPlatform()
{
    OSVERSIONINFO  osver;
    osver.dwOSVersionInfoSize = sizeof( osver ) ;
    return GetVersionEx( &osver ) ? (long) osver.dwPlatformId : (long)-1;
}
#endif

const wxString wxProcessKiller::ErrorString() const
{
#ifdef _WIN32
    TCHAR *pszMsg;
    FormatMessage(  
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        m_nErr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR)&pszMsg,
        0,
        NULL 
        );
    return pszMsg;
#else 
    return strerror(errno);
#endif
}

