Fix personal info.
[reactos.git] / rosapps / 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 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 TCHAR[s+1];
182 if (!m_pszContext)
183 return FALSE;
184 _tcscpy(m_pszContext,pszContext);
185
186 s = _tcslen(pszBegin);
187 m_pszBegin = new TCHAR[s+1];
188 if (!m_pszBegin)
189 return FALSE;
190 _tcscpy(m_pszBegin,pszBegin);
191
192 s = _tcslen(pszCurrentKey);
193 m_pszCurrentKey = new TCHAR[s+1];
194 if (!m_pszCurrentKey)
195 return FALSE;
196 _tcscpy(m_pszCurrentKey,pszCurrentKey);
197
198 return TRUE;
199 }
200
201 rblnNew = FALSE;
202 return TRUE;
203 }
204
205 BOOL CCompletionList::Add(const TCHAR *pszText, BOOL blnIsKey)
206 {
207 if (_tcsnicmp(pszText,m_pszBegin,_tcslen(m_pszBegin)) != 0)
208 return TRUE;
209 CCompletionMatch *pNode = new CCompletionMatch;
210 if (!pNode)
211 return FALSE;
212 if (!pNode->Init(pszText))
213 return FALSE;
214
215 ASSERT(pNode->m_pszText);
216 ASSERT(pNode->m_pNext == NULL);
217
218 // add new node to tail
219 pNode->m_blnIsKey = blnIsKey;
220 if (m_pTail)
221 {
222 pNode->m_pPrev = m_pTail;
223 ASSERT(m_pTail->m_pNext == NULL);
224 m_pTail->m_pNext = pNode;
225 m_pTail = pNode;
226 }
227 else
228 {
229 ASSERT(m_pHead == NULL);
230 m_pHead = m_pTail = pNode;
231 }
232
233 m_nCount++;
234
235 m_pLastSearched = NULL;
236
237 return TRUE;
238 }
239
240 const TCHAR * CCompletionList::Get(unsigned __int64 nIndex, BOOL& rblnIsKey)
241 {
242 ASSERT(nIndex < m_nCount);
243 BOOL blnForward = FALSE;
244 CCompletionMatch *pNode = NULL;
245
246 unsigned __int64 nRelativeIndex = 0;
247
248 if (m_pLastSearched)
249 {
250 pNode = m_pLastSearched;
251 blnForward = nIndex > m_nLastSearched;
252 nRelativeIndex = blnForward?(nIndex-m_nLastSearched):(m_nLastSearched-nIndex);
253 if ((nRelativeIndex > nIndex)||(nRelativeIndex > m_nCount-nIndex-1))
254 pNode = NULL; // seraching from tail or from head is more effective
255 }
256
257 if (!pNode && (nIndex <= m_nCount/2))
258 { // search from head
259 pNode = m_pHead;
260 blnForward = TRUE;
261 nRelativeIndex = nIndex;
262 }
263
264 if (!pNode)
265 { // search from tail
266 pNode = m_pTail;
267 blnForward = FALSE;
268 nRelativeIndex = m_nCount-nIndex-1;
269 }
270
271 while(pNode)
272 {
273 if (nRelativeIndex == 0)
274 {
275 m_nLastSearched = nIndex;
276 m_pLastSearched = pNode;
277 rblnIsKey = pNode->m_blnIsKey;
278 if (!pNode->m_pszText)
279 ASSERT(FALSE);
280 return pNode->m_pszText;
281 }
282
283 nRelativeIndex--;
284
285 pNode = blnForward?(pNode->m_pNext):(pNode->m_pPrev);
286 }
287
288 ASSERT(FALSE);
289 return NULL;
290 }
291
292 unsigned __int64 CCompletionList::GetCount()
293 {
294 return m_nCount;
295 }
296
297 const TCHAR * CCompletionList::GetContext()
298 {
299 return m_pszContext;
300 }
301
302 const TCHAR * CCompletionList::GetBegin()
303 {
304 return m_pszBegin;
305 }
306
307 void CCompletionList::DeleteList()
308 {
309 CCompletionMatch *pNode;
310 while(m_pHead)
311 {
312 pNode = m_pHead;
313 m_pHead = m_pHead->m_pNext;
314 delete pNode;
315 }
316 m_pTail = NULL;
317 ASSERT(m_pHead == NULL);
318 m_nCount = 0;
319 }
320
321 // --- end of CCompletionList implementation ---
322
323 BOOL FillCompletion(const TCHAR *pszKey)
324 {
325 g_Completion.DeleteList();
326 LONG nError;
327 CRegistryKey Key;
328 TCHAR *pszSubkeyName = NULL;
329 DWORD dwMaxSubkeyNameLength;
330 TCHAR *pszValueName = NULL;
331 DWORD dwMaxValueNameSize;
332
333 if (!Tree.GetKey(pszKey?pszKey:_T("."),KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,Key))
334 return FALSE;
335
336 BOOL blnCompletionOnKeys = TRUE;
337 BOOL blnCompletionOnValues = TRUE;
338
339 /* if ((_tcsnicmp(pchContext,DIR_CMD,DIR_CMD_LENGTH) == 0)||
340 (_tcsnicmp(pchContext,CD_CMD,CD_CMD_LENGTH) == 0)||
341 (_tcsnicmp(pchContext,OWNER_CMD,OWNER_CMD_LENGTH) == 0)||
342 (_tcsnicmp(pchContext,DACL_CMD,DACL_CMD_LENGTH) == 0)||
343 (_tcsnicmp(pchContext,SACL_CMD,SACL_CMD_LENGTH) == 0))
344 {
345 blnCompletionOnValues = FALSE;
346 }*/
347 // else if (_tcsnicmp(pchContext,VALUE_CMD,VALUE_CMD_LENGTH) == 0)
348 // {
349 // blnCompletionOnKeys = FALSE;
350 // }
351
352 size_t nKeyNameSize = 0;
353 if (pszKey)
354 {
355 nKeyNameSize = _tcslen(pszKey);
356 if (_tcscmp(pszKey,_T("\\")))
357 nKeyNameSize++;
358 }
359
360 if (blnCompletionOnKeys)
361 {
362 nError = Key.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength);
363 if (nError != ERROR_SUCCESS)
364 return FALSE;
365
366 pszSubkeyName = new TCHAR[nKeyNameSize+dwMaxSubkeyNameLength+1];
367 if (!pszSubkeyName)
368 goto Abort;
369
370 if (pszKey)
371 _stprintf(pszSubkeyName,_tcscmp(pszKey,_T("\\"))?_T("%s\\"):_T("%s"),pszKey);
372
373 Key.InitSubkeyEnumeration(pszSubkeyName+nKeyNameSize,dwMaxSubkeyNameLength);
374 while ((nError = Key.GetNextSubkeyName()) == ERROR_SUCCESS)
375 if (!g_Completion.Add(pszSubkeyName,TRUE))
376 {
377 ASSERT(FALSE);
378 goto Abort;
379 }
380 if ((nError != ERROR_SUCCESS)&&(nError != ERROR_NO_MORE_ITEMS))
381 {
382 ASSERT(FALSE);
383 goto Abort;
384 }
385 }
386
387 if (blnCompletionOnValues)
388 {
389 nError = Key.GetMaxValueNameLength(dwMaxValueNameSize);
390 if (nError != ERROR_SUCCESS)
391 {
392 ASSERT(FALSE);
393 goto Abort;
394 }
395
396 pszValueName = new TCHAR[nKeyNameSize+dwMaxValueNameSize+1];
397 if (!pszValueName)
398 goto Abort;
399
400 if (pszKey)
401 _stprintf(pszValueName,_tcscmp(pszKey,_T("\\"))?_T("%s\\"):_T("%s"),pszKey);
402
403 Key.InitValueEnumeration(pszValueName+nKeyNameSize,dwMaxValueNameSize,NULL,0,NULL);
404 while((nError = Key.GetNextValue()) == ERROR_SUCCESS)
405 if (!g_Completion.Add(pszValueName,FALSE))
406 {
407 ASSERT(FALSE);
408 goto Abort;
409 }
410 if ((nError != ERROR_SUCCESS)&&(nError != ERROR_NO_MORE_ITEMS))
411 {
412 ASSERT(FALSE);
413 goto Abort;
414 }
415 }
416
417 if (pszValueName)
418 delete pszValueName;
419 if (pszSubkeyName)
420 delete pszSubkeyName;
421 return TRUE;
422 Abort:
423 if (pszValueName)
424 delete pszValueName;
425 if (pszSubkeyName)
426 delete pszSubkeyName;
427 return FALSE;
428 }
429
430 const TCHAR * CompletionCallback(unsigned __int64 & rnIndex,
431 const BOOL *pblnForward,
432 const TCHAR *pszContext,
433 const TCHAR *pszBegin)
434 {
435 static TCHAR pszBuffer[COMPLETION_BUFFER_SIZE];
436
437 // Find first non-white space in context
438 while(*pszContext && _istspace(*pszContext))
439 pszContext++;
440
441 BOOL blnNewCompletion = TRUE;
442 if (!g_Completion.IsNewCompletion(pszContext,pszBegin,blnNewCompletion))
443 {
444 ASSERT(FALSE);
445 return NULL;
446 }
447
448 if (blnNewCompletion)
449 {
450 _tcsncpy(pszBuffer,pszBegin,COMPLETION_BUFFER_SIZE-1);
451 pszBuffer[COMPLETION_BUFFER_SIZE-1] = 0;
452 TCHAR *pszSeparator = pszBuffer; // set it to aby non null value
453 if (_tcscmp(pszBuffer,_T("\\")))
454 {
455 pszSeparator = _tcsrchr(pszBuffer,_T('\\'));
456 if (pszSeparator)
457 *pszSeparator = 0;
458 }
459
460 if (!FillCompletion(pszSeparator?pszBuffer:NULL))
461 return NULL;
462 }
463
464 unsigned __int64 nTotalItems = g_Completion.GetCount();
465 if (nTotalItems == 0)
466 return NULL;
467
468 if (rnIndex >= nTotalItems)
469 rnIndex = nTotalItems-1;
470
471 if (pblnForward)
472 {
473 if (*pblnForward)
474 {
475 rnIndex++;
476 if (rnIndex >= nTotalItems)
477 {
478 if (g_blnCompletionCycle)
479 rnIndex = 0;
480 else
481 rnIndex--;
482 }
483 }
484 else
485 {
486 if (rnIndex)
487 rnIndex--;
488 else if (g_blnCompletionCycle)
489 rnIndex = nTotalItems-1;
490 }
491 }
492 BOOL blnIsKey = FALSE;
493 const TCHAR *pszName = g_Completion.Get(rnIndex,blnIsKey);
494
495 ASSERT(pszName);
496
497 _sntprintf(pszBuffer,COMPLETION_BUFFER_SIZE-1,_T("%s%s"),pszName,(blnIsKey?_T("\\"):_T("")));
498 pszBuffer[COMPLETION_BUFFER_SIZE-1] = 0;
499 return pszBuffer;
500 }
501
502 void InvalidateCompletion()
503 {
504 g_Completion.Invalidate();
505 }