jedioutcast/utils/Radiant/sourcesafe.cpp

1163 lines
27 KiB
C++
Raw Permalink Normal View History

2013-04-04 18:02:27 +00:00
// Filename:- sourcesafe.cpp
//
// (read the sourcesafe.h notes for extra info)
//
#include "stdafx.h"
// Microsoft's GUID stuff never seems to work properly, so...
//
#define DEFINE_GUID_THAT_WORKS(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#include <ocidl.h>
#include "oddbits.h"
#include "ssauto.h"
#include "ssauterr.h"
#include "sourcesafe.h"
// SourceSafe can sometimes take a LONG time to return from calls to it, so in order to let users know that your app
// hasn't crashed elsewhere it's a good idea to fill in these macros with something appropriate to your own code...
//
int iSourceSafeReferenceCount = 0;
void SourceSafe_Enter(LPCSTR psString)
{
// if (!iSourceSafeReferenceCount++)
{
CString str = psString;
str.Insert(0,"SourceSafe: ");
Sys_Printf(va("%s\n",str));
}
}
void SourceSafe_Leave(void)
{
// if (!--iSourceSafeReferenceCount)
{
//((CMainFrame*)AfxGetMainWnd())->StatusMessage
Sys_Printf("Ready\n");
}
}
#define _SS_ENTER(string) SourceSafe_Enter(string) //((CMainFrame*)AfxGetMainWnd())->StatusMessage("(Waiting for SourceSafe...)");
#define _SS_LEAVE() SourceSafe_Leave() //((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
// Coding note, generally the functions beginning "SS_" are called externally, and the ones beginning "_" are internal.
//
// Incidentally, the docs say:
//
// -----
// Be sure to link with the following libraries:
// user32.lib uuid.lib oleaut32.lib ole32.lib
// -----
//
//... but this appears not to be needed. I suspect stdafx.h may have something to do with that, so I'll leave
// that comment there in case this is ever used with standard windows code only
//
// replace these 3 with hacks to fit this module to BehavEd...
//
CString g_cstrSourceSafeINI = sEF1_SS_INI;
CString g_cstrSourceSafeProject = sEF1_SS_PROJECT;
BOOL g_bUseSourceSafe = true;
// remember to do a SysFreeString(<BSTR>) on the returned value when you've finished with it!
//
BSTR StringToBSTR(LPCSTR string)
{
OLECHAR* svalue = NULL;
BSTR bstrValue = NULL;
int iLen;
bool bFree_svalue = false;
if( (iLen = MultiByteToWideChar(CP_ACP, 0, string, -1, svalue, 0 )) != 1)
{
svalue = new OLECHAR[iLen];
bFree_svalue = true;
if( MultiByteToWideChar(CP_ACP, 0, string, -1, svalue, iLen ) == 0 )
{
ErrorBox( va("StringToBSTR: Error converting \"%s\" to BSTR", string));
}
}
else
{
svalue = L"";
}
bstrValue = SysAllocString(svalue);
if (svalue && bFree_svalue)
delete svalue;
return bstrValue;
}
// thanks for making us jump through yet more hoops, Microsoft...
//
// (actually there is a "legal" way to do this, in a similar way to above, but I can be arsed looking it up)
//
LPCSTR BSTRToString(BSTR pStrOLE_MyName)
{
static CString str;
str.Empty();
WORD *pw = (WORD *) pStrOLE_MyName;
char c;
do
{
c = LOBYTE(*pw++);
str+=va("%c",c); // hahahaha!
}
while (c);
return (LPCSTR) str;
}
IClassFactory *g_pClf = NULL;
IVSSDatabase *g_pVdb = NULL;
BSTR bstrPath = NULL;
BSTR bstrUName = NULL;
BSTR bstrUPass = NULL;
BSTR bstrProject= NULL;
//
bool gbDatabaseHeldOpen = false;
bool _OpenDatabase(bool bQuietErrors = false); // spot the retrofit <g>
bool _OpenDatabase(bool bQuietErrors)
{
if (gbDatabaseHeldOpen)
return true;
static bool bFirstTime = false;
if (!bFirstTime)
{
bFirstTime = true;
SS_Startup_OnceOnly();
}
_SS_ENTER("Opening...");
bool bReturn = false;
CLSID clsid;
bstrPath = StringToBSTR ( (LPCSTR) g_cstrSourceSafeINI ); // eg "\\\\RAVEND\\VSS\\SRCSAFE.INI"
bstrUName = SysAllocString(L""); // user name (eg) "scork" or "guest", but best left blank for auto.
bstrUPass = SysAllocString(L""); // user password, again leave blank
bstrProject = StringToBSTR ( (LPCSTR) g_cstrSourceSafeProject ); // eg. "$/StarTrek/BaseQ3/Maps"
CoInitialize(0);
if(S_OK == CLSIDFromProgID(L"SourceSafe", &clsid ))
{
if(S_OK == CoGetClassObject( clsid, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&g_pClf ))
{
if(S_OK == g_pClf->CreateInstance( NULL, IID_IVSSDatabase, (void **) &g_pVdb ))
{
if(S_OK == g_pVdb->Open(bstrPath, bstrUName, bstrUPass))
{
if (S_OK == g_pVdb->put_CurrentProject(bstrProject))
{
gbDatabaseHeldOpen = true;
bReturn = true;
}
else
{
if (!bQuietErrors)
ErrorBox( va("SourceSafe: Failed to set current project to \"%s\"",g_cstrSourceSafeProject));
}
}
else
{
if (!bQuietErrors)
ErrorBox( va("SourceSafe: Failed during open of INI file \"%s\"",g_cstrSourceSafeINI));
}
}
else
{
if (!bQuietErrors)
ErrorBox( "SourceSafe: Failed to create a database instance");
}
}
else
{
if (!bQuietErrors)
ErrorBox( "SourceSafe: Failed to get ClassFactory interface");
}
}
else
{
if (!bQuietErrors)
ErrorBox( "SourceSafe: Failed to get CLSID (GUID)");
}
_SS_LEAVE();
return bReturn;
}
// this should be called regardless of whether or not the _OpenDatabase function succeeded because it could
// have got part way through and only error'd later, so this frees up what it needs to
//
void _CloseDatabase()
{
if (gbDatabaseHeldOpen)
return;
_SS_ENTER("Closeing...");
if (g_pVdb)
{
g_pVdb->Release();
g_pVdb = NULL;
}
if (g_pClf)
{
g_pClf->Release();
g_pClf = NULL;
}
CoUninitialize();
// no need to check for NULL ptr for this function...
//
SysFreeString(bstrPath); bstrPath = NULL;
SysFreeString(bstrUName); bstrUName = NULL;
SysFreeString(bstrUPass); bstrUPass = NULL;
SysFreeString(bstrProject); bstrProject = NULL;
_SS_LEAVE();
}
// there isn't really any totally legal way to do this because Radiant doesn't have projects with files added to
// sourcesafe through menus etc in the same way as (eg) VC, so I'll just do a bit of a kludge for now. This should
// be updated for any other projects/files you want to handle yourself...
//
// currently I'm expecting files like:
//
// "Q:\\quake\\baseq3\\scripts\\borg.shader"
//
// .. which I convert by matching the "baseq3" part to produce:
//
// "$/StarTrek/BaseQ3/Shaders/borg3.shader"
//
// returns NULL on fail.
//
// BEHAVED NOTE:
//
// scripts pathnames are in usually in the form : "Q:\\quake\\baseq3\\real_scripts\\oz\\myborg.txt"
// sourcesafe pathnames are in the form : "$/StarTrek/real_scripts" downwards
//
// ... so I just need to search for the common part of the path ("real_scripts")...
//
LPCSTR FilenameToProjectName(LPCSTR path, bool bQuiet = false);
LPCSTR FilenameToProjectName(LPCSTR path, bool bQuiet)
{
static CString strPath;
strPath = path; // do NOT join to above line (or it only happens first time)
strPath.Replace("\\","/");
int loc = strPath.Find("/maps/");
if (loc>=0)
{
strPath = strPath.Mid(loc+6); // skip over "/maps/"
}
else
{
if (!bQuiet)
{
ErrorBox( va("SourceSafe: FilenameToProjectName(): Failed to convert \"%s\" to SS path",path));
}
return NULL;
}
strPath.Insert(0,va("%s/",(LPCSTR) g_cstrSourceSafeProject));
return (LPCSTR) strPath;
}
// return is how many versions were listed, AFAIK it should always be at least 1 (ie "created") for any valid SS item
//
int _ListVersions( IVSSDatabase* db, LPCSTR psPathSS, CString &strOutput )
{
_SS_ENTER("ListVersions...");
#define MAX_VERSIONS_TO_LIST 40 // otherwise these things get fucking *huge*!
int iVersions = 0;
BSTR bstrval;
char lpbuf[200];
char lpbuf2[200];
IVSSItem *vssi;
IVSSVersion *vers;
IVSSVersions *vx;
LPUNKNOWN lpunk;
IEnumVARIANT *ppvobj;
VARIANT st;
BSTR bstrValue;
int x;
ULONG fetched;
long lvnum;
HRESULT hr;
strOutput = "";
bstrValue = StringToBSTR(psPathSS);
if( S_OK == db->get_VSSItem(bstrValue, FALSE, &vssi) )
{
if( S_OK == vssi->get_Versions( 0l, &vx ) )
{
if( S_OK == vx->_NewEnum(&lpunk) )
{
if(!FAILED(lpunk->QueryInterface(IID_IEnumVARIANT, (void**)&ppvobj)))
{
vssi->get_Spec( &bstrval );
x = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1,
lpbuf, sizeof(lpbuf), NULL, NULL );
// strOutput = va("History of: %s\n", lpbuf );
// OutputDebugString(va("History of: %s\n", lpbuf ));
// strOutput+= "ACTION USER NAME VERSION NUMBER\n";
// OutputDebugString("ACTION USER NAME VERSION NUMBER\n");
do
{
ppvobj->Next( 1UL, &st, &fetched );
if( fetched != 0 )
{
if(!FAILED(hr = st.punkVal->QueryInterface(IID_IVSSVersion,(void**)&vers)))
{
vers->get_Action( &bstrval );
WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1,
lpbuf, sizeof(lpbuf), NULL, NULL );
vers->get_Username( &bstrval );
WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1,
lpbuf2, sizeof( lpbuf2 ), NULL, NULL );
vers->get_VersionNumber( &lvnum );
// OutputDebugString(va("%s %s %ld\n", lpbuf, lpbuf2, lvnum ));
// version numbers can go a bit weird, in that global labels have different version
// numbers, but since it's all presented in a backwards order I'd have to do a whole
// bunch of messy stuff putting them into arrays then scanning them from last to first
// to check version numbers were sequential, then zap any that weren't, so in the end
// I'm just going to to ignore version numbers for labels...
//
if (!strnicmp(lpbuf,"labeled",7))
{
strOutput += va("%s by user %s\n", lpbuf, lpbuf2, lvnum );
}
else
{
strOutput += va("%s by user %s (Ver. %ld)\n", lpbuf, lpbuf2, lvnum );
}
iVersions++;
vers->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListVersions(): Failed in QueryInterface(IID_IVSSVersion) for file \"$s\"",psPathSS ));
}
st.punkVal->Release();
}
} while( fetched != 0
#ifdef MAX_VERSIONS_TO_LIST
&& iVersions < MAX_VERSIONS_TO_LIST
#endif
);
ppvobj->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListVersions(): Failed in QueryInterface(IID_IEnumVARIANT) for file \"$s\"",psPathSS ));
}
lpunk->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListVersions(): Failed in _NewEnum() for file \"$s\"",psPathSS ));
}
vx->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListVersions(): Failed in get_Versions() for file \"$s\"",psPathSS ));
}
vssi->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListVersions(): Failed in get_VSSItem() for file \"$s\"",psPathSS ));
}
SysFreeString(bstrValue);
#ifdef MAX_VERSIONS_TO_LIST
if (iVersions >= MAX_VERSIONS_TO_LIST)
{
strOutput += va("\n\n(History list capped to %d entries)",MAX_VERSIONS_TO_LIST);
}
#endif
_SS_LEAVE();
return iVersions;
}
// int return is number of users who have this file checked out, unless param 'psNameToCheckForMatch' is NZ,
// in which case return is a bool as to whether or not the name in question (usually our name) has the file checked out...
//
// ( output list is CR-delineated )
//
// return is -1 for error, else checkout count, else 1 for true if doing a me-match via last param
//
int _ListCheckOuts( IVSSDatabase* db, LPCSTR psPathSS, CString &strOutput, LPCSTR psNameToCheckForMatch )
{
_SS_ENTER("ListCheckOuts...");
int iCheckOuts = -1;
BSTR bstrval;
char lpbuf[200];
char lpbuf2[200];
IVSSItem *vssi;
IVSSVersion *vers;
IVSSCheckouts *vx;
LPUNKNOWN lpunk;
IEnumVARIANT *ppvobj;
VARIANT st;
BSTR bstrValue;
int x;
ULONG fetched;
HRESULT hr;
strOutput = "";
bstrValue = StringToBSTR( psPathSS );
if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ))
{
if( S_OK == vssi->get_Checkouts( &vx ) )
{
if( S_OK == vx->_NewEnum(&lpunk) )
{
if(!FAILED(lpunk->QueryInterface(IID_IEnumVARIANT, (void**)&ppvobj)))
{
vssi->get_Spec( &bstrval );
x = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, lpbuf, sizeof(lpbuf), NULL, NULL );
// OutputDebugString(va("Checkout status of: %s\n", lpbuf ));
// strOutput = va("Checkout status of: %s\n", lpbuf ));
do
{
ppvobj->Next( 1UL, &st, &fetched );
if( fetched != 0 )
{
if(!FAILED(hr = st.punkVal->QueryInterface(IID_IVSSCheckout,(void**)&vers)))
{
// vers->get_Action( &bstrval );
// WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1,
// lpbuf, sizeof(lpbuf), NULL, NULL );
vers->get_Username( &bstrval );
WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1,
lpbuf2, sizeof( lpbuf2 ), NULL, NULL );
// vers->get_VersionNumber( &lvnum );
// OutputDebugString(va("%s %s %ld\n", lpbuf, lpbuf2, lvnum ));
//OutputDebugString(va("%s\n", lpbuf2));
strOutput += va("%s\n", lpbuf2);
if ( psNameToCheckForMatch )
{
if (!stricmp(lpbuf2,psNameToCheckForMatch) ) // is stricmp too paranoid?
{
iCheckOuts = 1; // hardwire: checked out by me = true
}
}
else
{
if (iCheckOuts == -1) // first time - change error-flag to valid count?
{
iCheckOuts = 1;
}
else
{
iCheckOuts++;
}
}
vers->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in QueryInterface(IID_IVSSCheckout) for file \"$s\"",psPathSS ));
}
st.punkVal->Release();
}
} while( fetched != 0 );
ppvobj->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in QueryInterface(IID_IEnumVARIANT) for file \"$s\"",psPathSS ));
}
lpunk->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in _NewEnum() for file \"$s\"",psPathSS ));
}
vx->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in get_Checkouts() for file \"$s\"",psPathSS ));
}
vssi->Release();
}
else
{
ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in get_VSSItem() for file \"$s\"",psPathSS ));
}
SysFreeString(bstrValue);
_SS_LEAVE();
return iCheckOuts;
}
bool _IsCheckedOut( IVSSDatabase* db, LPCSTR psPathSS )
{
_SS_ENTER("Is checked out?...");
IVSSItem* vssi;
bool bReturn = false;
BSTR bstrValue = StringToBSTR( psPathSS );
if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) )
{
long lStatus;
if( S_OK == vssi->get_IsCheckedOut( &lStatus ) )
{
bReturn = !!lStatus;
}
else
{
ErrorBox( va("SourceSafe: Error during _IsCheckedOut() for file \"%s\"",psPathSS ));
}
vssi->Release();
}
else
{
ErrorBox( va( "SourceSafe: _IsCheckedOut(): Error during get_VSSItem() for file \"%s\"",psPathSS ));
}
SysFreeString( bstrValue );
_SS_LEAVE();
return bReturn;
}
bool _CheckOut( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk )
{
_SS_ENTER("Check Out...");
IVSSItem* vssi;
bool bReturn = false;
BSTR bstrValue = StringToBSTR ( psPathSS );
BSTR bsComment = SysAllocString( L"" );
if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) )
{
BSTR bsLocal = StringToBSTR(psPathDisk);
if( S_OK == vssi->Checkout( bsComment, bsLocal, 0 ))
{
bReturn = true;
}
else
{
ErrorBox( va( "SourceSafe: Error during _CheckOut() for file \"%s\"",psPathSS ));
}
SysFreeString( bsLocal );
vssi->Release();
}
else
{
ErrorBox( va( "SourceSafe: _CheckOut(): Error during get_VSSItem() for file \"%s\"",psPathSS ));
}
SysFreeString( bsComment );
SysFreeString( bstrValue );
_SS_LEAVE();
return bReturn;
}
bool _Add( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk )
{
_SS_ENTER("Add...");
IVSSItem *vssi,*vssi2;
bool bReturn = false;
BSTR bstrValue = StringToBSTR ( psPathSS );
BSTR bsComment = SysAllocString( L"" );
CString strPathRelativeToProject = psPathSS;
strPathRelativeToProject.Replace("/","\\");
Filename_RemoveFilename(strPathRelativeToProject);
strPathRelativeToProject.Replace("\\","/");
// ( eg: "$/StarTrek/BaseQ3/real_scripts/ste" )
BSTR bstrProject = StringToBSTR(strPathRelativeToProject);
// ensure SS path exists...
//
CString strBuiltUpProject;
CString strSourceProject(strPathRelativeToProject);
while (!strSourceProject.IsEmpty())
{
CString strProjectPathSoFar(strBuiltUpProject);
CString strNewSubProj;
int iLoc = strSourceProject.Find("/");
if (iLoc >=0)
{
strNewSubProj = strSourceProject.Left(iLoc);
}
else
{
strNewSubProj = strSourceProject;
strSourceProject = "";
}
strBuiltUpProject += strBuiltUpProject.IsEmpty() ? "" : "/";
strBuiltUpProject += strNewSubProj;
strSourceProject = strSourceProject.Mid(iLoc+1);
if (strBuiltUpProject != "$") // get_VSSItem() doesn't work on root, and it'll always be there anyway
{
BSTR bstrTempProjectPath = StringToBSTR(strBuiltUpProject);
{
if( S_OK != db->get_VSSItem( bstrTempProjectPath, FALSE, &vssi ) )
{
// need to build this new part of the path...
//
BSTR bstrProjectPathSoFar = StringToBSTR(strProjectPathSoFar);
{
if( S_OK == db->get_VSSItem( bstrProjectPathSoFar, FALSE, &vssi ) )
{
BSTR bstrNewSubProj = StringToBSTR(strNewSubProj);
{
if (S_OK == vssi->NewSubproject(bstrNewSubProj, bsComment, &vssi2))
{
// yay!
}
else
{
ErrorBox(va("SS_Add(): Error creating subproject \"%s\" in \"%s\"!",(LPCSTR) strNewSubProj, (LPCSTR) strProjectPathSoFar));
break;
}
}
SysFreeString( bstrNewSubProj );
}
}
SysFreeString( bstrProjectPathSoFar );
}
}
SysFreeString( bstrTempProjectPath );
}
}
if( S_OK == db->get_VSSItem( bstrProject, FALSE, &vssi ) )
{
BSTR bsLocal = StringToBSTR( psPathDisk );
// if( S_OK == vssi->Checkin ( bsComment, bsLocal, 0 ))
if( S_OK == vssi->Add ( bsLocal, bsComment, 0, &vssi2))
{
vssi2->Release();
bReturn = true;
}
else
{
ErrorBox( va( "SourceSafe: Error during _Add() for file \"%s\"",psPathSS ));
}
SysFreeString( bsLocal );
vssi->Release();
}
else
{
ErrorBox( va( "SourceSafe: _CheckIn(): Error during get_VSSItem() for file \"%s\"",psPathSS ));
}
SysFreeString( bsComment );
SysFreeString( bstrValue );
_SS_LEAVE();
return bReturn;
}
bool _CheckIn( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk )
{
_SS_ENTER("Check In...");
IVSSItem* vssi;
bool bReturn = false;
BSTR bstrValue = StringToBSTR ( psPathSS );
BSTR bsComment = SysAllocString( L"" );
if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) )
{
BSTR bsLocal = StringToBSTR( psPathDisk );
if( S_OK == vssi->Checkin ( bsComment, bsLocal, 0 ))
{
bReturn = true;
}
else
{
ErrorBox( va( "SourceSafe: Error during _CheckIn() for file \"%s\"",psPathSS ));
}
SysFreeString( bsLocal );
vssi->Release();
}
else
{
ErrorBox( va( "SourceSafe: _CheckIn(): Error during get_VSSItem() for file \"%s\"",psPathSS ));
}
SysFreeString( bsComment );
SysFreeString( bstrValue );
_SS_LEAVE();
return bReturn;
}
bool _UndoCheckOut( IVSSDatabase* db, LPCSTR psPathSS )
{
_SS_ENTER("Undo Check out...");
IVSSItem* vssi;
bool bReturn = false;
BSTR bstrValue = StringToBSTR ( psPathSS );
BSTR bsComment = SysAllocString( L"" );
if( S_OK == db->get_VSSItem(bstrValue, FALSE, &vssi ) )
{
BSTR bsLocal = SysAllocString( L"" ); // this doesn't seem to need a path to the disk file, presumably it works it out
if( S_OK == vssi->UndoCheckout( bsLocal, 0 ))
{
bReturn = true;
}
else
{
ErrorBox( va("SourceSafe: Error during _UndoCheckOut() for file \"%s\"",psPathSS));
}
SysFreeString(bsLocal);
vssi->Release();
}
else
{
ErrorBox( va( "SourceSafe: _UndoCheckOut(): Error during get_VSSItem() for file \"%s\"",psPathSS ));
}
SysFreeString( bsComment );
SysFreeString( bstrValue );
_SS_LEAVE();
return bReturn;
}
// these routines called externally...
//
bool SS_IsUnderSourceControl( LPCSTR psPathedFilename )
{
CWaitCursor waitcursor;
bool bReturn = false;
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName(psPathedFilename, true); // bQuiet
if (psSSName )
{
if (_OpenDatabase() )
{
_SS_ENTER("Checking if item is under control");
IVSSItem* vssi;
BSTR bstrValue = StringToBSTR ( psSSName );
if( S_OK == g_pVdb->get_VSSItem( bstrValue, FALSE, &vssi ) )
{
vssi->Release();
bReturn = true;
}
else
{
// item doesn't appear to be under SS control
}
SysFreeString( bstrValue );
_SS_LEAVE();
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_Add(LPCSTR psPathedFilename)
{
CWaitCursor waitcursor;
bool bReturn = false;
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName(psPathedFilename);
if ( psSSName )
{
if ( _OpenDatabase() )
{
if (_Add( g_pVdb, psSSName, psPathedFilename ) )
{
bReturn = true;
}
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_CheckIn(LPCSTR psPathedFilename)
{
CWaitCursor waitcursor;
bool bReturn = false;
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName(psPathedFilename);
if ( psSSName )
{
if ( _OpenDatabase() )
{
if ( _CheckIn( g_pVdb, psSSName, psPathedFilename ) )
{
bReturn = true;
}
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_CheckOut( LPCSTR psPathedFilename )
{
CWaitCursor waitcursor;
bool bReturn = false;
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName(psPathedFilename);
if ( psSSName )
{
if ( _OpenDatabase() )
{
if ( _CheckOut( g_pVdb, psSSName, psPathedFilename ) )
{
bReturn = true;
}
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_UndoCheckOut( LPCSTR psPathedFilename )
{
CWaitCursor waitcursor;
bool bReturn = false;
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName(psPathedFilename);
if ( psSSName )
{
if ( _OpenDatabase() )
{
if ( _UndoCheckOut( g_pVdb, psSSName ) )
{
bReturn = true;
}
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_IsCheckedOut( LPCSTR psPathedFilename )
{
CWaitCursor waitcursor;
bool bReturn = false;
ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName(psPathedFilename);
if ( psSSName )
{
if ( _OpenDatabase() )
{
if ( _IsCheckedOut( g_pVdb, psSSName ) )
{
bReturn = true;
}
}
_CloseDatabase();
}
}
return bReturn;
}
// similar to above, but we're only interested if it's checked out by us...
//
// (actually after I wrote this I found a more legal way to do it, but this works...)
//
bool SS_IsCheckedOutByMe( LPCSTR psPathedFilename )
{
CWaitCursor waitcursor;
bool bReturn = false;
ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName( psPathedFilename );
if ( psSSName )
{
if ( _OpenDatabase() )
{
_SS_ENTER("Is checked out by me?");
CString junk;
BSTR pStrOLE_MyName;
if (S_OK == g_pVdb->get_UserName(&pStrOLE_MyName))
{
int iCount = _ListCheckOuts( g_pVdb, psSSName, junk, BSTRToString( pStrOLE_MyName) );
if ( iCount == 1)
{
bReturn = true;
}
}
else
{
ErrorBox( "SourceSafe: SS_IsCheckedOutByMe(): Failed in get_UserName()" );
}
_SS_LEAVE();
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_ListVersions( LPCSTR psPathedFilename, CString &strOutput )
{
CWaitCursor waitcursor;
bool bReturn = false;
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName( psPathedFilename );
if ( psSSName )
{
if ( _OpenDatabase() )
{
if ( _ListVersions( g_pVdb, psSSName, strOutput ))
{
bReturn = true;
}
}
_CloseDatabase();
}
}
return bReturn;
}
// if false (error) return, don't rely on a valid iCount!
//
bool SS_ListCheckOuts( LPCSTR psPathedFilename, CString &strOutput, int &iCount )
{
CWaitCursor waitcursor;
bool bReturn = false;
ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise
if ( SS_FunctionsAvailable() )
{
LPCSTR psSSName = FilenameToProjectName( psPathedFilename );
if ( psSSName )
{
if ( _OpenDatabase() )
{
iCount = _ListCheckOuts( g_pVdb, psSSName, strOutput, NULL );
if (iCount != -1)
{
bReturn = true; // strictly speaking this may not be true, but if (later: hmmmm, what was i going to write here? <g>)
}
}
_CloseDatabase();
}
}
return bReturn;
}
bool SS_FunctionsAvailable( void )
{
bool bReturn = false;
if ( g_bUseSourceSafe )
{
if (_OpenDatabase(true)) // bool bQuietErrors
{
bReturn = true;
}
_CloseDatabase();
}
return bReturn;
}
// this is a pretty tacky, but for now... :-)
//
bool SS_SetupOk( void )
{
CWaitCursor waitcursor;
bool g_bUseSourceSafe_PREV = g_bUseSourceSafe;
g_bUseSourceSafe = true;
bool bReturn = SS_FunctionsAvailable();
g_bUseSourceSafe = g_bUseSourceSafe_PREV;
return bReturn;
}
void SS_Startup_OnceOnly(void)
{
// nothing at the moment...
}
void SS_Shutdown_OnceOnly(void)
{
gbDatabaseHeldOpen = false;
_CloseDatabase();
}
void SS_LoadFromRegistry(void)
{
char sBuffer[256];
LONG
lSize = sizeof(sBuffer);
if (LoadRegistryInfo("Radiant::SS_INI", &sBuffer, &lSize))
g_cstrSourceSafeINI = sBuffer;
lSize = sizeof(sBuffer);
if (LoadRegistryInfo("Radiant::SS_Project", &sBuffer, &lSize))
g_cstrSourceSafeProject = sBuffer;
lSize = sizeof(sBuffer);
if (LoadRegistryInfo("Radiant::SS_Use", &sBuffer, &lSize))
g_bUseSourceSafe = !!strlen(sBuffer); // any length string = true
}
void SS_SaveToRegistry(void)
{
char sBuffer[256];
strcpy(sBuffer,g_cstrSourceSafeINI);
SaveRegistryInfo("Radiant::SS_INI", &sBuffer, sizeof(sBuffer));
strcpy(sBuffer,g_cstrSourceSafeProject);
SaveRegistryInfo("Radiant::SS_Project", &sBuffer, sizeof(sBuffer));
strcpy(sBuffer,g_bUseSourceSafe?"true":""); // zero-len string for false!
SaveRegistryInfo("Radiant::SS_Use", &sBuffer, sizeof(sBuffer));
}
/////////////////// eof /////////////////