mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
515 lines
12 KiB
C++
515 lines
12 KiB
C++
/*
|
|
* regexpl - Console Registry Explorer
|
|
*
|
|
* Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net>
|
|
*
|
|
* 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; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
// Completion.cpp : Defines the completion related functions.
|
|
#include "ph.h"
|
|
#include "RegistryKey.h"
|
|
#include "RegistryTree.h"
|
|
|
|
#define COMPLETION_BUFFER_SIZE 4096
|
|
|
|
extern CRegistryTree Tree;
|
|
|
|
BOOL g_blnCompletionCycle = TRUE;
|
|
|
|
class CCompletionMatch
|
|
{
|
|
public:
|
|
CCompletionMatch()
|
|
{
|
|
m_pNext = m_pPrev = NULL;
|
|
m_pszText = NULL;
|
|
};
|
|
|
|
BOOL Init(const TCHAR *pszText)
|
|
{
|
|
// find the offset to "unique" part
|
|
const TCHAR *pszUnique = _tcsrchr(pszText,_T('\\'));
|
|
pszUnique = pszUnique?(pszUnique+1):pszText;
|
|
BOOL b = _tcschr(pszUnique,_T(' ')) != NULL; // has it spaces in it ???
|
|
size_t s = _tcslen(pszText);
|
|
|
|
if (m_pszText)
|
|
delete[] m_pszText;
|
|
|
|
m_pszText = new (std::nothrow) TCHAR [s+(b?3:1)]; // if we have spaces in unique part, we need 2 addtional chars for "
|
|
|
|
if (!m_pszText)
|
|
return FALSE;
|
|
|
|
ASSERT(pszText <= pszUnique);
|
|
s = pszUnique - pszText; // calculate offset of the unique part
|
|
if (s)
|
|
_tcsncpy(m_pszText,pszText,s);
|
|
|
|
if (b)
|
|
m_pszText[s++] = _T('\"');
|
|
|
|
_tcscpy(m_pszText+s,pszUnique);
|
|
|
|
if (b)
|
|
_tcscat(m_pszText,_T("\""));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
~CCompletionMatch()
|
|
{
|
|
if (m_pszText)
|
|
delete[] m_pszText;
|
|
}
|
|
|
|
private:
|
|
TCHAR *m_pszText;
|
|
BOOL m_blnIsKey;
|
|
CCompletionMatch *m_pNext;
|
|
CCompletionMatch *m_pPrev;
|
|
friend class CCompletionList;
|
|
};
|
|
|
|
class CCompletionList
|
|
{
|
|
public:
|
|
CCompletionList();
|
|
~CCompletionList();
|
|
|
|
BOOL IsNewCompletion(const TCHAR *pszContext, const TCHAR *pszBegin, BOOL& rblnNew);
|
|
BOOL Add(const TCHAR *pszText, BOOL blnIsKey);
|
|
const TCHAR *Get(unsigned __int64 nIndex, BOOL& rblnIsKey);
|
|
unsigned __int64 GetCount();
|
|
const TCHAR * GetContext();
|
|
const TCHAR * GetBegin();
|
|
void DeleteList();
|
|
void Invalidate();
|
|
|
|
private:
|
|
CCompletionMatch *m_pHead; // head of completions linked list
|
|
CCompletionMatch *m_pTail; // tail of completions linked list
|
|
CCompletionMatch *m_pLastSearched;
|
|
unsigned int m_nLastSearched;
|
|
TCHAR *m_pszContext;
|
|
TCHAR *m_pszBegin;
|
|
TCHAR *m_pszCurrentKey;
|
|
unsigned __int64 m_nCount;
|
|
} g_Completion;
|
|
|
|
// --- begin of CCompletionList implementation ---
|
|
CCompletionList::CCompletionList()
|
|
{
|
|
m_pHead = NULL;
|
|
m_pTail = NULL;
|
|
m_nCount = 0;
|
|
m_pLastSearched = NULL;
|
|
m_pszContext = NULL;
|
|
m_pszBegin = NULL;
|
|
m_pszCurrentKey = NULL;
|
|
}
|
|
|
|
CCompletionList::~CCompletionList()
|
|
{
|
|
DeleteList();
|
|
|
|
if (m_pszContext)
|
|
delete[] m_pszContext;
|
|
|
|
if (m_pszBegin)
|
|
delete[] m_pszBegin;
|
|
|
|
if (m_pszCurrentKey)
|
|
delete[] m_pszCurrentKey;
|
|
}
|
|
|
|
void CCompletionList::Invalidate()
|
|
{
|
|
if (m_pszCurrentKey)
|
|
{
|
|
delete[] m_pszCurrentKey;
|
|
m_pszCurrentKey = NULL;
|
|
}
|
|
}
|
|
|
|
BOOL CCompletionList::IsNewCompletion(const TCHAR *pszContext, const TCHAR *pszBegin, BOOL& rblnNew)
|
|
{
|
|
const TCHAR *pszCurrentKey = Tree.GetCurrentPath();
|
|
if (!m_pszContext ||
|
|
!m_pszBegin ||
|
|
!m_pszCurrentKey ||
|
|
(_tcscmp(m_pszContext,pszContext) != 0) ||
|
|
(_tcscmp(m_pszBegin,pszBegin) != 0) ||
|
|
(_tcscmp(m_pszCurrentKey,pszCurrentKey)))
|
|
{ // new completion
|
|
DeleteList();
|
|
rblnNew = TRUE;
|
|
if (m_pszContext)
|
|
{
|
|
delete[] m_pszContext;
|
|
m_pszContext = NULL;
|
|
}
|
|
|
|
if (m_pszBegin)
|
|
{
|
|
delete[] m_pszBegin;
|
|
m_pszBegin = NULL;
|
|
}
|
|
|
|
if (m_pszCurrentKey)
|
|
{
|
|
delete[] m_pszCurrentKey;
|
|
m_pszCurrentKey = NULL;
|
|
}
|
|
|
|
size_t s = _tcslen(pszContext);
|
|
m_pszContext = new (std::nothrow) TCHAR[s+1];
|
|
if (!m_pszContext)
|
|
return FALSE;
|
|
_tcscpy(m_pszContext,pszContext);
|
|
|
|
s = _tcslen(pszBegin);
|
|
m_pszBegin = new (std::nothrow) TCHAR[s+1];
|
|
if (!m_pszBegin)
|
|
{
|
|
delete[] m_pszContext;
|
|
m_pszContext = NULL;
|
|
return FALSE;
|
|
}
|
|
_tcscpy(m_pszBegin,pszBegin);
|
|
|
|
s = _tcslen(pszCurrentKey);
|
|
m_pszCurrentKey = new (std::nothrow) TCHAR[s+1];
|
|
if (!m_pszCurrentKey)
|
|
{
|
|
delete[] m_pszContext;
|
|
delete[] m_pszBegin;
|
|
m_pszContext = NULL;
|
|
m_pszBegin = NULL;
|
|
return FALSE;
|
|
}
|
|
_tcscpy(m_pszCurrentKey,pszCurrentKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
rblnNew = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CCompletionList::Add(const TCHAR *pszText, BOOL blnIsKey)
|
|
{
|
|
if (_tcsnicmp(pszText,m_pszBegin,_tcslen(m_pszBegin)) != 0)
|
|
return TRUE;
|
|
CCompletionMatch *pNode = new (std::nothrow) CCompletionMatch;
|
|
if (!pNode)
|
|
return FALSE;
|
|
if (!pNode->Init(pszText))
|
|
return FALSE;
|
|
|
|
ASSERT(pNode->m_pszText);
|
|
ASSERT(pNode->m_pNext == NULL);
|
|
|
|
// add new node to tail
|
|
pNode->m_blnIsKey = blnIsKey;
|
|
if (m_pTail)
|
|
{
|
|
pNode->m_pPrev = m_pTail;
|
|
ASSERT(m_pTail->m_pNext == NULL);
|
|
m_pTail->m_pNext = pNode;
|
|
m_pTail = pNode;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_pHead == NULL);
|
|
m_pHead = m_pTail = pNode;
|
|
}
|
|
|
|
m_nCount++;
|
|
|
|
m_pLastSearched = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
const TCHAR * CCompletionList::Get(unsigned __int64 nIndex, BOOL& rblnIsKey)
|
|
{
|
|
ASSERT(nIndex < m_nCount);
|
|
BOOL blnForward = FALSE;
|
|
CCompletionMatch *pNode = NULL;
|
|
|
|
unsigned __int64 nRelativeIndex = 0;
|
|
|
|
if (m_pLastSearched)
|
|
{
|
|
pNode = m_pLastSearched;
|
|
blnForward = nIndex > m_nLastSearched;
|
|
nRelativeIndex = blnForward?(nIndex-m_nLastSearched):(m_nLastSearched-nIndex);
|
|
if ((nRelativeIndex > nIndex)||(nRelativeIndex > m_nCount-nIndex-1))
|
|
pNode = NULL; // seraching from tail or from head is more effective
|
|
}
|
|
|
|
if (!pNode && (nIndex <= m_nCount/2))
|
|
{ // search from head
|
|
pNode = m_pHead;
|
|
blnForward = TRUE;
|
|
nRelativeIndex = nIndex;
|
|
}
|
|
|
|
if (!pNode)
|
|
{ // search from tail
|
|
pNode = m_pTail;
|
|
blnForward = FALSE;
|
|
nRelativeIndex = m_nCount-nIndex-1;
|
|
}
|
|
|
|
while(pNode)
|
|
{
|
|
if (nRelativeIndex == 0)
|
|
{
|
|
m_nLastSearched = nIndex;
|
|
m_pLastSearched = pNode;
|
|
rblnIsKey = pNode->m_blnIsKey;
|
|
if (!pNode->m_pszText)
|
|
ASSERT(FALSE);
|
|
return pNode->m_pszText;
|
|
}
|
|
|
|
nRelativeIndex--;
|
|
|
|
pNode = blnForward?(pNode->m_pNext):(pNode->m_pPrev);
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
unsigned __int64 CCompletionList::GetCount()
|
|
{
|
|
return m_nCount;
|
|
}
|
|
|
|
const TCHAR * CCompletionList::GetContext()
|
|
{
|
|
return m_pszContext;
|
|
}
|
|
|
|
const TCHAR * CCompletionList::GetBegin()
|
|
{
|
|
return m_pszBegin;
|
|
}
|
|
|
|
void CCompletionList::DeleteList()
|
|
{
|
|
CCompletionMatch *pNode;
|
|
while(m_pHead)
|
|
{
|
|
pNode = m_pHead;
|
|
m_pHead = m_pHead->m_pNext;
|
|
delete pNode;
|
|
}
|
|
m_pTail = NULL;
|
|
ASSERT(m_pHead == NULL);
|
|
m_nCount = 0;
|
|
}
|
|
|
|
// --- end of CCompletionList implementation ---
|
|
|
|
BOOL FillCompletion(const TCHAR *pszKey)
|
|
{
|
|
g_Completion.DeleteList();
|
|
LONG nError;
|
|
CRegistryKey Key;
|
|
TCHAR *pszSubkeyName = NULL;
|
|
DWORD dwMaxSubkeyNameLength;
|
|
TCHAR *pszValueName = NULL;
|
|
DWORD dwMaxValueNameSize;
|
|
|
|
if (!Tree.GetKey(pszKey?pszKey:_T("."),KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,Key))
|
|
return FALSE;
|
|
|
|
BOOL blnCompletionOnKeys = TRUE;
|
|
BOOL blnCompletionOnValues = TRUE;
|
|
|
|
/* if ((_tcsnicmp(pchContext,DIR_CMD,DIR_CMD_LENGTH) == 0)||
|
|
(_tcsnicmp(pchContext,CD_CMD,CD_CMD_LENGTH) == 0)||
|
|
(_tcsnicmp(pchContext,OWNER_CMD,OWNER_CMD_LENGTH) == 0)||
|
|
(_tcsnicmp(pchContext,DACL_CMD,DACL_CMD_LENGTH) == 0)||
|
|
(_tcsnicmp(pchContext,SACL_CMD,SACL_CMD_LENGTH) == 0))
|
|
{
|
|
blnCompletionOnValues = FALSE;
|
|
}*/
|
|
// else if (_tcsnicmp(pchContext,VALUE_CMD,VALUE_CMD_LENGTH) == 0)
|
|
// {
|
|
// blnCompletionOnKeys = FALSE;
|
|
// }
|
|
|
|
size_t nKeyNameSize = 0;
|
|
if (pszKey)
|
|
{
|
|
nKeyNameSize = _tcslen(pszKey);
|
|
if (_tcscmp(pszKey,_T("\\")))
|
|
nKeyNameSize++;
|
|
}
|
|
|
|
if (blnCompletionOnKeys)
|
|
{
|
|
nError = Key.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength);
|
|
if (nError != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
pszSubkeyName = new (std::nothrow) TCHAR[nKeyNameSize+dwMaxSubkeyNameLength+1];
|
|
if (!pszSubkeyName)
|
|
goto Abort;
|
|
|
|
if (pszKey)
|
|
_stprintf(pszSubkeyName,_tcscmp(pszKey,_T("\\"))?_T("%s\\"):_T("%s"),pszKey);
|
|
|
|
Key.InitSubkeyEnumeration(pszSubkeyName+nKeyNameSize,dwMaxSubkeyNameLength);
|
|
while ((nError = Key.GetNextSubkeyName()) == ERROR_SUCCESS)
|
|
if (!g_Completion.Add(pszSubkeyName,TRUE))
|
|
{
|
|
ASSERT(FALSE);
|
|
goto Abort;
|
|
}
|
|
if ((nError != ERROR_SUCCESS)&&(nError != ERROR_NO_MORE_ITEMS))
|
|
{
|
|
ASSERT(FALSE);
|
|
goto Abort;
|
|
}
|
|
}
|
|
|
|
if (blnCompletionOnValues)
|
|
{
|
|
nError = Key.GetMaxValueNameLength(dwMaxValueNameSize);
|
|
if (nError != ERROR_SUCCESS)
|
|
{
|
|
ASSERT(FALSE);
|
|
goto Abort;
|
|
}
|
|
|
|
pszValueName = new (std::nothrow) TCHAR[nKeyNameSize+dwMaxValueNameSize+1];
|
|
if (!pszValueName)
|
|
goto Abort;
|
|
|
|
if (pszKey)
|
|
_stprintf(pszValueName,_tcscmp(pszKey,_T("\\"))?_T("%s\\"):_T("%s"),pszKey);
|
|
|
|
Key.InitValueEnumeration(pszValueName+nKeyNameSize,dwMaxValueNameSize,NULL,0,NULL);
|
|
while((nError = Key.GetNextValue()) == ERROR_SUCCESS)
|
|
if (!g_Completion.Add(pszValueName,FALSE))
|
|
{
|
|
ASSERT(FALSE);
|
|
goto Abort;
|
|
}
|
|
if ((nError != ERROR_SUCCESS)&&(nError != ERROR_NO_MORE_ITEMS))
|
|
{
|
|
ASSERT(FALSE);
|
|
goto Abort;
|
|
}
|
|
}
|
|
|
|
if (pszValueName)
|
|
delete[] pszValueName;
|
|
if (pszSubkeyName)
|
|
delete[] pszSubkeyName;
|
|
return TRUE;
|
|
Abort:
|
|
if (pszValueName)
|
|
delete[] pszValueName;
|
|
if (pszSubkeyName)
|
|
delete[] pszSubkeyName;
|
|
return FALSE;
|
|
}
|
|
|
|
const TCHAR * CompletionCallback(unsigned __int64 & rnIndex,
|
|
const BOOL *pblnForward,
|
|
const TCHAR *pszContext,
|
|
const TCHAR *pszBegin)
|
|
{
|
|
static TCHAR pszBuffer[COMPLETION_BUFFER_SIZE];
|
|
|
|
// Find first non-white space in context
|
|
while(*pszContext && _istspace(*pszContext))
|
|
pszContext++;
|
|
|
|
BOOL blnNewCompletion = TRUE;
|
|
if (!g_Completion.IsNewCompletion(pszContext,pszBegin,blnNewCompletion))
|
|
{
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
if (blnNewCompletion)
|
|
{
|
|
_tcsncpy(pszBuffer,pszBegin,COMPLETION_BUFFER_SIZE-1);
|
|
pszBuffer[COMPLETION_BUFFER_SIZE-1] = 0;
|
|
TCHAR *pszSeparator = pszBuffer; // set it to aby non null value
|
|
if (_tcscmp(pszBuffer,_T("\\")))
|
|
{
|
|
pszSeparator = _tcsrchr(pszBuffer,_T('\\'));
|
|
if (pszSeparator)
|
|
*pszSeparator = 0;
|
|
}
|
|
|
|
if (!FillCompletion(pszSeparator?pszBuffer:NULL))
|
|
return NULL;
|
|
}
|
|
|
|
unsigned __int64 nTotalItems = g_Completion.GetCount();
|
|
if (nTotalItems == 0)
|
|
return NULL;
|
|
|
|
if (rnIndex >= nTotalItems)
|
|
rnIndex = nTotalItems-1;
|
|
|
|
if (pblnForward)
|
|
{
|
|
if (*pblnForward)
|
|
{
|
|
rnIndex++;
|
|
if (rnIndex >= nTotalItems)
|
|
{
|
|
if (g_blnCompletionCycle)
|
|
rnIndex = 0;
|
|
else
|
|
rnIndex--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rnIndex)
|
|
rnIndex--;
|
|
else if (g_blnCompletionCycle)
|
|
rnIndex = nTotalItems-1;
|
|
}
|
|
}
|
|
BOOL blnIsKey = FALSE;
|
|
const TCHAR *pszName = g_Completion.Get(rnIndex,blnIsKey);
|
|
|
|
ASSERT(pszName);
|
|
|
|
_sntprintf(pszBuffer,COMPLETION_BUFFER_SIZE-1,_T("%s%s"),pszName,(blnIsKey?_T("\\"):_T("")));
|
|
pszBuffer[COMPLETION_BUFFER_SIZE-1] = 0;
|
|
return pszBuffer;
|
|
}
|
|
|
|
void InvalidateCompletion()
|
|
{
|
|
g_Completion.Invalidate();
|
|
}
|