/*
** 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- December 1997
 */

/*
 * CvsPrefs.cpp --- class to handle CVS settings
 */

#include "stdafx.h"

#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <string>
#include "CvsPrefs.h"
#include "Authen.h"
#include "AppConsole.h"
#include "CvsArgs.h"
#include "MultiString.h"
#include "FileTraversal.h"

#ifdef TARGET_OS_MAC
#	define PCVS "\pCVSROOT"
#	include <Resources.h>
#	include <Folders.h>
#	include <MacErrors.h>
#	include <TextUtils.h>
#	include "VolsPaths.h"
#	include "MacMisc.h"
#	include "AppGlue.h"
#endif /* TARGET_OS_MAC */

#ifdef HAVE_UNISTD_H
#	include <unistd.h>
#endif

#ifdef HAVE_PWD_H
#	include <pwd.h>
#endif

#ifdef WIN32
#	include "WinCvsBrowser.h"
#	include "wincvs.h"
#	include "wincvs_winutil.h"
#	ifdef _DEBUG
#	define new DEBUG_NEW
#	undef THIS_FILE
	static char THIS_FILE[] = __FILE__;
#	endif
#endif /* WIN32 */

#ifdef qMacCvsPP
#	include "GUSIInternal.h"
#	include "GUSIFileSpec.h"
#	include "MacCvsApp.h"
#	include <UProcess.h>
#endif

#if qUnix
#	include "UCvsApp.h"
#	include "UCvsFolders.h"
#endif

#ifndef WIN32
#	define DEFAULT_MATCH_ENTRIES_CASE true
#else
#	define DEFAULT_MATCH_ENTRIES_CASE false
#endif
	
CvsPrefs gCvsPrefs;

static CPersistentInt gPersistentShema("P_PersistentShema", SCHEMA_0);

static CPersistentBool gCheckoutRO("P_CheckoutRO", false, kAddSettings);
static CPersistentBool gZ9Option("P_Z9Option", false, kAddSettings);
static CPersistentBool gPruneOption("P_PruneOption", true, kAddSettings);
static CPersistentBool gQuietOption("P_QuietOption", false);
static CPersistentInt gIsoConvert("P_NIsoConvert", ISO8559_none, kAddSettings);
static CPersistentBool gICTextOption("P_ICTextOption", false, kAddSettings);
static CPersistentBool gAddBinControl("P_AddBinControl", true);
static CPersistentInt gMacBinEncoding("P_NMacBinEncoding", MAC_HQX, kAddSettings);
static CPersistentBool gMacBinEncodeOnlyBinaryFiles("P_MacBinOnlyBinaries", true, kAddSettings);
static CPersistentInt gUseShell("P_UseShell", SHELL_TCL);
static CPersistentBool gMatchEntriesCase("P_MatchEntriesCase", DEFAULT_MATCH_ENTRIES_CASE);

#ifndef WIN32
// Obsolete, replaced by gFileActionType
static CPersistentBool gUseViewerAlways("P_UseViewerAlways", false, kAddSettings);
#endif

#if INTERNAL_AUTHEN
static PCPStr gCvsroot("P_Cvsroot");
#endif /*# INTERNAL_AUTHEN */

CMString gOldCvsroots(NUM_CVSROOT, "P_OldCvsroots");
static CKeyString gLastCvsroot(100, "P_LastCvsroot");
static CKeyString gLastModuleName(100, "P_LastModuleName");

static CPersistentInt gKserverPort("P_KserverPort", -1, kAddSettings);
static CPersistentInt gPserverPort("P_PserverPort", -1, kAddSettings);
static CPersistentInt gRhostPort("P_RhostPort", -1, kAddSettings);
static PCPStr gServerName("P_ServerName", 0L, kAddSettings);
static PCStr gRshName("P_RshName", 0L, kAddSettings);
static CPersistentBool gEncryptCommunication("P_EncryptCommunication", false, kAddSettings);

#if !qUnix && !TARGET_RT_MAC_MACHO
static PCStr gHome("P_Home", 0L, kAddSettings);
#endif /* !qUnix */

#ifdef qUnix
static PCStr gConsoleFont("P_ConsoleFont", "-adobe-courier-medium-r-*-*-12-*-*-*-*-*-iso8859-1");
#endif

#ifdef qUnix
static PCStr gViewer("P_Viewer", "emacs"); // not vi ;-)
#elif defined(qMacCvsPP)
static PCStr gViewer("P_Viewer", "");
#else
static PCStr gViewer("P_Viewer", "Notepad");
#endif

