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