1 /* 2 * regexpl - Console Registry Explorer 3 * 4 * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22 // Completion.cpp : Defines the completion related functions. 23 #include "ph.h" 24 #include "RegistryKey.h" 25 #include "RegistryTree.h" 26 27 #define COMPLETION_BUFFER_SIZE 4096 28 29 extern CRegistryTree Tree; 30 31 BOOL g_blnCompletionCycle = TRUE; 32 33 class CCompletionMatch 34 { 35 public: 36 CCompletionMatch() 37 { 38 m_pNext = m_pPrev = NULL; 39 m_pszText = NULL; 40 }; 41 42 BOOL Init(const TCHAR *pszText) 43 { 44 // find the offset to "unique" part 45 const TCHAR *pszUnique = _tcsrchr(pszText,_T('\\')); 46 pszUnique = pszUnique?(pszUnique+1):pszText; 47 BOOL b = _tcschr(pszUnique,_T(' ')) != NULL; // has it spaces in it ??? 48 size_t s = _tcslen(pszText); 49 50 if (m_pszText) 51 delete[] m_pszText; 52 53 m_pszText = new (std::nothrow) TCHAR [s+(b?3:1)]; // if we have spaces in unique part, we need 2 addtional chars for " 54 55 if (!m_pszText) 56 return FALSE; 57 58 ASSERT(pszText <= pszUnique); 59 s = pszUnique - pszText; // calculate offset of the unique part 60 if (s) 61 _tcsncpy(m_pszText,pszText,s); 62 63 if (b) 64 m_pszText[s++] = _T('\"'); 65 66 _tcscpy(m_pszText+s,pszUnique); 67 68 if (b) 69 _tcscat(m_pszText,_T("\"")); 70 71 return TRUE; 72 } 73 74 ~CCompletionMatch() 75 { 76 if (m_pszText) 77 delete[] m_pszText; 78 } 79 80 private: 81 TCHAR *m_pszText; 82 BOOL m_blnIsKey; 83 CCompletionMatch *m_pNext; 84 CCompletionMatch *m_pPrev; 85 friend class CCompletionList; 86 }; 87 88 class CCompletionList 89 { 90 public: 91 CCompletionList(); 92 ~CCompletionList(); 93 94 BOOL IsNewCompletion(const TCHAR *pszContext, const TCHAR *pszBegin, BOOL& rblnNew); 95 BOOL Add(const TCHAR *pszText, BOOL blnIsKey); 96 const TCHAR *Get(unsigned __int64 nIndex, BOOL& rblnIsKey); 97 unsigned __int64 GetCount(); 98 const TCHAR * GetContext(); 99 const TCHAR * GetBegin(); 100 void DeleteList(); 101 void Invalidate(); 102 103 private: 104 CCompletionMatch *m_pHead; // head of completions linked list 105 CCompletionMatch *m_pTail; // tail of completions linked list 106 CCompletionMatch *m_pLastSearched; 107 unsigned int m_nLastSearched; 108 TCHAR *m_pszContext; 109 TCHAR *m_pszBegin; 110 TCHAR *m_pszCurrentKey; 111 unsigned __int64 m_nCount; 112 } g_Completion; 113 114 // --- begin of CCompletionList implementation --- 115 CCompletionList::CCompletionList() 116 { 117 m_pHead = NULL; 118 m_pTail = NULL; 119 m_nCount = 0; 120 m_pLastSearched = NULL; 121 m_pszContext = NULL; 122 m_pszBegin = NULL; 123 m_pszCurrentKey = NULL; 124 } 125 126 CCompletionList::~CCompletionList() 127 { 128 DeleteList(); 129 130 if (m_pszContext) 131 delete[] m_pszContext; 132 133 if (m_pszBegin) 134 delete[] m_pszBegin; 135 136 if (m_pszCurrentKey) 137 delete[] m_pszCurrentKey; 138 } 139 140 void CCompletionList::Invalidate() 141 { 142 if (m_pszCurrentKey) 143 { 144 delete[] m_pszCurrentKey; 145 m_pszCurrentKey = NULL; 146 } 147 } 148 149 BOOL CCompletionList::IsNewCompletion(const TCHAR *pszContext, const TCHAR *pszBegin, BOOL& rblnNew) 150 { 151 const TCHAR *pszCurrentKey = Tree.GetCurrentPath(); 152 if (!m_pszContext || 153 !m_pszBegin || 154 !m_pszCurrentKey || 155 (_tcscmp(m_pszContext,pszContext) != 0) || 156 (_tcscmp(m_pszBegin,pszBegin) != 0) || 157 (_tcscmp(m_pszCurrentKey,pszCurrentKey))) 158 { // new completion 159 DeleteList(); 160 rblnNew = TRUE; 161 if (m_pszContext) 162 { 163 delete[] m_pszContext; 164 m_pszContext = NULL; 165 } 166 167 if (m_pszBegin) 168 { 169 delete[] m_pszBegin; 170 m_pszBegin = NULL; 171 } 172 173 if (m_pszCurrentKey) 174 { 175 delete[] m_pszCurrentKey; 176 m_pszCurrentKey = NULL; 177 } 178 179 size_t s = _tcslen(pszContext); 180 m_pszContext = new (std::nothrow) TCHAR[s+1]; 181 if (!m_pszContext) 182 return FALSE; 183 _tcscpy(m_pszContext,pszContext); 184 185 s = _tcslen(pszBegin); 186 m_pszBegin = new (std::nothrow) TCHAR[s+1]; 187 if (!m_pszBegin) 188 { 189 delete[] m_pszContext; 190 m_pszContext = NULL; 191 return FALSE; 192 } 193 _tcscpy(m_pszBegin,pszBegin); 194 195 s = _tcslen(pszCurrentKey); 196 m_pszCurrentKey = new (std::nothrow) TCHAR[s+1]; 197 if (!m_pszCurrentKey) 198 { 199 delete[] m_pszContext; 200 delete[] m_pszBegin; 201 m_pszContext = NULL; 202 m_pszBegin = NULL; 203 return FALSE; 204 } 205 _tcscpy(m_pszCurrentKey,pszCurrentKey); 206 207 return TRUE; 208 } 209 210 rblnNew = FALSE; 211 return TRUE; 212 } 213 214 BOOL CCompletionList::Add(const TCHAR *pszText, BOOL blnIsKey) 215 { 216 if (_tcsnicmp(pszText,m_pszBegin,_tcslen(m_pszBegin)) != 0) 217 return TRUE; 218 CCompletionMatch *pNode = new (std::nothrow) CCompletionMatch; 219 if (!pNode) 220 return FALSE; 221 if (!pNode->Init(pszText)) 222 return FALSE; 223 224 ASSERT(pNode->m_pszText); 225 ASSERT(pNode->m_pNext == NULL); 226 227 // add new node to tail 228 pNode->m_blnIsKey = blnIsKey; 229 if (m_pTail) 230 { 231 pNode->m_pPrev = m_pTail; 232 ASSERT(m_pTail->m_pNext == NULL); 233 m_pTail->m_pNext = pNode; 234 m_pTail = pNode; 235 } 236 else 237 { 238 ASSERT(m_pHead == NULL); 239 m_pHead = m_pTail = pNode; 240 } 241 242 m_nCount++; 243 244 m_pLastSearched = NULL; 245 246 return TRUE; 247 } 248 249 const TCHAR * CCompletionList::Get(unsigned __int64 nIndex, BOOL& rblnIsKey) 250 { 251 ASSERT(nIndex < m_nCount); 252 BOOL blnForward = FALSE; 253 CCompletionMatch *pNode = NULL; 254 255 unsigned __int64 nRelativeIndex = 0; 256 257 if (m_pLastSearched) 258 { 259 pNode = m_pLastSearched; 260 blnForward = nIndex > m_nLastSearched; 261 nRelativeIndex = blnForward?(nIndex-m_nLastSearched):(m_nLastSearched-nIndex); 262 if ((nRelativeIndex > nIndex)||(nRelativeIndex > m_nCount-nIndex-1)) 263 pNode = NULL; // seraching from tail or from head is more effective 264 } 265 266 if (!pNode && (nIndex <= m_nCount/2)) 267 { // search from head 268 pNode = m_pHead; 269 blnForward = TRUE; 270 nRelativeIndex = nIndex; 271 } 272 273 if (!pNode) 274 { // search from tail 275 pNode = m_pTail; 276 blnForward = FALSE; 277 nRelativeIndex = m_nCount-nIndex-1; 278 } 279 280 while(pNode) 281 { 282 if (nRelativeIndex == 0) 283 { 284 m_nLastSearched = nIndex; 285 m_pLastSearched = pNode; 286 rblnIsKey = pNode->m_blnIsKey; 287 if (!pNode->m_pszText) 288 ASSERT(FALSE); 289 return pNode->m_pszText; 290 } 291 292 nRelativeIndex--; 293 294 pNode = blnForward?(pNode->m_pNext):(pNode->m_pPrev); 295 } 296 297 ASSERT(FALSE); 298 return NULL; 299 } 300 301 unsigned __int64 CCompletionList::GetCount() 302 { 303 return m_nCount; 304 } 305 306 const TCHAR * CCompletionList::GetContext() 307 { 308 return m_pszContext; 309 } 310 311 const TCHAR * CCompletionList::GetBegin() 312 { 313 return m_pszBegin; 314 } 315 316 void CCompletionList::DeleteList() 317 { 318 CCompletionMatch *pNode; 319 while(m_pHead) 320 { 321 pNode = m_pHead; 322 m_pHead = m_pHead->m_pNext; 323 delete pNode; 324 } 325 m_pTail = NULL; 326 ASSERT(m_pHead == NULL); 327 m_nCount = 0; 328 } 329 330 // --- end of CCompletionList implementation --- 331 332 BOOL FillCompletion(const TCHAR *pszKey) 333 { 334 g_Completion.DeleteList(); 335 LONG nError; 336 CRegistryKey Key; 337 TCHAR *pszSubkeyName = NULL; 338 DWORD dwMaxSubkeyNameLength; 339 TCHAR *pszValueName = NULL; 340 DWORD dwMaxValueNameSize; 341 342 if (!Tree.GetKey(pszKey?pszKey:_T("."),KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,Key)) 343 return FALSE; 344 345 BOOL blnCompletionOnKeys = TRUE; 346 BOOL blnCompletionOnValues = TRUE; 347 348 /* if ((_tcsnicmp(pchContext,DIR_CMD,DIR_CMD_LENGTH) == 0)|| 349 (_tcsnicmp(pchContext,CD_CMD,CD_CMD_LENGTH) == 0)|| 350 (_tcsnicmp(pchContext,OWNER_CMD,OWNER_CMD_LENGTH) == 0)|| 351 (_tcsnicmp(pchContext,DACL_CMD,DACL_CMD_LENGTH) == 0)|| 352 (_tcsnicmp(pchContext,SACL_CMD,SACL_CMD_LENGTH) == 0)) 353 { 354 blnCompletionOnValues = FALSE; 355 }*/ 356 // else if (_tcsnicmp(pchContext,VALUE_CMD,VALUE_CMD_LENGTH) == 0) 357 // { 358 // blnCompletionOnKeys = FALSE; 359 // } 360 361 size_t nKeyNameSize = 0; 362 if (pszKey) 363 { 364 nKeyNameSize = _tcslen(pszKey); 365 if (_tcscmp(pszKey,_T("\\"))) 366 nKeyNameSize++; 367 } 368 369 if (blnCompletionOnKeys) 370 { 371 nError = Key.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength); 372 if (nError != ERROR_SUCCESS) 373 return FALSE; 374 375 pszSubkeyName = new (std::nothrow) TCHAR[nKeyNameSize+dwMaxSubkeyNameLength+1]; 376 if (!pszSubkeyName) 377 goto Abort; 378 379 if (pszKey) 380 _stprintf(pszSubkeyName,_tcscmp(pszKey,_T("\\"))?_T("%s\\"):_T("%s"),pszKey); 381 382 Key.InitSubkeyEnumeration(pszSubkeyName+nKeyNameSize,dwMaxSubkeyNameLength); 383 while ((nError = Key.GetNextSubkeyName()) == ERROR_SUCCESS) 384 if (!g_Completion.Add(pszSubkeyName,TRUE)) 385 { 386 ASSERT(FALSE); 387 goto Abort; 388 } 389 if ((nError != ERROR_SUCCESS)&&(nError != ERROR_NO_MORE_ITEMS)) 390 { 391 ASSERT(FALSE); 392 goto Abort; 393 } 394 } 395 396 if (blnCompletionOnValues) 397 { 398 nError = Key.GetMaxValueNameLength(dwMaxValueNameSize); 399 if (nError != ERROR_SUCCESS) 400 { 401 ASSERT(FALSE); 402 goto Abort; 403 } 404 405 pszValueName = new (std::nothrow) TCHAR[nKeyNameSize+dwMaxValueNameSize+1]; 406 if (!pszValueName) 407 goto Abort; 408 409 if (pszKey) 410 _stprintf(pszValueName,_tcscmp(pszKey,_T("\\"))?_T("%s\\"):_T("%s"),pszKey); 411 412 Key.InitValueEnumeration(pszValueName+nKeyNameSize,dwMaxValueNameSize,NULL,0,NULL); 413 while((nError = Key.GetNextValue()) == ERROR_SUCCESS) 414 if (!g_Completion.Add(pszValueName,FALSE)) 415 { 416 ASSERT(FALSE); 417 goto Abort; 418 } 419 if ((nError != ERROR_SUCCESS)&&(nError != ERROR_NO_MORE_ITEMS)) 420 { 421 ASSERT(FALSE); 422 goto Abort; 423 } 424 } 425 426 if (pszValueName) 427 delete[] pszValueName; 428 if (pszSubkeyName) 429 delete[] pszSubkeyName; 430 return TRUE; 431 Abort: 432 if (pszValueName) 433 delete[] pszValueName; 434 if (pszSubkeyName) 435 delete[] pszSubkeyName; 436 return FALSE; 437 } 438 439 const TCHAR * CompletionCallback(unsigned __int64 & rnIndex, 440 const BOOL *pblnForward, 441 const TCHAR *pszContext, 442 const TCHAR *pszBegin) 443 { 444 static TCHAR pszBuffer[COMPLETION_BUFFER_SIZE]; 445 446 // Find first non-white space in context 447 while(*pszContext && _istspace(*pszContext)) 448 pszContext++; 449 450 BOOL blnNewCompletion = TRUE; 451 if (!g_Completion.IsNewCompletion(pszContext,pszBegin,blnNewCompletion)) 452 { 453 ASSERT(FALSE); 454 return NULL; 455 } 456 457 if (blnNewCompletion) 458 { 459 _tcsncpy(pszBuffer,pszBegin,COMPLETION_BUFFER_SIZE-1); 460 pszBuffer[COMPLETION_BUFFER_SIZE-1] = 0; 461 TCHAR *pszSeparator = pszBuffer; // set it to aby non null value 462 if (_tcscmp(pszBuffer,_T("\\"))) 463 { 464 pszSeparator = _tcsrchr(pszBuffer,_T('\\')); 465 if (pszSeparator) 466 *pszSeparator = 0; 467 } 468 469 if (!FillCompletion(pszSeparator?pszBuffer:NULL)) 470 return NULL; 471 } 472 473 unsigned __int64 nTotalItems = g_Completion.GetCount(); 474 if (nTotalItems == 0) 475 return NULL; 476 477 if (rnIndex >= nTotalItems) 478 rnIndex = nTotalItems-1; 479 480 if (pblnForward) 481 { 482 if (*pblnForward) 483 { 484 rnIndex++; 485 if (rnIndex >= nTotalItems) 486 { 487 if (g_blnCompletionCycle) 488 rnIndex = 0; 489 else 490 rnIndex--; 491 } 492 } 493 else 494 { 495 if (rnIndex) 496 rnIndex--; 497 else if (g_blnCompletionCycle) 498 rnIndex = nTotalItems-1; 499 } 500 } 501 BOOL blnIsKey = FALSE; 502 const TCHAR *pszName = g_Completion.Get(rnIndex,blnIsKey); 503 504 ASSERT(pszName); 505 506 _sntprintf(pszBuffer,COMPLETION_BUFFER_SIZE-1,_T("%s%s"),pszName,(blnIsKey?_T("\\"):_T(""))); 507 pszBuffer[COMPLETION_BUFFER_SIZE-1] = 0; 508 return pszBuffer; 509 } 510 511 void InvalidateCompletion() 512 { 513 g_Completion.Invalidate(); 514 } 515