static PCStr gHelper("P_Helper", "netscape");
static PCStr gBrowser("P_Browser", "gmc");

#ifdef qUnix
static PCStr gExtdiff("P_Extdiff", "emacs");
#elif TARGET_RT_MAC_MACHO
static PCStr gExtdiff("P_Extdiff", "/usr/bin/opendiff");
#else
static PCStr gExtdiff("P_Extdiff", "");
#endif

#if DIRTY_MCP_SUPPORT
static CPersistentBool gDirtySupport("P_DirtySupport", true, kAddSettings);
#endif

static CPersistentBool gAlwaysUseCvsroot("P_AlwaysUseCvsroot", false, kAddSettings);
static CPersistentInt gLogoutTimeOut("P_LogoutTimeOut", 0);
static PCStr gProxyHost("P_ProxyHost", "www.mydomain", kAddSettings);
static CPersistentInt gProxyPort("P_ProxyPort", 8888, kAddSettings);
static CPersistentBool gUseProxy("P_UseProxy", false, kAddSettings);
static CPersistentBool gUnixLF("P_UnixLF", false, kAddSettings);
static CPersistentBool gMacLF("P_MacLF", false, kAddSettings);
static CPersistentInt gZLevel("P_ZLevel", 9, kAddSettings);
static CPersistentInt gCvsVersion("P_CvsVersion", 0, kAddSettings);
static CPersistentBool gCvsConsole("P_CvsConsole", false, kAddSettings);
static CPersistentInt gWordWrapLogMsg("P_WordWrapLogMsg", 0, kAddSettings);
static PCStr gLastWorkingDir("P_LastWorkingDir", 0L, kAddSettings);

static CPersistentBool gDisableSplashScreen("P_DisableSplashScreen", false);

static CPersistentInt gStatusOutputType("P_StatusOutputType", STATUSOUTPUT_VERBOSE);
static CPersistentInt gFileActionType("P_FileActionType", 0);
static CPersistentBool gUpdateCaseSensitiveNames("P_UpdateCaseSensitiveNames", false, kAddSettings);
static CPersistentBool gUpdateLastCheckinTime("P_UpdateLastCheckinTime", false, kAddSettings);
static CPersistentBool gUpdateThreeWayConflicts("P_UpdateThreeWayConflicts", false, kAddSettings);

static CPersistentBool gHideCommandDlgUpdate("P_HideCommandDlgUpdate", false);
static CPersistentBool gHideCommandDlgDiff("P_HideCommandDlgDiff", false);
static CPersistentBool gHideCommandDlgLog("P_HideCommandDlgLog", false);
static CPersistentBool gHideCommandDlgGraph("P_HideCommandDlgGraph", false);
static CPersistentBool gHideCommandDlgAnnotate("P_HideCommandDlgAnnotate", false);
static CPersistentBool gHideCommandDlgStatus("P_HideCommandDlgStatus", false);
static CPersistentBool gHideCommandDlgFileAction("P_HideCommandDlgFileAction", false);
static CPersistentBool gHideCommandDlgUseShift("P_HideCommandDlgUseShift", true);
static CPersistentBool gCtrlPressedOnOK("P_CtrlPressedOnOK", true);

CKeyString gWhichCvs(20, "P_WhichCvs");
static CPersistentBool gUseAltCvs("P_UseAltCvs", false, kAddSettings);
static PCStr gSettingsFolder("P_SettingsFolder", 0L);
static CPersistentInt gShowCvs("P_ShowCvs", SHOWCVS_HIDE, kAddSettings);

static PCStr gTclLibrary("P_TclLibrary", 0L);
static PCStr gPythonLibrary("P_PythonLibrary", 0L);

bool CvsPrefs::CheckoutRO(void) const {return gCheckoutRO;}
bool CvsPrefs::Z9Option(void) const {return gZ9Option;}
bool CvsPrefs::PruneOption(void) const {return gPruneOption;}
bool CvsPrefs::QuietOption(void) const {return gQuietOption;}
ISO8559 CvsPrefs::IsoConvert(void) const {return (ISO8559)(int)gIsoConvert;}
bool CvsPrefs::ICTextOption(void) const {return gICTextOption;}
bool CvsPrefs::AddControl(void) const {return gAddBinControl;}
bool CvsPrefs::MacBinEncodeBinariesOnly(void) const {return gMacBinEncodeOnlyBinaryFiles;}
MACENCODING CvsPrefs::MacBinEncoding(void) const {return (MACENCODING)(int)gMacBinEncoding;}
int CvsPrefs::KserverPort(void) const {return gKserverPort;}
int CvsPrefs::PserverPort(void) const {return gPserverPort;}
int CvsPrefs::RhostPort(void) const {return gRhostPort;}
bool CvsPrefs::MatchEntriesCase(void) const {return gMatchEntriesCase;}

