You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
609 lines
13 KiB
609 lines
13 KiB
// WebChangelistEditor.cpp : Implementation of CWebChangelistEditor
|
|
|
|
#include "stdafx.h"
|
|
#include "WebChangelistEditor.h"
|
|
#include <fcntl.h>
|
|
|
|
|
|
// CWebChangelistEditor
|
|
|
|
|
|
STDMETHODIMP CWebChangelistEditor::Initialize(BSTR ChangelistKey, BOOL* Result)
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD cbData = MAX_PATH * sizeof(WCHAR);
|
|
DWORD dwDatatype;
|
|
|
|
// Initialize the Result return value to FALSE:
|
|
if (Result == NULL)
|
|
return E_POINTER;
|
|
|
|
*Result = FALSE;
|
|
|
|
// Put our Key/Value name into a CComBSTR
|
|
m_ChangelistKey = ChangelistKey;
|
|
|
|
// Make sure we can't be initialized twice:
|
|
if (m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
// Sanity check the ChangelistKey argument.
|
|
// It must be 40 characters, null-terminated, containing only hex digits.
|
|
if (m_ChangelistKey.Length() != 40)
|
|
goto Done;
|
|
if (m_ChangelistKey[40] != L'\0')
|
|
goto Done;
|
|
if (wcsspn(m_ChangelistKey, L"ABCDEFabcdef1234567890") != 40)
|
|
goto Done;
|
|
|
|
// Open our Key:
|
|
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
g_wszRegKey,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey) != ERROR_SUCCESS)
|
|
{
|
|
hKey = NULL;
|
|
goto Done;
|
|
}
|
|
|
|
// Read the specified subkey:
|
|
if ((RegQueryValueExW(hKey,
|
|
m_ChangelistKey,
|
|
0,
|
|
&dwDatatype,
|
|
(LPBYTE)m_wszCLFilename,
|
|
&cbData) != ERROR_SUCCESS) ||
|
|
(dwDatatype != REG_SZ))
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
// Now that we have the filename and key read successfully
|
|
// we can parse the file and populate all the properties of this object.
|
|
*Result = m_fInitialized = _ReadFile();
|
|
|
|
Done:
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
if (!m_fInitialized)
|
|
{
|
|
// If we failed, clean up nicely.
|
|
_WipeRegEntries();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::Save()
|
|
{
|
|
FILE *CLFile = NULL;
|
|
IFilesAndActions *pIFiles = NULL;
|
|
IFileAndAction *pIFile = NULL;
|
|
long count;
|
|
VARIANT varTemp;
|
|
CComBSTR bstrFilename;
|
|
CComBSTR bstrAction;
|
|
BOOL fEnabled;
|
|
HKEY hKey;
|
|
|
|
// Save can't be called unless we've been properly initialized
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
// Open the file write-only.
|
|
// This clears the file contents.
|
|
CLFile = _wfopen(m_wszCLFilename, L"wt");
|
|
if (CLFile == NULL)
|
|
goto Done;
|
|
|
|
// Print a much shortened comment block:
|
|
fwprintf(CLFile, L"# Source Depot Changelist.\n");
|
|
// Print the simple stuff
|
|
fwprintf(CLFile, L"\nChange:\t%s\n", m_Change);
|
|
if (_wcsicmp(m_Status, L"new") != 0)
|
|
{
|
|
// Only print the Date line if Status != "new"
|
|
fwprintf(CLFile, L"\nDate:\t%s\n", m_Date);
|
|
}
|
|
fwprintf(CLFile, L"\nClient:\t%s\n", m_Client);
|
|
fwprintf(CLFile, L"\nUser:\t%s\n", m_User);
|
|
fwprintf(CLFile, L"\nStatus:\t%s\n", m_Status);
|
|
fwprintf(CLFile, L"\nDescription:\n");
|
|
fwprintf(CLFile, L"\t%s\n", m_Description);
|
|
fwprintf(CLFile, L"\nFiles:\n");
|
|
|
|
// Get the FilesAndActions interface
|
|
if (FAILED(m_Files->QueryInterface<IFilesAndActions>(&pIFiles)))
|
|
goto Done; // This shouldn't ever fail.
|
|
|
|
varTemp.vt = VT_EMPTY;
|
|
|
|
// Loop over the entries and remove each entry as we print it
|
|
while (SUCCEEDED(pIFiles->get_Count(&count)) &&
|
|
(count >= 1))
|
|
{
|
|
if (SUCCEEDED(pIFiles->get_Item(1, &varTemp)) && // Get item
|
|
(varTemp.vt == VT_DISPATCH) && // Check Variant type
|
|
(pIFile = (IFileAndAction*)varTemp.pdispVal) && // <-- Yes, this is an assignment
|
|
SUCCEEDED(pIFile->get_Filename(&bstrFilename)) &&
|
|
SUCCEEDED(pIFile->get_Action(&bstrAction)) &&
|
|
SUCCEEDED(pIFile->get_Enabled(&fEnabled)))
|
|
{
|
|
// If it's enabled, print the complete line.
|
|
if (fEnabled)
|
|
fwprintf(CLFile, L"\t%s\t# %s\n", bstrFilename, bstrAction);
|
|
// Clean up for the next pass
|
|
//pIFile->Release();
|
|
pIFile = NULL;
|
|
VariantClear(&varTemp);
|
|
pIFiles->Remove(1);
|
|
}
|
|
else
|
|
break; // This should not happen.
|
|
} // End of while loop
|
|
|
|
|
|
Done:
|
|
// OK, we're closing. Un-Initialize the thing.
|
|
m_fInitialized = FALSE;
|
|
|
|
// Close the file
|
|
if (CLFile)
|
|
fclose(CLFile);
|
|
|
|
// Release the FilesAndActions interface
|
|
if (pIFiles)
|
|
pIFiles->Release();
|
|
|
|
// Delete the registry value we used.
|
|
// This may trigger the waiting executable to continue, so do
|
|
// this _after_ saving and closing the file.
|
|
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
g_wszRegKey,
|
|
0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&hKey) == ERROR_SUCCESS)
|
|
{
|
|
RegDeleteValueW(hKey, m_ChangelistKey);
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CWebChangelistEditor::_ReadFile(void)
|
|
{
|
|
BOOL fRetVal = FALSE;
|
|
FILE *CLFile = NULL;
|
|
IFilesAndActions *pIFiles = NULL;
|
|
WCHAR wszBuffer[500];
|
|
WCHAR wszBuffer2[500];
|
|
WCHAR wszBuffer3[50];
|
|
long count;
|
|
DWORD dwState = 0; // State of parsing engine
|
|
|
|
|
|
// Ensure that we don't have any old Files entries
|
|
if (FAILED(m_Files->QueryInterface<IFilesAndActions>(&pIFiles)))
|
|
return FALSE;
|
|
|
|
if (FAILED(pIFiles->get_Count(&count)))
|
|
goto Done;
|
|
|
|
if (count != 0)
|
|
goto Done;
|
|
|
|
// Open the file read-only
|
|
CLFile = _wfopen(m_wszCLFilename, L"rt");
|
|
if (CLFile == NULL)
|
|
goto Done;
|
|
|
|
while (fwscanf(CLFile, L"%499[^\n]%*[\n]", &wszBuffer) == 1)
|
|
{
|
|
// Expect a comment block at the top of the file
|
|
switch (dwState)
|
|
{
|
|
case 0: // Comment block at top of file
|
|
if (wszBuffer[0] == L'#')
|
|
{
|
|
// Skip each comment line
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Stop expecting a comment block
|
|
dwState++;
|
|
// Fall through to status=1 below
|
|
}
|
|
case 1: // Change field
|
|
if (wcsncmp(wszBuffer, L"Change:\t", 8) == 0)
|
|
{
|
|
// Store the Change string:
|
|
m_Change = &wszBuffer[8];
|
|
// move on:
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 2: // Date field
|
|
if (wcsncmp(wszBuffer, L"Date:\t", 6) == 0)
|
|
{
|
|
// Store the Date string:
|
|
m_Date = &wszBuffer[6];
|
|
// move on:
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Maybe the Date line is missing. Skip it.
|
|
dwState++;
|
|
// Fall through to status=3 below
|
|
}
|
|
case 3: // Client field
|
|
if (wcsncmp(wszBuffer, L"Client:\t", 8) == 0)
|
|
{
|
|
// Store the Client string:
|
|
m_Client = &wszBuffer[8];
|
|
// move on:
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 4: // User field
|
|
if (wcsncmp(wszBuffer, L"User:\t", 6) == 0)
|
|
{
|
|
// Store the User string:
|
|
m_User = &wszBuffer[6];
|
|
// move on:
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 5: // Status field
|
|
if (wcsncmp(wszBuffer, L"Status:\t", 8) == 0)
|
|
{
|
|
// Store the Status string:
|
|
m_Status = &wszBuffer[8];
|
|
// move on:
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 6: // Description field name
|
|
if (wcscmp(wszBuffer, L"Description:") == 0)
|
|
{
|
|
// Found it, but the actual Description is on the next line
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 7: // Description field value
|
|
if (wszBuffer[0] == L'\t')
|
|
{
|
|
// Store the Description string:
|
|
m_Description = &wszBuffer[1];
|
|
// move on:
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 8: // Files field name
|
|
if (wcscmp(wszBuffer, L"Files:") == 0)
|
|
{
|
|
// Found it, but the actual Files and Actions are on the following lines
|
|
dwState++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
case 9: // Files and Actions field values
|
|
if (swscanf(wszBuffer, L"\t%499[^\t]\t# %49s",
|
|
wszBuffer2, wszBuffer3) == 2)
|
|
{
|
|
// Add the new FileAndAction data
|
|
if (_AddFileAndAction(pIFiles, wszBuffer2, wszBuffer3) == FALSE)
|
|
{
|
|
// Unable to add data. Nothing we can do about it.
|
|
goto Done;
|
|
}
|
|
|
|
// move on:
|
|
break; // This is the final state. No increment.
|
|
}
|
|
else
|
|
{
|
|
// Invalid file.
|
|
goto Done;
|
|
}
|
|
} // end of case statement
|
|
} // end of while loop
|
|
|
|
if ((dwState == 7) || (dwState == 9))
|
|
{
|
|
// We got to the Description section, so we completed parsing successfully.
|
|
fRetVal = TRUE;
|
|
}
|
|
Done:
|
|
if ((dwState == 9) && (fRetVal == FALSE))
|
|
{
|
|
// We might have to remove some failed files entries
|
|
while (SUCCEEDED(pIFiles->get_Count(&count)) &&
|
|
(count >= 1))
|
|
{
|
|
if (FAILED(pIFiles->Remove(1)))
|
|
break;
|
|
}
|
|
}
|
|
if (pIFiles)
|
|
pIFiles->Release();
|
|
if (CLFile)
|
|
fclose(CLFile);
|
|
return fRetVal;
|
|
}
|
|
|
|
BOOL CWebChangelistEditor::_AddFileAndAction(IFilesAndActions *pIFiles, WCHAR* wszFilename, WCHAR* wszAction)
|
|
{
|
|
CComBSTR bstrFile = wszFilename;
|
|
CComBSTR bstrAction = wszAction;
|
|
IFileAndAction *pIFile = NULL;
|
|
CComObject<CFileAndAction> *pBase = NULL;
|
|
CComVariant varTemp;
|
|
|
|
// Create a new FileAndAction object
|
|
if (FAILED(CComObject<CFileAndAction>::CreateInstance(&pBase)))
|
|
{
|
|
// Unable to create instance. Nothing we can do about it.
|
|
return FALSE;
|
|
}
|
|
if (FAILED(pBase->QueryInterface<IFileAndAction>(&pIFile)))
|
|
{
|
|
// Unable to query inferface. Nothing we can do about it.
|
|
return FALSE;
|
|
}
|
|
|
|
// Add the data to the new object
|
|
if (FAILED(pIFile->put_Action(bstrAction)) ||
|
|
FAILED(pIFile->put_Filename(bstrFile)))
|
|
{
|
|
// Unable to add data. Nothing we can do about it.
|
|
pIFile->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Put the Interface ptr into a Variant, and release the old Interface ptr
|
|
varTemp = (IDispatch*)pIFile;
|
|
pIFile->Release();
|
|
|
|
// Add the new object to the collection
|
|
if (FAILED(pIFiles->Add(varTemp)))
|
|
{
|
|
// Unable to add data. Nothing we can do about it.
|
|
return FALSE;
|
|
}
|
|
|
|
// Success
|
|
return TRUE;
|
|
}
|
|
|
|
void CWebChangelistEditor::_WipeRegEntries(void)
|
|
{
|
|
HKEY hKey = NULL;
|
|
WCHAR wszValueName[41];
|
|
DWORD cchValueName = 41;
|
|
DWORD dwDatatype;
|
|
DWORD dwIndex;
|
|
HRESULT hr;
|
|
|
|
// Open our Key:
|
|
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
g_wszRegKey,
|
|
0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&hKey) != ERROR_SUCCESS)
|
|
{
|
|
hKey = NULL;
|
|
goto Done;
|
|
}
|
|
|
|
// For each value under that is named a 40-digit hex value,
|
|
// Delete it.
|
|
dwIndex = 0;
|
|
while ((hr = RegEnumValueW(hKey,
|
|
dwIndex,
|
|
wszValueName,
|
|
&cchValueName,
|
|
0,
|
|
&dwDatatype,
|
|
NULL, NULL)) != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
if (FAILED(hr))
|
|
{
|
|
// This is most likely because the buffers are the wrong size
|
|
// but we don't care about the key unless we can read it into
|
|
// the buffers we've got, so just move on.
|
|
dwIndex++;
|
|
cchValueName = 41; // Set it back to the size of the buffer.
|
|
continue;
|
|
}
|
|
// Check the datatype:
|
|
if (dwDatatype != REG_SZ)
|
|
{
|
|
dwIndex++;
|
|
cchValueName = 41; // Set it back to the size of the buffer.
|
|
continue;
|
|
}
|
|
// Check the name:
|
|
// It must be 40 characters, containing only hex digits.
|
|
if ((cchValueName != 40) ||
|
|
(wcsspn(wszValueName, L"ABCDEFabcdef1234567890") != 40))
|
|
{
|
|
dwIndex++;
|
|
cchValueName = 41; // Set it back to the size of the buffer.
|
|
continue;
|
|
}
|
|
// If all the above checks succeeded, delete the value.
|
|
RegDeleteValueW(hKey, wszValueName);
|
|
// Since we've changed the indexing, start over:
|
|
cchValueName = 41; // Set it back to the size of the buffer.
|
|
dwIndex = 0;
|
|
}
|
|
|
|
Done:
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_Change(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_Change.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::put_Change(BSTR newVal)
|
|
{
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
m_Change = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_Date(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_Date.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::put_Date(BSTR newVal)
|
|
{
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
m_Date = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_Client(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_Client.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::put_Client(BSTR newVal)
|
|
{
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
m_Client = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_User(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_User.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::put_User(BSTR newVal)
|
|
{
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
m_User = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_Status(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_Status.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::put_Status(BSTR newVal)
|
|
{
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
m_Status = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_Description(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_Description.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::put_Description(BSTR newVal)
|
|
{
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
m_Description = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CWebChangelistEditor::get_Files(IFilesAndActions** pVal)
|
|
{
|
|
if (pVal == NULL)
|
|
return E_POINTER;
|
|
if (!m_fInitialized)
|
|
return E_FAIL;
|
|
|
|
return m_Files->QueryInterface<IFilesAndActions>(pVal);
|
|
}
|
|
|