#ifndef WIN32
bool CvsPrefs::UseViewerAlways(void) const {return gUseViewerAlways;}
#endif

const char* CvsPrefs::ServerName(void) const {return gServerName.empty() ? 0L : (const char*)gServerName;}
const char* CvsPrefs::RshName(void) const {return gRshName.empty() ? 0L : (const char*)gRshName;}
bool CvsPrefs::EncryptCommunication(void) const {return gEncryptCommunication;}

const char* CvsPrefs::Home(void) const
{
#if !qUnix && !TARGET_RT_MAC_MACHO
#	ifdef WIN32
	// Windows 98 doesn't like C:\/.cvspass
	gHome.rtrim (kPathDelimiter);
#	endif
	return gHome.empty() ? 0L : (const char*)gHome;
#else /* qUnix || TARGET_RT_MAC_MACHO */
	static CStr home;
	if (!home.empty())
		return home;

	char *env = getenv ("HOME");
	struct passwd *pw;
	if (env)
		home = env;
	else if ((pw = (struct passwd *) getpwuid (getuid ()))
			 && pw->pw_dir)
		home = pw->pw_dir;
	else
		return 0L;

	return home;
#endif /* qUnix ||ĘTARGET_RT_MAC_MACHO */
}

#ifdef qUnix
const char* CvsPrefs::ConsoleFont(void) const {return gConsoleFont.empty() ? 0L : (const char*)gConsoleFont;}
#endif

#if DIRTY_MCP_SUPPORT
bool CvsPrefs::DirtySupport(void) const {return gDirtySupport;}
#endif

const char* CvsPrefs::Viewer(void) const {return gViewer.empty() ? 0L : (const char*)gViewer;}
const char* CvsPrefs::Helper(void) const {return gHelper.empty() ? 0L : (const char*)gHelper;}
const char* CvsPrefs::Browser(void) const {return gBrowser.empty() ? 0L : (const char*)gBrowser;}
const char* CvsPrefs::ExtDiff(void) const {return gExtdiff.empty() ? 0L : (const char*)gExtdiff;}

int CvsPrefs::LogoutTimeOut(void) const {return gLogoutTimeOut;}
bool CvsPrefs::AlwaysUseCvsroot(void) const {return gAlwaysUseCvsroot;}
const char* CvsPrefs::ProxyHost(void) const {return gProxyHost;}
int CvsPrefs::ProxyPort(void) const {return gProxyPort;}
bool CvsPrefs::UseProxy(void) const {return gUseProxy;}
bool CvsPrefs::UnixLF(void) const {return gUnixLF;}
bool CvsPrefs::MacLF(void) const {return gMacLF;}
int CvsPrefs::ZLevel(void) const {return gZLevel;}
int CvsPrefs::CvsVersion(void) const {return gCvsVersion;}
bool CvsPrefs::CvsConsole(void) const {return gCvsConsole;}
int CvsPrefs::WordWrapLogMsg(void) const {return gWordWrapLogMsg;}
SHELL_KIND CvsPrefs::GetUseShell(void) const {return (SHELL_KIND)(int)gUseShell;}
bool CvsPrefs::DisableSplashScreen(void) const {return gDisableSplashScreen;}

STATUSOUTPUT_TYPE CvsPrefs::StatusOutputType(void) const {return (STATUSOUTPUT_TYPE)(int)gStatusOutputType;}
FILEACTION_TYPE CvsPrefs::FileActionType(void) const {return (FILEACTION_TYPE)(int)gFileActionType;}
bool CvsPrefs::UpdateCaseSensitiveNames(void) const {return gUpdateCaseSensitiveNames;}
bool CvsPrefs::UpdateLastCheckinTime(void) const {return gUpdateLastCheckinTime;}
bool CvsPrefs::UpdateThreeWayConflicts(void) const {return gUpdateThreeWayConflicts;}


const char* CvsPrefs::SettingsFolder(void) const {return gSettingsFolder.empty() ? 0L : (const char*)gSettingsFolder;};
const char* CvsPrefs::TclLibrary(void) const {return gTclLibrary.empty() ? 0L : (const char*)gTclLibrary;};
const char* CvsPrefs::PythonLibrary(void) const {return gPythonLibrary.empty() ? 0L : (const char*)gPythonLibrary;};

/* Return the current working dir (last working dir).   */
/* This directory is automatically selected when WinCVS */
/* is opened the next time. */
const char* CvsPrefs::LastWorkingDir(void) const
{
	return gLastWorkingDir.empty() ? 0L : (const char*)gLastWorkingDir;
}

bool CvsPrefs::HideCommandDlgUpdate(void) const {return gHideCommandDlgUpdate;}
bool CvsPrefs::HideCommandDlgDiff(void) const {return gHideCommandDlgDiff;}
bool CvsPrefs::HideCommandDlgLog(void) const {return gHideCommandDlgLog;}
bool CvsPrefs::HideCommandDlgGraph(void) const {return gHideCommandDlgGraph;}
bool CvsPrefs::HideCommandDlgAnnotate(void) const {return gHideCommandDlgAnnotate;}
bool CvsPrefs::HideCommandDlgStatus(void) const {return gHideCommandDlgStatus;}
bool CvsPrefs::HideCommandDlgFileAction(void) const {return gHideCommandDlgFileAction;}
bool CvsPrefs::HideCommandDlgUseShift(void) const {return gHideCommandDlgUseShift;}
bool CvsPrefs::CtrlPressedOnOK(void) const {return gCtrlPressedOnOK;}

void CvsPrefs::SetCheckoutRO(bool newState) {gCheckoutRO = newState;}
void CvsPrefs::SetZ9Option(bool newState) {gZ9Option = newState;}
void CvsPrefs::SetPruneOption(bool newState) {gPruneOption = newState;}
void CvsPrefs::SetQuietOption(bool newState) {gQuietOption = newState;}
void CvsPrefs::SetIsoConvert(ISO8559 newState) {gIsoConvert = (int)newState;}
void CvsPrefs::SetICTextOption(bool newState) {gICTextOption = newState;}
void CvsPrefs::SetAddControl(bool newState) {gAddBinControl = newState;}
void CvsPrefs::SetMacBinEncodeBinariesOnly(bool newState) {gMacBinEncodeOnlyBinaryFiles = newState;}
void CvsPrefs::SetMacBinEncoding(MACENCODING newState) {gMacBinEncoding = (int)newState;}
void CvsPrefs::SetKserverPort(int newState) {gKserverPort = newState;}
void CvsPrefs::SetPserverPort(int newState) {gPserverPort = newState;}
void CvsPrefs::SetRhostPort(int newState) {gRhostPort = newState;}
void CvsPrefs::SetServerName(const char* newservername) {gServerName = newservername;}
void CvsPrefs::SetRshName(const char* newrshname) {gRshName = newrshname;}
void CvsPrefs::SetEncryptCommunication(bool newState) {gEncryptCommunication = newState;}
void CvsPrefs::SetMatchEntriesCase(bool newState) {gMatchEntriesCase = newState;}

#ifndef WIN32
void CvsPrefs::SetUseViewerAlways(bool newState) {gUseViewerAlways = newState;}
#endif

void CvsPrefs::SetHome(const char* newhome)
{
#if !qUnix && !TARGET_RT_MAC_MACHO
	gHome = newhome;
#	ifdef WIN32
	// Windows 98 doesn't like C:\/.cvspass
	gHome.rtrim (kPathDelimiter);
#	endif
#endif /* !qUnix && !TARGET_RT_MAC_MACHO */
}

#ifdef qUnix
void CvsPrefs::SetConsoleFont(const char* newFont) {gConsoleFont = newFont;}
#endif

#if DIRTY_MCP_SUPPORT
void CvsPrefs::SetDirtySupport(bool newState) {gDirtySupport = newState;}
#endif

void CvsPrefs::SetViewer(const char* newviewer) {gViewer = newviewer;}
void CvsPrefs::SetBrowser(const char* newviewer) {gBrowser = newviewer;}
void CvsPrefs::SetHelper(const char* newviewer) {gHelper = newviewer;}
void CvsPrefs::SetExtDiff(const char* newdiff) {gExtdiff = newdiff;}

void CvsPrefs::SetLogoutTimeOut(int newTimeout) {gLogoutTimeOut = newTimeout;}
void CvsPrefs::SetAlwaysUseCvsroot(bool newState) {gAlwaysUseCvsroot = newState;}
void CvsPrefs::SetUseProxy(bool useit) {gUseProxy = useit;}
void CvsPrefs::SetUnixLF(bool useit) {gUnixLF = useit;}
void CvsPrefs::SetMacLF(bool useit) {gMacLF = useit;}
void CvsPrefs::SetZLevel(int level) {gZLevel = level;}
void CvsPrefs::SetProxyHost(const char* newhost) {gProxyHost = newhost;}
void CvsPrefs::SetProxyPort(int newport) {gProxyPort = newport;}
void CvsPrefs::SetCvsVersion(int vers) {gCvsVersion = vers;}
void CvsPrefs::SetCvsConsole(bool console) {gCvsConsole = console;}
void CvsPrefs::SetWordWrapLogMsg(int wordwrap) {gWordWrapLogMsg = wordwrap;}
void CvsPrefs::SetLastWorkingDir(const char* workdir) {gLastWorkingDir = workdir;}
void CvsPrefs::SetUseShell(SHELL_KIND shell) {gUseShell = shell;}
void CvsPrefs::SetDisableSplashScreen(bool newState) {gDisableSplashScreen = newState;}

void CvsPrefs::SetStatusOutputType(STATUSOUTPUT_TYPE outputType) {gStatusOutputType = outputType;}
void CvsPrefs::SetFileActionType(FILEACTION_TYPE fileActionType) {gFileActionType = fileActionType;}
void CvsPrefs::SetUpdateCaseSensitiveNames(bool newState) {gUpdateCaseSensitiveNames = newState;}
void CvsPrefs::SetUpdateLastCheckinTime(bool newState) {gUpdateLastCheckinTime = newState;}
void CvsPrefs::SetUpdateThreeWayConflicts(bool newState) {gUpdateThreeWayConflicts = newState;}

void CvsPrefs::SetSettingsFolder(const char* settingsFolder) {gSettingsFolder = settingsFolder;}
void CvsPrefs::SetTclLibrary(const char* tclLibrary) {gTclLibrary = tclLibrary;}
void CvsPrefs::SetPythonLibrary(const char* pythonLibrary) {gPythonLibrary = pythonLibrary;}

void CvsPrefs::SetHideCommandDlgUpdate(bool newState) {gHideCommandDlgUpdate = newState;}
void CvsPrefs::SetHideCommandDlgDiff(bool newState) {gHideCommandDlgDiff = newState;}
void CvsPrefs::SetHideCommandDlgLog(bool newState) {gHideCommandDlgLog = newState;}
void CvsPrefs::SetHideCommandDlgGraph(bool newState) {gHideCommandDlgGraph = newState;}
void CvsPrefs::SetHideCommandDlgAnnotate(bool newState) {gHideCommandDlgAnnotate = newState;}
void CvsPrefs::SetHideCommandDlgStatus(bool newState) {gHideCommandDlgStatus = newState;}
void CvsPrefs::SetHideCommandDlgFileAction(bool newState) {gHideCommandDlgFileAction = newState;}
void CvsPrefs::SetHideCommandDlgUseShift(bool newState) {gHideCommandDlgUseShift = newState;}
void CvsPrefs::SetCtrlPressedOnOK(bool newState) {gCtrlPressedOnOK = newState;}

#if INTERNAL_AUTHEN
bool CvsPrefs::empty(void) const  {	return gCvsroot.empty();  }
int  CvsPrefs::length(void) const {	return gCvsroot.length(); }

CvsPrefs::operator const char*() const {	return (const char*)gCvsroot; }
CvsPrefs::operator const unsigned char *() const {return (const unsigned char *)gCvsroot;}
const char* CvsPrefs::operator=(const char*newstr) {	return gCvsroot = newstr; }
const unsigned char *CvsPrefs::operator=(const unsigned char *newstr) {	return gCvsroot = newstr; }
#endif /* INTERNAL_AUTHEN */

void CvsPrefs::save(bool quiet /*= false*/)
{
#if INTERNAL_AUTHEN
	if( empty() )
		return;
#endif /* INTERNAL_AUTHEN */

	if( SCHEMA_CURRENT != gPersistentShema )
	{
		gPersistentShema = SCHEMA_CURRENT;
	}

	bool needSave = CPersistent::NeedSave();
	bool needSaveSettings = CPersistent::NeedSave(true);
	if( !needSave )
		return;

	CPersistent::SaveAll();

	if( !needSaveSettings )
		return;

#ifdef WIN32
	{
		extern PCPStr gOldLoc;
		SavePersistentSettings(gOldLoc, quiet);
	}
#endif
#ifdef qMacCvsPP
	CMacCvsApp::SavePersistentSettings(quiet);
#endif /* qMacCvsPP */
#if qUnix
	UCvsFolders *browser = UCvsApp::gApp->GetBrowserView();
	if(browser != 0L && browser->GetRoot() != 0L)
		SavePersistentSettings(browser->GetRoot(), quiet);
#endif
}

void CvsPrefs::load(void)
{
	bool loaded = CPersistent::LoadAll();

	if( loaded )
	{
		if( NeedCvsrootMigration() )
		{
			MigrateCvsrootList();
		}

		return;
	}

	// backward compatibility
#ifdef WIN32
	UINT value;
	
	CString cstr;
#if INTERNAL_AUTHEN
	cstr = AfxGetApp()->GetProfileString(PROFILE_NAME, "CVSROOT");
	if(!cstr.IsEmpty())
		*this = (const char*)cstr;
#endif /* INTERNAL_AUTHEN */

	if((value = AfxGetApp()->GetProfileInt(PROFILE_NAME, "CheckoutRO", -1)) != -1)
		SetCheckoutRO(value ? 1 : 0);
	if((value = AfxGetApp()->GetProfileInt(PROFILE_NAME, "Z9Option", -1)) != -1)
		SetZ9Option(value ? 1 : 0);
	if((value = AfxGetApp()->GetProfileInt(PROFILE_NAME, "QuietOption", -1)) != -1)
		SetQuietOption(value ? 1 : 0);
	if((value = AfxGetApp()->GetProfileInt(PROFILE_NAME, "PruneOption", -1)) != -1)
		SetPruneOption(value ? 1 : 0);

#if INTERNAL_AUTHEN
	if((value = AfxGetApp()->GetProfileInt(PROFILE_NAME, "Authentication", -1)) != -1)
		gAuthen.setkind((AuthenKind)value);
#endif /* INTERNAL_AUTHEN */

	if((value = AfxGetApp()->GetProfileInt(PROFILE_NAME, "AddControl", -1)) != -1)
		SetAddControl(value ? 1 : 0);

	char tmp[20];
	for(int i = 0; i < NUM_CVSROOT; i++)
	{
		sprintf(tmp, "CVSROOTLIST%d", i);
		cstr = AfxGetApp()->GetProfileString(PROFILE_NAME, tmp);
		if(!cstr.IsEmpty())
			gOldCvsroots.Insert((const char*)cstr);
	}

#endif /* WIN32 */

}

/*!
	Check whether there is a need for the cvsroot history migration
	\return true if migration needed, false otherwise
	\note Implementation is platform-specific
*/
bool CvsPrefs::NeedCvsrootMigration()
{
	bool res = false;

#ifdef WIN32
	if( SCHEMA_0 == gPersistentShema )
	{
		res = true;
	}
#endif /* WIN32 */

#if qUnix
	if( SCHEMA_0 == gPersistentShema || SCHEMA_1 == gPersistentShema )
	{
		res = true;
	}
#endif // qUnix
	
#if qMacCvsPP
#endif // qMacCvsPP

	return res;
}

/*!
	Find the best CVS root match for a given path
	\param path Path to find the match for
	\param lastCvsroot Return last CVS root
	\return true if match was found, false otherwise
*/
bool FindBestLastCvsroot(const char* path, CStr& lastCvsroot)
{
	bool res = false;

	CStr bestCvsrootPath;
	CStr bestCvsroot;

	const std::vector<CStr>& list = gLastCvsroot.GetList();
	for(std::vector<CStr>::const_iterator i = list.begin(); i != list.end(); ++i)
	{
		CStr key;
		CStr value;
		
		gLastCvsroot.Split((*i).c_str(), key, value);
		if( StrPathCmp(key, path) == 0 )
		{
			lastCvsroot = value;
			res = true;
			break;
		}
		
		std::string strKey = key.c_str();
		std::string strPath = path;

		strKey.erase(strKey.find_last_not_of(kPathDelimiter) + 1, std::string::npos);
		strPath.erase(strPath.find_last_not_of(kPathDelimiter) + 1, std::string::npos);
		
		std::transform(strKey.begin(), strKey.end(), strKey.begin(), tolower);
		std::transform(strPath.begin(), strPath.end(), strPath.begin(), tolower);

		if( strPath.find(strKey) != std::string::npos && strKey.length() > bestCvsrootPath.length() )
		{
			bestCvsrootPath = key;
			bestCvsroot = value;
		}
	}

	if( !res && !bestCvsroot.empty() )
	{
		lastCvsroot = bestCvsroot;
		res = true;
	}

	return res;
}

/*!
	Insert the mapping of last CVS root for a given path
	\param path Path
	\param cvsroot Last CVS root
*/
void InsertLastCvsroot(const char* path, const char* cvsroot)
{
	CStr mapValue;
	gLastCvsroot.Concatenate(mapValue, path, cvsroot);
	gLastCvsroot.Insert(mapValue);
}

/*!
	Find the best module name match for a given path
	\param path Path to find the match for
	\param lastModuleName Return last module name
	\return true if match was found, false otherwise
*/
bool FindBestLastModuleName(const char* path, CStr& lastModuleName)
{
	bool res = false;

	CStr bestModuleNamePath;
	CStr bestModuleName;

	const std::vector<CStr>& list = gLastModuleName.GetList();
	for(std::vector<CStr>::const_iterator i = list.begin(); i != list.end(); ++i)
	{
		CStr key;
		CStr value;
		
		gLastModuleName.Split((*i).c_str(), key, value);
		if( StrPathCmp(key, path) == 0 )
		{
			lastModuleName = value;
			res = true;
			break;
		}
		
		std::string strKey = key.c_str();
		std::string strPath = path;

		strKey.erase(strKey.find_last_not_of(kPathDelimiter) + 1, std::string::npos);
		strPath.erase(strPath.find_last_not_of(kPathDelimiter) + 1, std::string::npos);
		
		std::transform(strKey.begin(), strKey.end(), strKey.begin(), tolower);
		std::transform(strPath.begin(), strPath.end(), strPath.begin(), tolower);

		if( strPath.find(strKey) != std::string::npos && strKey.length() > bestModuleName.length() )
		{
			bestModuleNamePath = key;
			bestModuleName = value;
		}
	}

	if( !res && !bestModuleName.empty() )
	{
		lastModuleName = bestModuleName;
		res = true;
	}

	return res;
}

/*!
	Insert the mapping of last module name for a given path
	\param path Path
	\param moduleName Last module name
*/
void InsertLastModuleName(const char* path, const char* moduleName)
{
	CStr mapValue;
	gLastModuleName.Concatenate(mapValue, path, moduleName);
	gLastModuleName.Insert(mapValue);
}

/*!
	Helper method to migrate into a single CMString for cvsroot history
*/
void MigrateCvsrootList()
{
	for(int nIndex = 0; nIndex <= NUM_CVSROOT; nIndex++)
	{
		CStr cvsroot;
		char tmp[20];
		sprintf(tmp, "P_Cvsroot%d", nIndex);
		
#ifdef WIN32
		BYTE* ppData;
		UINT pBytes = 0;

		if( !AfxGetApp()->GetProfileBinary(PROFILE_NAME, tmp, &ppData, &pBytes) )
		{
			continue;
		}

		cvsroot = (char*)ppData;
		delete ppData;
#endif /* WIN32 */
		
#if qUnix
		/* remove unused entries */
		gOldCvsroots.Remove(tmp);
#endif // qUnix
	
#if qMacCvsPP
#endif // qMacCvsPP
		
		if( !cvsroot.empty() )
		{
			gOldCvsroots.Insert(cvsroot);
		}
	}
}

#ifdef WIN32
kWinVersion CvsPrefs::WhichVersion()
{
	DWORD dwVersion = GetVersion(); // Get major and minor version numbers of Windows
	DWORD dwWindowsMajorVersion =  (DWORD)(LOBYTE(LOWORD(dwVersion)));
	DWORD dwWindowsMinorVersion =  (DWORD)(HIBYTE(LOWORD(dwVersion)));
	DWORD dwBuild;

	// Get build numbers for Windows NT or Win32s
	if (dwVersion < 0x80000000)                // Windows NT
	{
		dwBuild = (DWORD)(HIWORD(dwVersion));
		return dwWindowsMajorVersion == 3 ? kWinNT35 : kWinNT4;
	}
	else if (dwWindowsMajorVersion < 4)        // Win16s
	{
		dwBuild = (DWORD)(HIWORD(dwVersion));
		return kWin16;
	}
	
	// Windows 95 -- No build numbers provided    dwBuild =  0;
	return kWin32;
}
#endif /* WIN32 */


//
// TODO: If we sometimes implement multi-threaded CVS/TCL macro 
// execution then we have to implement thread synchronization in
// IsXXXRunning and SetXXXRunning methods.
//
// NOTE! IsCvsRunning and SetCvsRunning methods are not yet
// used in Mac/Unix/WinCVS. Each of these version have had
// their own app.gCvsRunning variable. I haven't cleaned up
// the code and changed them to use this "one and only" 
// CvsRunning methods. 
//
bool CvsPrefs::IsCvsRunning(void) const
{ 
	return(fCvsRunningCounter > 0); 
}

int CvsPrefs::SetCvsRunning(bool launched)  
{ 
	if(launched) 
		fCvsRunningCounter++;
	else if(fCvsRunningCounter > 0) 
		fCvsRunningCounter--;

	return fCvsRunningCounter;		
}

bool CvsPrefs::IsTclFileRunning(void) const
{ 
	return(fTclFileRunningCounter > 0); 
}

int CvsPrefs::SetTclFileRunning(bool launched)  
{ 
	if(launched) 
		fTclFileRunningCounter++;
	else if(fTclFileRunningCounter > 0) 
		fTclFileRunningCounter--;

	return fTclFileRunningCounter;		
}

bool CvsPrefs::UseAltCvs(void) const
{
	return gUseAltCvs;
}

void CvsPrefs::SetUseAltCvs(bool useit)
{
	gUseAltCvs = useit;
}

void CvsPrefs::SetWhichCvs(const char* cvsPath)
{
	UStr appPath;

#ifdef WIN32
	CWincvsApp* pApp = (CWincvsApp*)AfxGetApp();
	pApp->GetAppModule(appPath);

	CString strAppPath(appPath);
	strAppPath.MakeLower();
	appPath = strAppPath;
#elif qUnix
	appPath << EXECLIB << "/" << "cvs";
#elif TARGET_RT_MAC_MACHO
	appPath = CMacCvsApp::gApp->GetAppPath();
#endif

	if( !appPath.empty() ) 
	{
		UStr value;
		gWhichCvs.Concatenate(value, appPath, cvsPath);
		gWhichCvs.Insert(value);
	} 
	else
	{
		gWhichCvs.Insert(cvsPath);
	}
}

const char* CvsPrefs::WhichCvs(UStr& path, bool userOnly) const
{
	path.clear();

	UStr appPath;

#ifdef WIN32
	CWincvsApp* pApp = (CWincvsApp*)AfxGetApp();
	pApp->GetAppModule(appPath);
	
	CString strAppPath(appPath);
	strAppPath.MakeLower();
	appPath = strAppPath;
#elif qUnix
	appPath << EXECLIB << "/" << "cvs";
#elif TARGET_RT_MAC_MACHO
	appPath = CMacCvsApp::gApp->GetAppPath();
#endif

	// search if beside the default cvs, the user defined one to use
	// with this application only.
	UStr candidate;
	if( !appPath.empty() )
	{
		UStr value;
		gWhichCvs.Concatenate(value, appPath, "");
		TMString<UStr>::list_t::const_iterator i = gWhichCvs.Find(value);
		if( i != gWhichCvs.end() )
		{
			UStr key;
			gWhichCvs.Split(*i, key, candidate);
		}
	}

	// verify this cvs exe is still there
	struct stat sb;
	if( !candidate.empty() && stat(candidate, &sb) != -1 && S_ISREG(sb.st_mode) )
	{
		path = candidate;
		return path.c_str();
	}

	// fallback to the default one.
	if( userOnly )
		return 0L;

#ifdef WIN32
	CWincvsApp* app = (CWincvsApp*)AfxGetApp();

	app->GetAppPath(path);
	if( !path.endsWith(kPathDelimiter) )
		path << kPathDelimiter;
	
	path << "cvsnt" << kPathDelimiter;
	path << (gCvsPrefs.WhichVersion() == kWin32 ? "cvs95.exe" : "cvs.exe");
#elif qUnix
	if( path.empty() )
		path = EXECLIB;

	if(!path.endsWith(kPathDelimiter))
		path << kPathDelimiter;

	path << "cvs";
#elif TARGET_RT_MAC_MACHO
	path = CMacCvsApp::gApp->GetAppResourcesPath();
	if(!path.endsWith(kPathDelimiter))
		path << kPathDelimiter;
	path << "cvsgui";
#endif

	return path.c_str();
}

SHOWCVS_TYPE CvsPrefs::ShowCvs(void) const
{
	return (SHOWCVS_TYPE)(int)gShowCvs;
}

void CvsPrefs::SetShowCvs(SHOWCVS_TYPE showCvs)
{
	gShowCvs = showCvs;
}
